From fc02a678de3d47f0288e29408fdc01c489f952f8 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 13 Nov 2017 22:52:53 +0900 Subject: [PATCH] Openflow parser --- .gitignore | 20 + README.md | 21 + config/config.exs | 16 + lib/openflow.ex | 50 + lib/openflow/.#nx_packet_in2.ex | 1 + lib/openflow/action.ex | 29 + lib/openflow/actions/copy_ttl_in.ex | 19 + lib/openflow/actions/copy_ttl_out.ex | 19 + lib/openflow/actions/dec_mpls_ttl.ex | 19 + lib/openflow/actions/dec_nw_ttl.ex | 19 + lib/openflow/actions/experimenter.ex | 32 + lib/openflow/actions/group.ex | 19 + lib/openflow/actions/nx_bundle.ex | 80 ++ lib/openflow/actions/nx_bundle_load.ex | 102 ++ lib/openflow/actions/nx_clone.ex | 28 + lib/openflow/actions/nx_conjunction.ex | 28 + lib/openflow/actions/nx_conntrack.ex | 88 ++ lib/openflow/actions/nx_controller.ex | 32 + lib/openflow/actions/nx_controller2.ex | 119 ++ lib/openflow/actions/nx_ct_clear.ex | 21 + lib/openflow/actions/nx_dec_mpls_ttl.ex | 21 + lib/openflow/actions/nx_dec_ttl.ex | 21 + lib/openflow/actions/nx_dec_ttl_cnt_ids.ex | 31 + lib/openflow/actions/nx_exit.ex | 21 + lib/openflow/actions/nx_fin_timeout.ex | 26 + lib/openflow/actions/nx_flow_spec.ex | 29 + lib/openflow/actions/nx_flow_spec_load.ex | 83 ++ lib/openflow/actions/nx_flow_spec_match.ex | 83 ++ lib/openflow/actions/nx_flow_spec_output.ex | 38 + lib/openflow/actions/nx_learn.ex | 78 ++ lib/openflow/actions/nx_learn2.ex | 106 ++ lib/openflow/actions/nx_multipath.ex | 78 ++ lib/openflow/actions/nx_nat.ex | 121 ++ lib/openflow/actions/nx_note.ex | 26 + lib/openflow/actions/nx_output_reg.ex | 52 + lib/openflow/actions/nx_output_reg2.ex | 51 + lib/openflow/actions/nx_output_trunc.ex | 31 + lib/openflow/actions/nx_pop_mpls.ex | 24 + lib/openflow/actions/nx_pop_queue.ex | 24 + lib/openflow/actions/nx_push_mpls.ex | 24 + lib/openflow/actions/nx_reg_load.ex | 54 + lib/openflow/actions/nx_reg_load2.ex | 40 + lib/openflow/actions/nx_reg_move.ex | 56 + lib/openflow/actions/nx_resubmit.ex | 26 + lib/openflow/actions/nx_resubmit_table.ex | 33 + lib/openflow/actions/nx_resubmit_table_ct.ex | 30 + lib/openflow/actions/nx_sample.ex | 44 + lib/openflow/actions/nx_sample2.ex | 49 + lib/openflow/actions/nx_sample3.ex | 56 + lib/openflow/actions/nx_set_mpls_label.ex | 24 + lib/openflow/actions/nx_set_mpls_tc.ex | 24 + lib/openflow/actions/nx_set_mpls_ttl.ex | 24 + lib/openflow/actions/nx_set_queue.ex | 24 + lib/openflow/actions/nx_set_tunnel.ex | 24 + lib/openflow/actions/nx_set_tunnel64.ex | 24 + lib/openflow/actions/nx_stack_pop.ex | 36 + lib/openflow/actions/nx_stack_push.ex | 36 + lib/openflow/actions/nx_write_metadata.ex | 32 + lib/openflow/actions/output.ex | 31 + lib/openflow/actions/pop_mpls.ex | 19 + lib/openflow/actions/pop_pbb.ex | 19 + lib/openflow/actions/pop_vlan.ex | 19 + lib/openflow/actions/push_mpls.ex | 19 + lib/openflow/actions/push_pbb.ex | 19 + lib/openflow/actions/push_vlan.ex | 19 + lib/openflow/actions/set_field.ex | 34 + lib/openflow/actions/set_mpls_ttl.ex | 19 + lib/openflow/actions/set_nw_ttl.ex | 19 + lib/openflow/actions/set_queue.ex | 19 + lib/openflow/barrier/reply.ex | 24 + lib/openflow/barrier/request.ex | 24 + lib/openflow/buckets.ex | 54 + lib/openflow/echo.ex | 2 + lib/openflow/echo/reply.ex | 25 + lib/openflow/echo/request.ex | 26 + lib/openflow/enums.ex | 1081 +++++++++++++++++ lib/openflow/error_msg.ex | 53 + lib/openflow/experimenter.ex | 39 + lib/openflow/features.ex | 2 + lib/openflow/features/reply.ex | 39 + lib/openflow/features/request.ex | 24 + lib/openflow/flow_mod.ex | 109 ++ lib/openflow/flow_removed.ex | 42 + lib/openflow/get_async/reply.ex | 40 + lib/openflow/get_async/request.ex | 20 + lib/openflow/get_config.ex | 2 + lib/openflow/get_config/reply.ex | 26 + lib/openflow/get_config/request.ex | 24 + lib/openflow/group_mod.ex | 49 + lib/openflow/hello.ex | 103 ++ lib/openflow/instruction.ex | 28 + lib/openflow/instructions/apply_actions.ex | 20 + lib/openflow/instructions/clear_actions.ex | 19 + lib/openflow/instructions/experimenter.ex | 18 + lib/openflow/instructions/goto_table.ex | 19 + lib/openflow/instructions/meter.ex | 19 + lib/openflow/instructions/write_actions.ex | 20 + lib/openflow/instructions/write_metadata.ex | 19 + lib/openflow/match.ex | 154 +++ lib/openflow/match/field.ex | 636 ++++++++++ lib/openflow/meter_band.ex | 28 + lib/openflow/meter_band/drop.ex | 22 + lib/openflow/meter_band/experimenter.ex | 24 + lib/openflow/meter_band/remark.ex | 24 + lib/openflow/meter_mod.ex | 32 + lib/openflow/multipart/aggregate/reply.ex | 19 + lib/openflow/multipart/aggregate/request.ex | 67 + lib/openflow/multipart/desc/reply.ex | 30 + lib/openflow/multipart/desc/request.ex | 24 + lib/openflow/multipart/flow/reply.ex | 75 ++ lib/openflow/multipart/flow/request.ex | 67 + lib/openflow/multipart/group/ *Minibuf-1* | 1 + lib/openflow/multipart/group/.# *Minibuf-1* | 1 + lib/openflow/multipart/group/reply.ex | 69 ++ lib/openflow/multipart/group/request.ex | 29 + lib/openflow/multipart/group_desc/reply.ex | 54 + lib/openflow/multipart/group_desc/request.ex | 24 + .../multipart/group_features/reply.ex | 44 + .../multipart/group_features/request.ex | 24 + lib/openflow/multipart/meter/reply.ex | 63 + lib/openflow/multipart/meter/request.ex | 29 + .../multipart/meter_config/request.ex | 29 + lib/openflow/multipart/port_stats/reply.ex | 78 ++ lib/openflow/multipart/port_stats/request.ex | 29 + lib/openflow/multipart/queue/reply.ex | 59 + lib/openflow/multipart/queue/request.ex | 34 + lib/openflow/multipart/reply.ex | 27 + lib/openflow/multipart/request.ex | 27 + lib/openflow/multipart/table/reply.ex | 45 + lib/openflow/multipart/table/request.ex | 24 + lib/openflow/nx_packet_in2.ex | 34 + lib/openflow/nx_packet_in_format.ex | 30 + lib/openflow/nx_set_controller_id.ex | 26 + lib/openflow/packet_in.ex | 60 + lib/openflow/packet_out.ex | 51 + lib/openflow/port.ex | 47 + lib/openflow/port_mod.ex | 48 + lib/openflow/port_status.ex | 20 + lib/openflow/role/reply.ex | 30 + lib/openflow/role/request.ex | 30 + lib/openflow/set_async.ex | 40 + lib/openflow/set_config.ex | 32 + lib/openflow/table_mod.ex | 28 + lib/openflow/utils.ex | 73 ++ lib/tres.ex | 2 + lib/tres/application.ex | 17 + lib/tres/message_handler.ex | 52 + lib/tres/message_handler_sup.ex | 16 + lib/tres/secure_channel.ex | 385 ++++++ lib/tres/secure_channel_state.ex | 51 + lib/tres/switch_registry.ex | 32 + lib/tres/utils.ex | 26 + mix.exs | 24 + mix.lock | 6 + test/ofp_action_test.exs | 566 +++++++++ test/ofp_echo_test.exs | 52 + test/ofp_error_test.exs | 56 + test/ofp_features_test.exs | 58 + test/ofp_flow_mod_test.exs | 280 +++++ test/ofp_flow_removed_test.exs | 57 + test/ofp_get_config_test.exs | 52 + test/ofp_group_mod_test.exs | 26 + test/ofp_hello_test.exs | 26 + test/ofp_packet_in_test.exs | 87 ++ test/ofp_packet_out_test.exs | 49 + test/ofp_port_mod_test.exs | 34 + test/ofp_port_status_test.exs | 48 + test/ofp_set_config_test.exs | 32 + test/ofp_table_mod_test.exs | 28 + ...t,exec(load:0xf009->NXM_NX_CT_MARK[])).raw | 1 + test/packet_data/4-0-ofp_desc_reply.packet | Bin 0 -> 1072 bytes test/packet_data/4-1-ofp_packet_out.packet | Bin 0 -> 138 bytes test/packet_data/4-10-ofp_hello.packet | Bin 0 -> 16 bytes .../4-11-ofp_flow_stats_request.packet | Bin 0 -> 56 bytes .../4-12-ofp_flow_stats_reply.packet | Bin 0 -> 552 bytes test/packet_data/4-13-ofp_echo_request.packet | Bin 0 -> 12 bytes test/packet_data/4-14-ofp_echo_reply.packet | Bin 0 -> 12 bytes test/packet_data/4-15-ofp_error_msg.packet | Bin 0 -> 20 bytes test/packet_data/4-16-ofp_experimenter.packet | Bin 0 -> 20 bytes .../4-17-ofp_barrier_request.packet | Bin 0 -> 8 bytes .../packet_data/4-18-ofp_barrier_reply.packet | Bin 0 -> 8 bytes test/packet_data/4-19-ofp_role_request.packet | Bin 0 -> 24 bytes test/packet_data/4-2-ofp_flow_mod.packet | Bin 0 -> 280 bytes test/packet_data/4-20-ofp_role_reply.packet | Bin 0 -> 24 bytes test/packet_data/4-21-ofp_group_mod.packet | Bin 0 -> 48 bytes test/packet_data/4-22-ofp_port_mod.packet | Bin 0 -> 40 bytes test/packet_data/4-23-ofp_table_mod.packet | Bin 0 -> 16 bytes test/packet_data/4-24-ofp_desc_request.packet | Bin 0 -> 16 bytes .../4-25-ofp_aggregate_stats_request.packet | Bin 0 -> 56 bytes .../4-26-ofp_aggregate_stats_reply.packet | Bin 0 -> 40 bytes .../4-27-ofp_table_stats_request.packet | Bin 0 -> 16 bytes .../4-28-ofp_table_stats_reply.packet | Bin 0 -> 64 bytes .../4-29-ofp_port_stats_request.packet | Bin 0 -> 24 bytes test/packet_data/4-3-ofp_flow_mod.packet | Bin 0 -> 80 bytes .../4-30-ofp_port_stats_reply.packet | Bin 0 -> 240 bytes .../4-31-ofp_group_features_request.packet | Bin 0 -> 16 bytes .../4-32-ofp_group_features_reply.packet | Bin 0 -> 56 bytes .../4-33-ofp_group_desc_request.packet | Bin 0 -> 16 bytes .../4-34-ofp_group_desc_reply.packet | Bin 0 -> 56 bytes .../4-35-ofp_queue_get_config_request.packet | Bin 0 -> 16 bytes .../4-36-ofp_queue_get_config_reply.packet | Bin 0 -> 211 bytes .../4-37-ofp_queue_stats_request.packet | Bin 0 -> 24 bytes .../4-38-ofp_queue_stats_reply.packet | Bin 0 -> 136 bytes test/packet_data/4-39-ofp_port_status.packet | Bin 0 -> 80 bytes test/packet_data/4-4-ofp_packet_in.packet | Bin 0 -> 148 bytes test/packet_data/4-40-ofp_flow_removed.packet | Bin 0 -> 64 bytes .../4-41-ofp_error_msg_experimenter.packet | Bin 0 -> 27 bytes .../4-42-ofp_get_async_request.packet | Bin 0 -> 8 bytes .../4-43-ofp_get_async_reply.packet | Bin 0 -> 32 bytes test/packet_data/4-44-ofp_set_async.packet | Bin 0 -> 32 bytes test/packet_data/4-45-ofp_meter_mod.packet | Bin 0 -> 64 bytes test/packet_data/4-46-ofp_flow_mod.packet | Bin 0 -> 96 bytes .../4-47-ofp_meter_config_request.packet | Bin 0 -> 24 bytes .../4-48-ofp_meter_config_reply.packet | Bin 0 -> 40 bytes .../4-49-ofp_meter_stats_request.packet | Bin 0 -> 24 bytes .../4-5-ofp_features_request.packet | Bin 0 -> 8 bytes .../4-50-ofp_meter_stats_reply.packet | Bin 0 -> 72 bytes .../4-51-ofp_meter_features_request.packet | Bin 0 -> 16 bytes .../4-52-ofp_meter_features_reply.packet | Bin 0 -> 32 bytes .../4-53-ofp_port_desc_request.packet | Bin 0 -> 16 bytes .../4-54-ofp_port_desc_reply.packet | Bin 0 -> 144 bytes .../4-55-ofp_table_features_request.packet | Bin 0 -> 11128 bytes .../4-56-ofp_table_features_reply.packet | Bin 0 -> 11128 bytes .../4-57-ofp_group_stats_request.packet | Bin 0 -> 24 bytes .../4-58-ofp_group_stats_reply.packet | Bin 0 -> 72 bytes test/packet_data/4-59-ofp_packet_in.packet | Bin 0 -> 378 bytes .../packet_data/4-6-ofp_features_reply.packet | Bin 0 -> 32 bytes test/packet_data/4-60-ofp_flow_mod.packet | Bin 0 -> 400 bytes .../4-61-ofp_experimenter_request.packet | Bin 0 -> 32 bytes .../4-62-ofp_experimenter_reply.packet | Bin 0 -> 40 bytes .../4-63-onf_flow_monitor_request.packet | Bin 0 -> 408 bytes test/packet_data/4-7-ofp_set_config.packet | Bin 0 -> 12 bytes .../4-8-ofp_get_config_request.packet | Bin 0 -> 8 bytes .../4-9-ofp_get_config_reply.packet | Bin 0 -> 12 bytes .../libofproto-OFP13-echo_reply.packet | Bin 0 -> 8 bytes .../libofproto-OFP13-echo_request.packet | Bin 0 -> 8 bytes .../libofproto-OFP13-error_msg.packet | Bin 0 -> 20 bytes .../libofproto-OFP13-features_reply.packet | Bin 0 -> 32 bytes .../libofproto-OFP13-flow_mod.packet | Bin 0 -> 256 bytes .../libofproto-OFP13-flow_mod.truncated64 | 0 ...bofproto-OFP13-flow_mod_conjunction.packet | Bin 0 -> 144 bytes ...ibofproto-OFP13-flow_mod_match_conj.packet | Bin 0 -> 104 bytes .../libofproto-OFP13-flow_removed.packet | Bin 0 -> 120 bytes .../libofproto-OFP13-get_config_reply.packet | Bin 0 -> 12 bytes .../packet_data/libofproto-OFP13-hello.packet | Bin 0 -> 16 bytes .../libofproto-OFP13-meter_mod.packet | Bin 0 -> 48 bytes ...OFP13-ofp_packet_out_packet_library.packet | Bin 0 -> 138 bytes .../libofproto-OFP13-packet_in.packet | Bin 0 -> 70 bytes .../libofproto-OFP13-port_mod.packet | Bin 0 -> 40 bytes .../libofproto-OFP13-port_status.packet | Bin 0 -> 80 bytes .../libofproto-OFP13-set_config.packet | Bin 0 -> 12 bytes .../libofproto-OFP13-table_mod.packet | Bin 0 -> 16 bytes test/packet_data/nx_bundle.raw | Bin 0 -> 40 bytes test/packet_data/nx_bundle_load.raw | Bin 0 -> 40 bytes test/packet_data/nx_clone.raw | Bin 0 -> 32 bytes test/packet_data/nx_controller.raw | Bin 0 -> 16 bytes test/packet_data/nx_controller2.raw | Bin 0 -> 64 bytes test/packet_data/nx_ct(alg=ftp).raw | Bin 0 -> 24 bytes test/packet_data/nx_ct(alg=tftp).raw | Bin 0 -> 24 bytes test/packet_data/nx_ct(commit).raw | Bin 0 -> 24 bytes test/packet_data/nx_ct(commit, force).raw | Bin 0 -> 24 bytes ...7],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw | Bin 0 -> 72 bytes ...t,exec(load:0xf009->NXM_NX_CT_MARK[])).raw | Bin 0 -> 48 bytes ...e,exec(load:0xf009->NXM_NX_CT_MARK[])).raw | Bin 0 -> 48 bytes test/packet_data/nx_ct(commit,nat(dst)).raw | Bin 0 -> 40 bytes ...t,nat(dst=10.0.0.128-10.0.0.254,hash)).raw | Bin 0 -> 48 bytes test/packet_data/nx_ct(commit,nat(src)).raw | Bin 0 -> 40 bytes ..._ct(commit,nat(src=10.0.0.240,random)).raw | Bin 0 -> 48 bytes ...40-10.0.0.254:32768-65535,persistent)).raw | Bin 0 -> 56 bytes ...at(src=10.0.0.240:32768-65535,random)).raw | Bin 0 -> 48 bytes ...:20c:29ff:fe88:a18b]:255-4096,random)).raw | Bin 0 -> 80 bytes ...0c:29ff:fe88:a18b]:255-4096,random))fd.raw | Bin 0 -> 80 bytes ...88:1-fe80::20c:29ff:fe88:a18b,random)).raw | Bin 0 -> 72 bytes ...(src=fe80::20c:29ff:fe88:a18b,random)).raw | Bin 0 -> 56 bytes test/packet_data/nx_ct(nat).raw | Bin 0 -> 40 bytes test/packet_data/nx_ct(table=10).raw | Bin 0 -> 24 bytes test/packet_data/nx_ct(zone=10).raw | Bin 0 -> 24 bytes .../nx_ct(zone=NXM_NX_REG0[0..15]).raw | Bin 0 -> 24 bytes test/packet_data/nx_ct.raw | Bin 0 -> 24 bytes test/packet_data/nx_ct_clear.raw | Bin 0 -> 16 bytes test/packet_data/nx_dec_ttl.raw | Bin 0 -> 16 bytes test/packet_data/nx_dec_ttl_cnt_ids.raw | Bin 0 -> 32 bytes test/packet_data/nx_exit.raw | Bin 0 -> 16 bytes test/packet_data/nx_fin_timeout.raw | Bin 0 -> 16 bytes test/packet_data/nx_learn.raw | Bin 0 -> 72 bytes test/packet_data/nx_learn2.raw | Bin 0 -> 80 bytes test/packet_data/nx_multipath.raw | Bin 0 -> 32 bytes test/packet_data/nx_note.raw | Bin 0 -> 16 bytes test/packet_data/nx_output_reg.raw | Bin 0 -> 24 bytes test/packet_data/nx_output_trunc.raw | Bin 0 -> 16 bytes test/packet_data/nx_packet_in2.raw | Bin 0 -> 152 bytes test/packet_data/nx_pop_queue.raw | Bin 0 -> 16 bytes test/packet_data/nx_reg_load.raw | Bin 0 -> 24 bytes test/packet_data/nx_reg_move.raw | Bin 0 -> 24 bytes test/packet_data/nx_resubmit.raw | Bin 0 -> 16 bytes test/packet_data/nx_resubmit_table.raw | Bin 0 -> 16 bytes test/packet_data/nx_resubmit_table_ct.raw | Bin 0 -> 16 bytes test/packet_data/nx_sample.raw | Bin 0 -> 24 bytes test/packet_data/nx_sample2.raw | Bin 0 -> 32 bytes test/packet_data/nx_sample3.raw | Bin 0 -> 32 bytes test/packet_data/nx_set_controller_id.raw | Bin 0 -> 24 bytes test/packet_data/nx_set_packet_in_format.raw | Bin 0 -> 20 bytes test/packet_data/nx_set_queue.raw | Bin 0 -> 16 bytes test/packet_data/nx_set_tunnel.raw | Bin 0 -> 16 bytes test/packet_data/nx_set_tunnel64.raw | Bin 0 -> 24 bytes test/packet_data/ofp_echo_reply.raw | Bin 0 -> 8 bytes test/packet_data/ofp_echo_request.raw | Bin 0 -> 8 bytes test/packet_data/ofp_error.raw | Bin 0 -> 20 bytes test/packet_data/ofp_features_reply.raw | Bin 0 -> 32 bytes test/packet_data/ofp_features_request.raw | Bin 0 -> 8 bytes test/packet_data/ofp_get_config_reply.raw | Bin 0 -> 12 bytes test/packet_data/ofp_get_config_request.raw | Bin 0 -> 8 bytes test/packet_data/ofp_hello.raw | Bin 0 -> 16 bytes test/packet_data/ofp_set_config.raw | Bin 0 -> 12 bytes .../ovs-ofctl-of13-action_conjunction.packet | Bin 0 -> 144 bytes .../ovs-ofctl-of13-action_controller.packet | Bin 0 -> 80 bytes .../ovs-ofctl-of13-action_controller2.packet | Bin 0 -> 128 bytes .../ovs-ofctl-of13-action_ct.packet | Bin 0 -> 104 bytes .../ovs-ofctl-of13-action_ct_exec.packet | Bin 0 -> 120 bytes .../ovs-ofctl-of13-action_ct_nat.packet | Bin 0 -> 128 bytes .../ovs-ofctl-of13-action_ct_nat_v6.packet | Bin 0 -> 144 bytes ...s-ofctl-of13-action_dec_ttl_cnt_ids.packet | Bin 0 -> 104 bytes .../ovs-ofctl-of13-action_fintimeout.packet | Bin 0 -> 88 bytes .../ovs-ofctl-of13-action_learn.packet | Bin 0 -> 256 bytes .../ovs-ofctl-of13-action_note.packet | Bin 0 -> 80 bytes .../ovs-ofctl-of13-action_output_trunc.packet | Bin 0 -> 80 bytes .../ovs-ofctl-of13-action_resubmit.packet | Bin 0 -> 144 bytes .../ovs-ofctl-of13-action_sample.packet | Bin 0 -> 88 bytes .../ovs-ofctl-of13-action_sample2.packet | Bin 0 -> 96 bytes .../ovs-ofctl-of13-action_stack_pop.packet | Bin 0 -> 88 bytes .../ovs-ofctl-of13-action_stack_push.packet | Bin 0 -> 88 bytes .../ovs-ofctl-of13-match_conj.packet | Bin 0 -> 96 bytes ...s-ofctl-of13-match_load_nx_register.packet | Bin 0 -> 104 bytes ...s-ofctl-of13-match_move_nx_register.packet | Bin 0 -> 104 bytes .../ovs-ofctl-of13-match_pkt_mark.packet | Bin 0 -> 96 bytes ...vs-ofctl-of13-match_pkt_mark_masked.packet | Bin 0 -> 96 bytes test/test_helper.exs | 1 + test/tres_test.exs | 4 + 338 files changed, 9081 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 config/config.exs create mode 100644 lib/openflow.ex create mode 120000 lib/openflow/.#nx_packet_in2.ex create mode 100644 lib/openflow/action.ex create mode 100644 lib/openflow/actions/copy_ttl_in.ex create mode 100644 lib/openflow/actions/copy_ttl_out.ex create mode 100644 lib/openflow/actions/dec_mpls_ttl.ex create mode 100644 lib/openflow/actions/dec_nw_ttl.ex create mode 100644 lib/openflow/actions/experimenter.ex create mode 100644 lib/openflow/actions/group.ex create mode 100644 lib/openflow/actions/nx_bundle.ex create mode 100644 lib/openflow/actions/nx_bundle_load.ex create mode 100644 lib/openflow/actions/nx_clone.ex create mode 100644 lib/openflow/actions/nx_conjunction.ex create mode 100644 lib/openflow/actions/nx_conntrack.ex create mode 100644 lib/openflow/actions/nx_controller.ex create mode 100644 lib/openflow/actions/nx_controller2.ex create mode 100644 lib/openflow/actions/nx_ct_clear.ex create mode 100644 lib/openflow/actions/nx_dec_mpls_ttl.ex create mode 100644 lib/openflow/actions/nx_dec_ttl.ex create mode 100644 lib/openflow/actions/nx_dec_ttl_cnt_ids.ex create mode 100644 lib/openflow/actions/nx_exit.ex create mode 100644 lib/openflow/actions/nx_fin_timeout.ex create mode 100644 lib/openflow/actions/nx_flow_spec.ex create mode 100644 lib/openflow/actions/nx_flow_spec_load.ex create mode 100644 lib/openflow/actions/nx_flow_spec_match.ex create mode 100644 lib/openflow/actions/nx_flow_spec_output.ex create mode 100644 lib/openflow/actions/nx_learn.ex create mode 100644 lib/openflow/actions/nx_learn2.ex create mode 100644 lib/openflow/actions/nx_multipath.ex create mode 100644 lib/openflow/actions/nx_nat.ex create mode 100644 lib/openflow/actions/nx_note.ex create mode 100644 lib/openflow/actions/nx_output_reg.ex create mode 100644 lib/openflow/actions/nx_output_reg2.ex create mode 100644 lib/openflow/actions/nx_output_trunc.ex create mode 100644 lib/openflow/actions/nx_pop_mpls.ex create mode 100644 lib/openflow/actions/nx_pop_queue.ex create mode 100644 lib/openflow/actions/nx_push_mpls.ex create mode 100644 lib/openflow/actions/nx_reg_load.ex create mode 100644 lib/openflow/actions/nx_reg_load2.ex create mode 100644 lib/openflow/actions/nx_reg_move.ex create mode 100644 lib/openflow/actions/nx_resubmit.ex create mode 100644 lib/openflow/actions/nx_resubmit_table.ex create mode 100644 lib/openflow/actions/nx_resubmit_table_ct.ex create mode 100644 lib/openflow/actions/nx_sample.ex create mode 100644 lib/openflow/actions/nx_sample2.ex create mode 100644 lib/openflow/actions/nx_sample3.ex create mode 100644 lib/openflow/actions/nx_set_mpls_label.ex create mode 100644 lib/openflow/actions/nx_set_mpls_tc.ex create mode 100644 lib/openflow/actions/nx_set_mpls_ttl.ex create mode 100644 lib/openflow/actions/nx_set_queue.ex create mode 100644 lib/openflow/actions/nx_set_tunnel.ex create mode 100644 lib/openflow/actions/nx_set_tunnel64.ex create mode 100644 lib/openflow/actions/nx_stack_pop.ex create mode 100644 lib/openflow/actions/nx_stack_push.ex create mode 100644 lib/openflow/actions/nx_write_metadata.ex create mode 100644 lib/openflow/actions/output.ex create mode 100644 lib/openflow/actions/pop_mpls.ex create mode 100644 lib/openflow/actions/pop_pbb.ex create mode 100644 lib/openflow/actions/pop_vlan.ex create mode 100644 lib/openflow/actions/push_mpls.ex create mode 100644 lib/openflow/actions/push_pbb.ex create mode 100644 lib/openflow/actions/push_vlan.ex create mode 100644 lib/openflow/actions/set_field.ex create mode 100644 lib/openflow/actions/set_mpls_ttl.ex create mode 100644 lib/openflow/actions/set_nw_ttl.ex create mode 100644 lib/openflow/actions/set_queue.ex create mode 100644 lib/openflow/barrier/reply.ex create mode 100644 lib/openflow/barrier/request.ex create mode 100644 lib/openflow/buckets.ex create mode 100644 lib/openflow/echo.ex create mode 100644 lib/openflow/echo/reply.ex create mode 100644 lib/openflow/echo/request.ex create mode 100644 lib/openflow/enums.ex create mode 100644 lib/openflow/error_msg.ex create mode 100644 lib/openflow/experimenter.ex create mode 100644 lib/openflow/features.ex create mode 100644 lib/openflow/features/reply.ex create mode 100644 lib/openflow/features/request.ex create mode 100644 lib/openflow/flow_mod.ex create mode 100644 lib/openflow/flow_removed.ex create mode 100644 lib/openflow/get_async/reply.ex create mode 100644 lib/openflow/get_async/request.ex create mode 100644 lib/openflow/get_config.ex create mode 100644 lib/openflow/get_config/reply.ex create mode 100644 lib/openflow/get_config/request.ex create mode 100644 lib/openflow/group_mod.ex create mode 100644 lib/openflow/hello.ex create mode 100644 lib/openflow/instruction.ex create mode 100644 lib/openflow/instructions/apply_actions.ex create mode 100644 lib/openflow/instructions/clear_actions.ex create mode 100644 lib/openflow/instructions/experimenter.ex create mode 100644 lib/openflow/instructions/goto_table.ex create mode 100644 lib/openflow/instructions/meter.ex create mode 100644 lib/openflow/instructions/write_actions.ex create mode 100644 lib/openflow/instructions/write_metadata.ex create mode 100644 lib/openflow/match.ex create mode 100644 lib/openflow/match/field.ex create mode 100644 lib/openflow/meter_band.ex create mode 100644 lib/openflow/meter_band/drop.ex create mode 100644 lib/openflow/meter_band/experimenter.ex create mode 100644 lib/openflow/meter_band/remark.ex create mode 100644 lib/openflow/meter_mod.ex create mode 100644 lib/openflow/multipart/aggregate/reply.ex create mode 100644 lib/openflow/multipart/aggregate/request.ex create mode 100644 lib/openflow/multipart/desc/reply.ex create mode 100644 lib/openflow/multipart/desc/request.ex create mode 100644 lib/openflow/multipart/flow/reply.ex create mode 100644 lib/openflow/multipart/flow/request.ex create mode 100644 lib/openflow/multipart/group/ *Minibuf-1* create mode 120000 lib/openflow/multipart/group/.# *Minibuf-1* create mode 100644 lib/openflow/multipart/group/reply.ex create mode 100644 lib/openflow/multipart/group/request.ex create mode 100644 lib/openflow/multipart/group_desc/reply.ex create mode 100644 lib/openflow/multipart/group_desc/request.ex create mode 100644 lib/openflow/multipart/group_features/reply.ex create mode 100644 lib/openflow/multipart/group_features/request.ex create mode 100644 lib/openflow/multipart/meter/reply.ex create mode 100644 lib/openflow/multipart/meter/request.ex create mode 100644 lib/openflow/multipart/meter_config/request.ex create mode 100644 lib/openflow/multipart/port_stats/reply.ex create mode 100644 lib/openflow/multipart/port_stats/request.ex create mode 100644 lib/openflow/multipart/queue/reply.ex create mode 100644 lib/openflow/multipart/queue/request.ex create mode 100644 lib/openflow/multipart/reply.ex create mode 100644 lib/openflow/multipart/request.ex create mode 100644 lib/openflow/multipart/table/reply.ex create mode 100644 lib/openflow/multipart/table/request.ex create mode 100644 lib/openflow/nx_packet_in2.ex create mode 100644 lib/openflow/nx_packet_in_format.ex create mode 100644 lib/openflow/nx_set_controller_id.ex create mode 100644 lib/openflow/packet_in.ex create mode 100644 lib/openflow/packet_out.ex create mode 100644 lib/openflow/port.ex create mode 100644 lib/openflow/port_mod.ex create mode 100644 lib/openflow/port_status.ex create mode 100644 lib/openflow/role/reply.ex create mode 100644 lib/openflow/role/request.ex create mode 100644 lib/openflow/set_async.ex create mode 100644 lib/openflow/set_config.ex create mode 100644 lib/openflow/table_mod.ex create mode 100644 lib/openflow/utils.ex create mode 100644 lib/tres.ex create mode 100644 lib/tres/application.ex create mode 100644 lib/tres/message_handler.ex create mode 100644 lib/tres/message_handler_sup.ex create mode 100644 lib/tres/secure_channel.ex create mode 100644 lib/tres/secure_channel_state.ex create mode 100644 lib/tres/switch_registry.ex create mode 100644 lib/tres/utils.ex create mode 100644 mix.exs create mode 100644 mix.lock create mode 100644 test/ofp_action_test.exs create mode 100644 test/ofp_echo_test.exs create mode 100644 test/ofp_error_test.exs create mode 100644 test/ofp_features_test.exs create mode 100644 test/ofp_flow_mod_test.exs create mode 100644 test/ofp_flow_removed_test.exs create mode 100644 test/ofp_get_config_test.exs create mode 100644 test/ofp_group_mod_test.exs create mode 100644 test/ofp_hello_test.exs create mode 100644 test/ofp_packet_in_test.exs create mode 100644 test/ofp_packet_out_test.exs create mode 100644 test/ofp_port_mod_test.exs create mode 100644 test/ofp_port_status_test.exs create mode 100644 test/ofp_set_config_test.exs create mode 100644 test/ofp_table_mod_test.exs create mode 120000 test/packet_data/.#nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw create mode 100644 test/packet_data/4-0-ofp_desc_reply.packet create mode 100644 test/packet_data/4-1-ofp_packet_out.packet create mode 100644 test/packet_data/4-10-ofp_hello.packet create mode 100644 test/packet_data/4-11-ofp_flow_stats_request.packet create mode 100644 test/packet_data/4-12-ofp_flow_stats_reply.packet create mode 100644 test/packet_data/4-13-ofp_echo_request.packet create mode 100644 test/packet_data/4-14-ofp_echo_reply.packet create mode 100644 test/packet_data/4-15-ofp_error_msg.packet create mode 100644 test/packet_data/4-16-ofp_experimenter.packet create mode 100644 test/packet_data/4-17-ofp_barrier_request.packet create mode 100644 test/packet_data/4-18-ofp_barrier_reply.packet create mode 100644 test/packet_data/4-19-ofp_role_request.packet create mode 100644 test/packet_data/4-2-ofp_flow_mod.packet create mode 100644 test/packet_data/4-20-ofp_role_reply.packet create mode 100644 test/packet_data/4-21-ofp_group_mod.packet create mode 100644 test/packet_data/4-22-ofp_port_mod.packet create mode 100644 test/packet_data/4-23-ofp_table_mod.packet create mode 100644 test/packet_data/4-24-ofp_desc_request.packet create mode 100644 test/packet_data/4-25-ofp_aggregate_stats_request.packet create mode 100644 test/packet_data/4-26-ofp_aggregate_stats_reply.packet create mode 100644 test/packet_data/4-27-ofp_table_stats_request.packet create mode 100644 test/packet_data/4-28-ofp_table_stats_reply.packet create mode 100644 test/packet_data/4-29-ofp_port_stats_request.packet create mode 100644 test/packet_data/4-3-ofp_flow_mod.packet create mode 100644 test/packet_data/4-30-ofp_port_stats_reply.packet create mode 100644 test/packet_data/4-31-ofp_group_features_request.packet create mode 100644 test/packet_data/4-32-ofp_group_features_reply.packet create mode 100644 test/packet_data/4-33-ofp_group_desc_request.packet create mode 100644 test/packet_data/4-34-ofp_group_desc_reply.packet create mode 100644 test/packet_data/4-35-ofp_queue_get_config_request.packet create mode 100644 test/packet_data/4-36-ofp_queue_get_config_reply.packet create mode 100644 test/packet_data/4-37-ofp_queue_stats_request.packet create mode 100644 test/packet_data/4-38-ofp_queue_stats_reply.packet create mode 100644 test/packet_data/4-39-ofp_port_status.packet create mode 100644 test/packet_data/4-4-ofp_packet_in.packet create mode 100644 test/packet_data/4-40-ofp_flow_removed.packet create mode 100644 test/packet_data/4-41-ofp_error_msg_experimenter.packet create mode 100644 test/packet_data/4-42-ofp_get_async_request.packet create mode 100644 test/packet_data/4-43-ofp_get_async_reply.packet create mode 100644 test/packet_data/4-44-ofp_set_async.packet create mode 100644 test/packet_data/4-45-ofp_meter_mod.packet create mode 100644 test/packet_data/4-46-ofp_flow_mod.packet create mode 100644 test/packet_data/4-47-ofp_meter_config_request.packet create mode 100644 test/packet_data/4-48-ofp_meter_config_reply.packet create mode 100644 test/packet_data/4-49-ofp_meter_stats_request.packet create mode 100644 test/packet_data/4-5-ofp_features_request.packet create mode 100644 test/packet_data/4-50-ofp_meter_stats_reply.packet create mode 100644 test/packet_data/4-51-ofp_meter_features_request.packet create mode 100644 test/packet_data/4-52-ofp_meter_features_reply.packet create mode 100644 test/packet_data/4-53-ofp_port_desc_request.packet create mode 100644 test/packet_data/4-54-ofp_port_desc_reply.packet create mode 100644 test/packet_data/4-55-ofp_table_features_request.packet create mode 100644 test/packet_data/4-56-ofp_table_features_reply.packet create mode 100644 test/packet_data/4-57-ofp_group_stats_request.packet create mode 100644 test/packet_data/4-58-ofp_group_stats_reply.packet create mode 100644 test/packet_data/4-59-ofp_packet_in.packet create mode 100644 test/packet_data/4-6-ofp_features_reply.packet create mode 100644 test/packet_data/4-60-ofp_flow_mod.packet create mode 100644 test/packet_data/4-61-ofp_experimenter_request.packet create mode 100644 test/packet_data/4-62-ofp_experimenter_reply.packet create mode 100644 test/packet_data/4-63-onf_flow_monitor_request.packet create mode 100644 test/packet_data/4-7-ofp_set_config.packet create mode 100644 test/packet_data/4-8-ofp_get_config_request.packet create mode 100644 test/packet_data/4-9-ofp_get_config_reply.packet create mode 100644 test/packet_data/libofproto-OFP13-echo_reply.packet create mode 100644 test/packet_data/libofproto-OFP13-echo_request.packet create mode 100644 test/packet_data/libofproto-OFP13-error_msg.packet create mode 100644 test/packet_data/libofproto-OFP13-features_reply.packet create mode 100644 test/packet_data/libofproto-OFP13-flow_mod.packet create mode 100644 test/packet_data/libofproto-OFP13-flow_mod.truncated64 create mode 100644 test/packet_data/libofproto-OFP13-flow_mod_conjunction.packet create mode 100644 test/packet_data/libofproto-OFP13-flow_mod_match_conj.packet create mode 100644 test/packet_data/libofproto-OFP13-flow_removed.packet create mode 100644 test/packet_data/libofproto-OFP13-get_config_reply.packet create mode 100644 test/packet_data/libofproto-OFP13-hello.packet create mode 100644 test/packet_data/libofproto-OFP13-meter_mod.packet create mode 100644 test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet create mode 100644 test/packet_data/libofproto-OFP13-packet_in.packet create mode 100644 test/packet_data/libofproto-OFP13-port_mod.packet create mode 100644 test/packet_data/libofproto-OFP13-port_status.packet create mode 100644 test/packet_data/libofproto-OFP13-set_config.packet create mode 100644 test/packet_data/libofproto-OFP13-table_mod.packet create mode 100644 test/packet_data/nx_bundle.raw create mode 100644 test/packet_data/nx_bundle_load.raw create mode 100644 test/packet_data/nx_clone.raw create mode 100644 test/packet_data/nx_controller.raw create mode 100644 test/packet_data/nx_controller2.raw create mode 100644 test/packet_data/nx_ct(alg=ftp).raw create mode 100644 test/packet_data/nx_ct(alg=tftp).raw create mode 100644 test/packet_data/nx_ct(commit).raw create mode 100644 test/packet_data/nx_ct(commit, force).raw create mode 100644 test/packet_data/nx_ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw create mode 100644 test/packet_data/nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw create mode 100644 test/packet_data/nx_ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])).raw create mode 100644 test/packet_data/nx_ct(commit,nat(dst)).raw create mode 100644 test/packet_data/nx_ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)).raw create mode 100644 test/packet_data/nx_ct(commit,nat(src)).raw create mode 100644 test/packet_data/nx_ct(commit,nat(src=10.0.0.240,random)).raw create mode 100644 test/packet_data/nx_ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)).raw create mode 100644 test/packet_data/nx_ct(commit,nat(src=10.0.0.240:32768-65535,random)).raw create mode 100644 test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)).raw create mode 100644 test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))fd.raw create mode 100644 test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)).raw create mode 100644 test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)).raw create mode 100644 test/packet_data/nx_ct(nat).raw create mode 100644 test/packet_data/nx_ct(table=10).raw create mode 100644 test/packet_data/nx_ct(zone=10).raw create mode 100644 test/packet_data/nx_ct(zone=NXM_NX_REG0[0..15]).raw create mode 100644 test/packet_data/nx_ct.raw create mode 100644 test/packet_data/nx_ct_clear.raw create mode 100644 test/packet_data/nx_dec_ttl.raw create mode 100644 test/packet_data/nx_dec_ttl_cnt_ids.raw create mode 100644 test/packet_data/nx_exit.raw create mode 100644 test/packet_data/nx_fin_timeout.raw create mode 100644 test/packet_data/nx_learn.raw create mode 100644 test/packet_data/nx_learn2.raw create mode 100644 test/packet_data/nx_multipath.raw create mode 100644 test/packet_data/nx_note.raw create mode 100644 test/packet_data/nx_output_reg.raw create mode 100644 test/packet_data/nx_output_trunc.raw create mode 100644 test/packet_data/nx_packet_in2.raw create mode 100644 test/packet_data/nx_pop_queue.raw create mode 100644 test/packet_data/nx_reg_load.raw create mode 100644 test/packet_data/nx_reg_move.raw create mode 100644 test/packet_data/nx_resubmit.raw create mode 100644 test/packet_data/nx_resubmit_table.raw create mode 100644 test/packet_data/nx_resubmit_table_ct.raw create mode 100644 test/packet_data/nx_sample.raw create mode 100644 test/packet_data/nx_sample2.raw create mode 100644 test/packet_data/nx_sample3.raw create mode 100644 test/packet_data/nx_set_controller_id.raw create mode 100644 test/packet_data/nx_set_packet_in_format.raw create mode 100644 test/packet_data/nx_set_queue.raw create mode 100644 test/packet_data/nx_set_tunnel.raw create mode 100644 test/packet_data/nx_set_tunnel64.raw create mode 100644 test/packet_data/ofp_echo_reply.raw create mode 100644 test/packet_data/ofp_echo_request.raw create mode 100644 test/packet_data/ofp_error.raw create mode 100644 test/packet_data/ofp_features_reply.raw create mode 100644 test/packet_data/ofp_features_request.raw create mode 100644 test/packet_data/ofp_get_config_reply.raw create mode 100644 test/packet_data/ofp_get_config_request.raw create mode 100644 test/packet_data/ofp_hello.raw create mode 100644 test/packet_data/ofp_set_config.raw create mode 100644 test/packet_data/ovs-ofctl-of13-action_conjunction.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_controller.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_controller2.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_ct.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_ct_exec.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_ct_nat.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_ct_nat_v6.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_dec_ttl_cnt_ids.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_fintimeout.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_learn.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_note.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_output_trunc.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_resubmit.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_sample.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_sample2.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_stack_pop.packet create mode 100644 test/packet_data/ovs-ofctl-of13-action_stack_push.packet create mode 100644 test/packet_data/ovs-ofctl-of13-match_conj.packet create mode 100644 test/packet_data/ovs-ofctl-of13-match_load_nx_register.packet create mode 100644 test/packet_data/ovs-ofctl-of13-match_move_nx_register.packet create mode 100644 test/packet_data/ovs-ofctl-of13-match_pkt_mark.packet create mode 100644 test/packet_data/ovs-ofctl-of13-match_pkt_mark_masked.packet create mode 100644 test/test_helper.exs create mode 100644 test/tres_test.exs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..12179ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where 3rd-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez diff --git a/README.md b/README.md new file mode 100644 index 0000000..633bbbb --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Tres + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `tres` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:tres, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at [https://hexdocs.pm/tres](https://hexdocs.pm/tres). + diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..93b9ad9 --- /dev/null +++ b/config/config.exs @@ -0,0 +1,16 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +config :tres, + callback_module: Tres.ExampleHandler, + max_connections: 10, + num_acceptors: 10, + protocol: :tcp, + port: 6653 + +config :logger, + level: :info, + format: "$date $time [$level] $metadata$message\n", + metadata: [:application], + handle_otp_reports: true diff --git a/lib/openflow.ex b/lib/openflow.ex new file mode 100644 index 0000000..a5ca2bc --- /dev/null +++ b/lib/openflow.ex @@ -0,0 +1,50 @@ +defmodule Openflow do + @moduledoc "OpenFlow Protocol format codec" + + @ofp_header_size 8 + + @spec read(binary()) :: {:ok, map()} | {:error, :binary_too_small} + def read(binary) + when byte_size(binary) < @ofp_header_size do + {:error, :binary_too_small} + end + def read(<<_v::8, _t::8, len::16, _x::32, _r::bytes>> = binary) + when byte_size(binary) < len do + {:error, :binary_too_small} + end + def read(<>) do + body_len = len - @ofp_header_size + <> = binary2 + + result = type + |> Openflow.Enums.to_atom(:openflow_codec) + |> do_read(body_bin) + + case result do + {:ok, struct} -> {:ok, %{struct|version: ver, xid: xid}, rest} + {:error, reason} -> {:error, reason} + end + end + + def to_binary(%{__struct__: encoder, version: version, xid: xid} = msg) do + case encoder.to_binary(msg) do + body_bin when is_binary(body_bin) -> + length = @ofp_header_size + byte_size(body_bin) + <> + {:error, reason} -> + {:error, reason} + end + end + + # private functions + + defp do_read({:error, reason}, _) do + {:error, reason} + end + defp do_read(decoder, body_bin) do + case decoder.read(body_bin) do + {:error, reason} -> {:error, reason} + result when is_map(result) -> {:ok, result} + end + end +end diff --git a/lib/openflow/.#nx_packet_in2.ex b/lib/openflow/.#nx_packet_in2.ex new file mode 120000 index 0000000..b4344b8 --- /dev/null +++ b/lib/openflow/.#nx_packet_in2.ex @@ -0,0 +1 @@ +shun159@shun159.5674:1510580208 \ No newline at end of file diff --git a/lib/openflow/action.ex b/lib/openflow/action.ex new file mode 100644 index 0000000..2bccd6d --- /dev/null +++ b/lib/openflow/action.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Action do + + def read(action_bin) do + do_read([], action_bin) + end + + def to_binary(actions) when is_list(actions) do + to_binary(<<>>, actions) + end + def to_binary(action) do + to_binary([action]) + end + + # private functions + + defp do_read(acc, <<>>), do: Enum.reverse(acc) + defp do_read(acc, <<0::32, _::bytes>>), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + codec = Openflow.Enums.to_atom(type, :action_type) + do_read([codec.read(action_bin)|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [action|rest]) do + codec = action.__struct__ + to_binary(<>, rest) + end +end diff --git a/lib/openflow/actions/copy_ttl_in.ex b/lib/openflow/actions/copy_ttl_in.ex new file mode 100644 index 0000000..83347b4 --- /dev/null +++ b/lib/openflow/actions/copy_ttl_in.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.CopyTtlIn do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 12 + + def new do + %CopyTtlIn{} + end + + def to_binary(%CopyTtlIn{}) do + <<12::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<12::16, 8::16, _::size(4)-unit(8)>>) do + %CopyTtlIn{} + end +end diff --git a/lib/openflow/actions/copy_ttl_out.ex b/lib/openflow/actions/copy_ttl_out.ex new file mode 100644 index 0000000..759c0ad --- /dev/null +++ b/lib/openflow/actions/copy_ttl_out.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.CopyTtlOut do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 11 + + def new do + %CopyTtlOut{} + end + + def to_binary(%CopyTtlOut{}) do + <<11::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<11::16, 8::16, _::size(4)-unit(8)>>) do + %CopyTtlOut{} + end +end diff --git a/lib/openflow/actions/dec_mpls_ttl.ex b/lib/openflow/actions/dec_mpls_ttl.ex new file mode 100644 index 0000000..2931954 --- /dev/null +++ b/lib/openflow/actions/dec_mpls_ttl.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.DecMplsTtl do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 16 + + def new do + %DecMplsTtl{} + end + + def to_binary(%DecMplsTtl{}) do + <<16::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<16::16, 8::16, _::size(4)-unit(8)>>) do + %DecMplsTtl{} + end +end diff --git a/lib/openflow/actions/dec_nw_ttl.ex b/lib/openflow/actions/dec_nw_ttl.ex new file mode 100644 index 0000000..6d23b5d --- /dev/null +++ b/lib/openflow/actions/dec_nw_ttl.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.DecNwTtl do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 24 + + def new do + %DecNwTtl{} + end + + def to_binary(%DecNwTtl{}) do + <<24::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<24::16, 8::16, _::size(4)-unit(8)>>) do + %DecNwTtl{} + end +end diff --git a/lib/openflow/actions/experimenter.ex b/lib/openflow/actions/experimenter.ex new file mode 100644 index 0000000..8390026 --- /dev/null +++ b/lib/openflow/actions/experimenter.ex @@ -0,0 +1,32 @@ +defmodule Openflow.Action.Experimenter do + defstruct(exp_id: 0, data: "") + + alias __MODULE__ + + @experimter_size 8 + + def ofpat, do: 0xffff + + def new(exp_id, data \\ "") do + %Experimenter{exp_id: exp_id, data: data} + end + + def to_binary(%Experimenter{exp_id: exp_id, data: data}) do + length = @experimter_size + byte_size(data) + <<0xffff::16, length::16, exp_id::32, data::bytes>> + end + + def read(<<0xffff::16, _length::16, exp_id::32, exp_type::16, data::bytes>>) do + case Openflow.Utils.get_enum(exp_id, :action_vendor) do + vendor_id when is_integer(vendor_id) -> + %Experimenter{exp_id: exp_id, data: <>} + vendor when is_atom(vendor) -> + case Openflow.Utils.get_enum(exp_type, vendor) do + codec when is_atom(codec) -> + codec.read(<>) + exp_type when is_integer(exp_type) -> + %Experimenter{exp_id: exp_id, data: <>} + end + end + end +end diff --git a/lib/openflow/actions/group.ex b/lib/openflow/actions/group.ex new file mode 100644 index 0000000..ff2d756 --- /dev/null +++ b/lib/openflow/actions/group.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.Group do + defstruct(id: 0) + + alias __MODULE__ + + def ofpat, do: 22 + + def new(id) do + %Group{id: id} + end + + def to_binary(%Group{id: id}) do + <<22::16, 8::16, id::32>> + end + + def read(<<22::16, 8::16, id::32>>) do + %Group{id: id} + end +end diff --git a/lib/openflow/actions/nx_bundle.ex b/lib/openflow/actions/nx_bundle.ex new file mode 100644 index 0000000..6a65bc4 --- /dev/null +++ b/lib/openflow/actions/nx_bundle.ex @@ -0,0 +1,80 @@ +defmodule Openflow.Action.NxBundle do + defstruct( + algorithm: :active_backup, + hash_field: :eth_src, + basis: 0, + slave_type: :nx_in_port, + n_slaves: 0, + slaves: [] + ) + + @experimenter 0x00002320 + @nxast 12 + + alias __MODULE__ + + def new(options) do + hash_field = Keyword.get(options, :hash_field, :eth_src) + basis = Keyword.get(options, :basis, 0) + alg = Keyword.get(options, :algorithm, :active_backup) + slaves = Keyword.get(options, :slaves, []) + %NxBundle{algorithm: alg, + hash_field: hash_field, + basis: basis, + n_slaves: length(slaves), + slaves: slaves} + end + + def to_binary(%NxBundle{algorithm: alg, + hash_field: hash_field, + basis: basis, + slave_type: slave_type, + n_slaves: n_slaves, + slaves: slaves}) do + hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields) + alg_int = Openflow.Enums.to_int(alg, :nx_bd_algorithm) + slave_type_bin = Openflow.Match.codec_header(slave_type) + slaves_bin = codec_slaves(slaves) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + slave_len = n_slaves * 2 + <> = rest + alg = Openflow.Enums.to_atom(alg_int, :nx_bd_algorithm) + hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields) + slave_type = Openflow.Match.codec_header(slave_type_bin) + slaves = codec_slaves(slaves_bin) + n_slaves = length(slaves) + %NxBundle{algorithm: alg, + hash_field: hash_field, + basis: basis, + slave_type: slave_type, + n_slaves: n_slaves, + slaves: slaves} + end + + # private functions + + defp codec_slaves(slaves) when is_list(slaves) do + slaves1 = for slave <- slaves do + slave_int = Openflow.Utils.get_enum(slave, :openflow10_port_no) + <> + end + Enum.join(slaves1, "") + end + defp codec_slaves(slaves) when is_binary(slaves) do + for <> do + Openflow.Utils.get_enum(slave_int, :openflow10_port_no) + end + end +end diff --git a/lib/openflow/actions/nx_bundle_load.ex b/lib/openflow/actions/nx_bundle_load.ex new file mode 100644 index 0000000..a76d9f4 --- /dev/null +++ b/lib/openflow/actions/nx_bundle_load.ex @@ -0,0 +1,102 @@ +defmodule Openflow.Action.NxBundleLoad do + import Bitwise + + defstruct( + algorithm: :active_backup, + hash_field: :eth_src, + basis: 0, + slave_type: :nx_in_port, + n_slaves: 0, + slaves: [], + offset: 0, + n_bits: 0, + dst_field: nil + ) + + @experimenter 0x00002320 + @nxast 13 + + alias __MODULE__ + + def new(options) do + hash_field = Keyword.get(options, :hash_field, :eth_src) + basis = Keyword.get(options, :basis, 0) + alg = Keyword.get(options, :algorithm, :active_backup) + slaves = Keyword.get(options, :slaves, []) + dst_field = Keyword.get(options, :dst_field) + default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + %NxBundleLoad{algorithm: alg, + hash_field: hash_field, + basis: basis, + n_slaves: length(slaves), + slaves: slaves, + offset: ofs, + n_bits: n_bits, + dst_field: dst_field} + end + + def to_binary(%NxBundleLoad{algorithm: alg, + hash_field: hash_field, + basis: basis, + slave_type: slave_type, + n_slaves: n_slaves, + slaves: slaves, + offset: ofs, + n_bits: n_bits, + dst_field: dst_field}) do + hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields) + alg_int = Openflow.Enums.to_int(alg, :nx_bd_algorithm) + slave_type_bin = Openflow.Match.codec_header(slave_type) + slaves_bin = codec_slaves(slaves) + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + dst_field_bin = Openflow.Match.codec_header(dst_field) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + slave_len = n_slaves * 2 + <> = rest + alg = Openflow.Enums.to_atom(alg_int, :nx_bd_algorithm) + hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields) + slave_type = Openflow.Match.codec_header(slave_type_bin) + slaves = codec_slaves(slaves_bin) + n_slaves = length(slaves) + dst_field = Openflow.Match.codec_header(dst_field_bin) + %NxBundleLoad{algorithm: alg, + hash_field: hash_field, + basis: basis, + slave_type: slave_type, + n_slaves: n_slaves, + slaves: slaves, + offset: ofs, + n_bits: n_bits + 1, + dst_field: dst_field} + end + + # private functions + + defp codec_slaves(slaves) when is_list(slaves) do + slaves1 = for slave <- slaves do + slave_int = Openflow.Utils.get_enum(slave, :openflow10_port_no) + <> + end + Enum.join(slaves1, "") + end + defp codec_slaves(slaves) when is_binary(slaves) do + for <> do + Openflow.Utils.get_enum(slave_int, :openflow10_port_no) + end + end +end diff --git a/lib/openflow/actions/nx_clone.ex b/lib/openflow/actions/nx_clone.ex new file mode 100644 index 0000000..afa8618 --- /dev/null +++ b/lib/openflow/actions/nx_clone.ex @@ -0,0 +1,28 @@ +defmodule Openflow.Action.NxClone do + defstruct( + actions: [] + ) + + @experimenter 0x00002320 + @nxast 42 + + alias __MODULE__ + + def new(actions \\ []) do + %NxClone{actions: actions} + end + + def to_binary(%NxClone{actions: actions}) do + actions_bin = Openflow.Action.to_binary(actions) + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), actions_bin::bytes>>) do + actions = Openflow.Action.read(actions_bin) + %NxClone{actions: actions} + end +end diff --git a/lib/openflow/actions/nx_conjunction.ex b/lib/openflow/actions/nx_conjunction.ex new file mode 100644 index 0000000..3b90b4d --- /dev/null +++ b/lib/openflow/actions/nx_conjunction.ex @@ -0,0 +1,28 @@ +defmodule Openflow.Action.NxConjunction do + defstruct( + clause: 0, + n_clauses: 0, + id: 0 + ) + + @experimenter 0x00002320 + @nxast 34 + + alias __MODULE__ + + def new(options) do + clause = Keyword.get(options, :clause, 0) + n_clauses = Keyword.get(options, :n_clauses, 0) + id = Keyword.get(options, :id, 0) + %NxConjunction{clause: clause, n_clauses: n_clauses, id: id} + end + + def to_binary(%NxConjunction{clause: clause, n_clauses: n_clauses, id: id}) do + exp_body = <<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>>) do + %NxConjunction{clause: clause, n_clauses: n_clauses, id: id} + end +end diff --git a/lib/openflow/actions/nx_conntrack.ex b/lib/openflow/actions/nx_conntrack.ex new file mode 100644 index 0000000..76b033f --- /dev/null +++ b/lib/openflow/actions/nx_conntrack.ex @@ -0,0 +1,88 @@ +defmodule Openflow.Action.NxConntrack do + import Bitwise + + defstruct( + flags: [], + zone_src: nil, + zone_imm: 0, + zone_offset: nil, + zone_n_bits: nil, + recirc_table: 255, + alg: 0, + exec: [] + ) + + @experimenter 0x00002320 + @nxast 35 + + alias __MODULE__ + + def new(options \\ []) do + flags = Keyword.get(options, :flags, []) + zone_src = Keyword.get(options, :zone_src) + zone_ofs = Keyword.get(options, :zone_offset) + zone_n_bits = Keyword.get(options, :zone_n_bits) + zone_imm = Keyword.get(options, :zone_imm, 0) + recirc_table = Keyword.get(options, :recirc_table, 255) + alg = Keyword.get(options, :alg, 0) + exec = Keyword.get(options, :exec, []) + %NxConntrack{ + flags: flags, + zone_src: zone_src, + zone_imm: zone_imm, + zone_offset: zone_ofs, + zone_n_bits: zone_n_bits, + recirc_table: recirc_table, + alg: alg, + exec: exec + } + end + + def to_binary(%NxConntrack{ + flags: flags, + zone_src: zone_src, + zone_offset: zone_ofs, + zone_n_bits: zone_n_bits, + zone_imm: zone_imm, + recirc_table: recirc_table, + alg: alg, + exec: exec}) do + flags_int = Openflow.Enums.flags_to_int(flags, :nx_conntrack_flags) + {src_bin, ofs_nbits} = if not (is_nil(zone_src)) do + zone_src_bin = Openflow.Match.codec_header(zone_src) + {zone_src_bin, (zone_ofs <<< 6) ||| (zone_n_bits - 1)} + else + {<<0::32>>, zone_imm} + end + exec_bin = Openflow.Action.to_binary(exec) + exp_body = <<@experimenter::32, @nxast::16, flags_int::16, + src_bin::bytes, ofs_nbits::16, recirc_table::8, + 0::size(3)-unit(8), alg::16, exec_bin::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, flags_int::16, + src_bin::4-bytes, ofs_nbits::16-bits, recirc_table::8, + _::size(3)-unit(8), alg::16, exec_bin::bytes>>) do + flags = Openflow.Enums.int_to_flags(flags_int, :nx_conntrack_flags) + exec = Openflow.Action.read(exec_bin) + ct = %NxConntrack{ + flags: flags, + recirc_table: recirc_table, + alg: alg, + exec: exec + } + case src_bin do + <<0::32>> -> + <> = ofs_nbits + %{ct|zone_imm: zone_imm} + binary when is_binary(binary) -> + zone_src = Openflow.Match.codec_header(binary) + <> = ofs_nbits + %{ct|zone_src: zone_src, zone_offset: ofs, zone_n_bits: n_bits + 1} + end + end +end diff --git a/lib/openflow/actions/nx_controller.ex b/lib/openflow/actions/nx_controller.ex new file mode 100644 index 0000000..8128f40 --- /dev/null +++ b/lib/openflow/actions/nx_controller.ex @@ -0,0 +1,32 @@ +defmodule Openflow.Action.NxController do + defstruct( + max_len: :no_buffer, + id: 0, + reason: :action + ) + + @experimenter 0x00002320 + @nxast 20 + + alias __MODULE__ + + def new(options) do + max_len = Keyword.get(options, :max_len, :no_buffer) + controller_id = Keyword.get(options, :id, 0) + reason = Keyword.get(options, :reason, :action) + %NxController{max_len: max_len, id: controller_id, reason: reason} + end + + def to_binary(%NxController{max_len: max_len, id: controller_id, reason: reason}) do + max_len_int = Openflow.Utils.get_enum(max_len, :controller_max_len) + reason_int = Openflow.Enums.to_int(reason, :packet_in_reason) + exp_body = <<@experimenter::32, @nxast::16, max_len_int::16, controller_id::16, reason_int::8, 0::8>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, max_len_int::16, controller_id::16, reason_int::8, _::bytes>>) do + max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len) + reason = Openflow.Enums.to_atom(reason_int, :packet_in_reason) + %NxController{max_len: max_len, id: controller_id, reason: reason} + end +end diff --git a/lib/openflow/actions/nx_controller2.ex b/lib/openflow/actions/nx_controller2.ex new file mode 100644 index 0000000..6e1637c --- /dev/null +++ b/lib/openflow/actions/nx_controller2.ex @@ -0,0 +1,119 @@ +defmodule Openflow.Action.NxController2 do + defstruct( + max_len: :no_buffer, + id: 0, + reason: :action, + userdata: "", + pause: false + ) + + @experimenter 0x00002320 + @nxast 37 + + @prop_header_size 4 + + @prop_max_len 0 + @prop_ctl_id 1 + @prop_reason 2 + @prop_userdata 3 + @prop_pause 4 + + alias __MODULE__ + + def new(options) do + max_len = Keyword.get(options, :max_len, :no_buffer) + controller_id = Keyword.get(options, :id, 0) + reason = Keyword.get(options, :reason, :action) + userdata = Keyword.get(options, :userdata) + pause = Keyword.get(options, :pause, false) + %NxController2{max_len: max_len, + id: controller_id, + reason: reason, + userdata: userdata, + pause: pause} + end + + def to_binary(%NxController2{} = ctl) do + ext_header = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>> + prop_keys = get_prop_key(ctl) + props_bin = encode_prop("", prop_keys, ctl) + exp_body = <> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), body::bytes>>) do + %NxController2{} + |> decode_prop(body) + end + + # private functions + + defp get_prop_key(ctl) do + ctl + |> Map.from_struct + |> Enum.map(fn({k, v}) -> if(not is_nil(v), do: k, else: nil) end) + |> Enum.filter(fn(v) -> not is_nil(v) end) + end + + defp encode_prop(acc, [], _ctl), do: acc + defp encode_prop(acc, [prop|rest], ctl) do + value = Map.get(ctl, prop) + prop_bin = cond do + prop == :max_len and (value != :no_buffer or value < 0xffff) -> + padding_length = 2 + prop_length = @prop_header_size + 2 + padding_length + max_len_int = Openflow.Utils.get_enum(value, :controller_max_len) + <<@prop_max_len::16, prop_length::16, max_len_int::16, 0::size(padding_length)-unit(8)>> + prop == :id -> + padding_length = 2 + prop_length = @prop_header_size + 2 + padding_length + <<@prop_ctl_id::16, prop_length::16, value::16, 0::size(padding_length)-unit(8)>> + prop == :reason and value != :action -> + padding_length = 3 + prop_length = @prop_header_size + 1 + padding_length + reason_int = Openflow.Utils.get_enum(value, :packet_in_reason) + <<@prop_reason::16, prop_length::16, reason_int::8, 0::size(padding_length)-unit(8)>> + prop == :userdata and byte_size(value) > 0 -> + prop_length = @prop_header_size + byte_size(value) + padding_length = Openflow.Utils.padding(prop_length, 8) + <<@prop_userdata::16, prop_length::16, value::bytes, 0::size(padding_length)-unit(8)>> + prop == :pause and value == true -> + padding_length = 4 + prop_length = @prop_header_size + padding_length + <<@prop_pause::16, prop_length::16, 0::size(padding_length)-unit(8)>> + true -> + "" + end + encode_prop(<>, rest, ctl) + end + + defp decode_prop(ctl, ""), do: ctl + defp decode_prop(ctl, <> = bin) do + prop_type = Openflow.Enums.to_atom(prop_type_int, :nx_action_controller2_prop_type) + case prop_type do + :max_len -> + <<@prop_max_len::16, _prop_length::16, max_len_int::16, _::size(2)-unit(8), rest::bytes>> = bin + max_len = Openflow.Utils.get_enum(max_len_int, :controller_max_len) + decode_prop(struct(ctl, %{max_len: max_len}), rest) + :controller_id -> + <<@prop_ctl_id::16, _prop_length::16, controller_id::16, _::size(2)-unit(8), rest::bytes>> = bin + decode_prop(struct(ctl, %{controller_id: controller_id}), rest) + :reason -> + <<@prop_reason::16, _prop_length::16, reason_int::8, _::size(3)-unit(8), rest::bytes>> = bin + reason = Openflow.Utils.get_enum(reason_int, :packet_in_reason) + decode_prop(struct(ctl, %{reason: reason}), rest) + :userdata -> + <<@prop_userdata::16, prop_length::16, remains::bytes>> = bin + userdata_len = prop_length - 4 + padding_length = Openflow.Utils.padding(prop_length, 8) + <> = remains + decode_prop(struct(ctl, %{userdata: userdata}), rest) + :pause -> + <<@prop_pause::16, _::16, 0::size(4)-unit(8), rest::bytes>> = bin + decode_prop(struct(ctl, %{pause: true}), rest) + end + end +end diff --git a/lib/openflow/actions/nx_ct_clear.ex b/lib/openflow/actions/nx_ct_clear.ex new file mode 100644 index 0000000..32f0810 --- /dev/null +++ b/lib/openflow/actions/nx_ct_clear.ex @@ -0,0 +1,21 @@ +defmodule Openflow.Action.NxCtClear do + defstruct([]) + + @experimenter 0x00002320 + @nxast 43 + + alias __MODULE__ + + def new do + %NxCtClear{} + end + + def to_binary(%NxCtClear{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::16, 0::size(4)-unit(8)>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do + %NxCtClear{} + end +end diff --git a/lib/openflow/actions/nx_dec_mpls_ttl.ex b/lib/openflow/actions/nx_dec_mpls_ttl.ex new file mode 100644 index 0000000..8b2c0f1 --- /dev/null +++ b/lib/openflow/actions/nx_dec_mpls_ttl.ex @@ -0,0 +1,21 @@ +defmodule Openflow.Action.NxDecMplsTtl do + defstruct([]) + + @experimenter 0x00002320 + @nxast 26 + + alias __MODULE__ + + def new do + %NxDecMplsTtl{} + end + + def to_binary(%NxDecMplsTtl{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8)>>) do + %NxDecMplsTtl{} + end +end diff --git a/lib/openflow/actions/nx_dec_ttl.ex b/lib/openflow/actions/nx_dec_ttl.ex new file mode 100644 index 0000000..7d949b8 --- /dev/null +++ b/lib/openflow/actions/nx_dec_ttl.ex @@ -0,0 +1,21 @@ +defmodule Openflow.Action.NxDecTtl do + defstruct([]) + + @experimenter 0x00002320 + @nxast 18 + + alias __MODULE__ + + def new do + %NxDecTtl{} + end + + def to_binary(%NxDecTtl{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do + %NxDecTtl{} + end +end diff --git a/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex b/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex new file mode 100644 index 0000000..6704e75 --- /dev/null +++ b/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex @@ -0,0 +1,31 @@ +defmodule Openflow.Action.NxDecTtlCntIds do + defstruct(ids: []) + + @experimenter 0x00002320 + @nxast 21 + + alias __MODULE__ + + def new(ids) do + %NxDecTtlCntIds{ids: ids} + end + + def to_binary(%NxDecTtlCntIds{ids: ids}) do + n_controllers = length(ids) + ids_bin = Enum.join((for id <- ids, do: <>), "") + padding = Openflow.Utils.padding(n_controllers, 8) + exp_body = <<@experimenter::32, @nxast::16, n_controllers::16, + 0::size(4)-unit(8), ids_bin::bytes, 0::size(padding)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, n_controllers::16, body::bitstring>>) do + n_controllers_len = n_controllers * 16 + <<0::size(4)-unit(8), ids_bin::size(n_controllers_len)-bits, _::bitstring>> = body + ids = for <>, do: id + %NxDecTtlCntIds{ids: ids} + end +end diff --git a/lib/openflow/actions/nx_exit.ex b/lib/openflow/actions/nx_exit.ex new file mode 100644 index 0000000..842dd77 --- /dev/null +++ b/lib/openflow/actions/nx_exit.ex @@ -0,0 +1,21 @@ +defmodule Openflow.Action.NxExit do + defstruct([]) + + @experimenter 0x00002320 + @nxast 17 + + alias __MODULE__ + + def new do + %NxExit{} + end + + def to_binary(%NxExit{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::48>> + <<0xffff::16, 16::16, exp_body::bytes>> + end + + def read(<<@experimenter::32, @nxast::16, 0::48>>) do + %NxExit{} + end +end diff --git a/lib/openflow/actions/nx_fin_timeout.ex b/lib/openflow/actions/nx_fin_timeout.ex new file mode 100644 index 0000000..00a14d4 --- /dev/null +++ b/lib/openflow/actions/nx_fin_timeout.ex @@ -0,0 +1,26 @@ +defmodule Openflow.Action.NxFinTimeout do + defstruct( + idle_timeout: 0, + hard_timeout: 0 + ) + + @experimenter 0x00002320 + @nxast 19 + + alias __MODULE__ + + def new(options) do + fin_idle = Keyword.get(options, :idle_timeout, 0) + fin_hard = Keyword.get(options, :hard_timeout, 0) + %NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard} + end + + def to_binary(%NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard}) do + exp_body = <<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16>> + <<0xffff::16, 16::16, exp_body::bytes, 0::size(2)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, fin_idle::16, fin_hard::16, _::size(2)-unit(8)>>) do + %NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard} + end +end diff --git a/lib/openflow/actions/nx_flow_spec.ex b/lib/openflow/actions/nx_flow_spec.ex new file mode 100644 index 0000000..62a56b3 --- /dev/null +++ b/lib/openflow/actions/nx_flow_spec.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Action.NxFlowSpec do + + def read(flow_spec_bin) do + do_read([], flow_spec_bin) + end + + def to_binary(flow_specs) when is_list(flow_specs) do + to_binary(<<>>, flow_specs) + end + def to_binary(flow_spec) do + to_binary([flow_spec]) + end + + # private functions + + defp do_read(acc, <<>>), do: Enum.reverse(acc) + defp do_read(acc, <<0::16, _::bitstring>>), do: Enum.reverse(acc) + defp do_read(acc, <<_::2, _::1, type::2, _::bitstring>> = binary) do + codec = Openflow.Enums.to_atom(type, :nx_flow_spec_type) + {flow_spec, rest} = codec.read(binary) + do_read([flow_spec|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [flow_spec|rest]) do + codec = flow_spec.__struct__ + to_binary(<>, rest) + end +end diff --git a/lib/openflow/actions/nx_flow_spec_load.ex b/lib/openflow/actions/nx_flow_spec_load.ex new file mode 100644 index 0000000..120a90d --- /dev/null +++ b/lib/openflow/actions/nx_flow_spec_load.ex @@ -0,0 +1,83 @@ +defmodule Openflow.Action.NxFlowSpecLoad do + defstruct( + src: nil, + dst: nil, + n_bits: 0, + src_offset: 0, + dst_offset: 0 + ) + + @learn_src_field 0 + @learn_src_immediate 1 + @learn_dst 1 + + alias __MODULE__ + + def new(options) do + src = Keyword.get(options, :src) + dst = Keyword.get(options, :dst) + src_ofs = Keyword.get(options, :src_offset, 0) + dst_ofs = Keyword.get(options, :dst_offset, 0) + default_n_bits = Openflow.Match.Field.n_bits_of(dst) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + %NxFlowSpecLoad{src: src, + dst: dst, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs} + end + + def to_binary(%NxFlowSpecLoad{} = fsm) do + %NxFlowSpecLoad{ + dst: dst_field, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs + } = fsm + {src_code, src_bin} = codec_src(fsm) + dst_bin = Openflow.Match.codec_header(dst_field) + case src_code do + @learn_src_immediate -> + <<0::2, src_code::1, @learn_dst::2, n_bits::11, + src_bin::bytes, dst_bin::4-bytes, dst_ofs::16>> + @learn_src_field -> + <<0::2, src_code::1, @learn_dst::2, n_bits::11, + src_bin::4-bytes, src_ofs::16, dst_bin::4-bytes, dst_ofs::16>> + end + end + + def read(<<_::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, + src_ofs::16, dst_bin::4-bytes, dst_ofs::16, rest::bitstring>>) do + src = Openflow.Match.codec_header(src_bin) + dst = Openflow.Match.codec_header(dst_bin) + flow_spec = %NxFlowSpecLoad{src: src, + dst: dst, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs} + {flow_spec, rest} + end + def read(<<_::2, @learn_src_immediate::1, @learn_dst::2, n_bits::11, binary::bitstring>>) do + rounded_up_len = Openflow.Utils.pad_length(n_bits, 8) + rounded_up_nbits = n_bits + rounded_up_len + <> = binary + dst = Openflow.Match.codec_header(dst_bin) + src = Openflow.Match.Field.codec(src_bin, dst) + flow_spec = %NxFlowSpecLoad{src: src, + dst: dst, + n_bits: n_bits, + dst_offset: dst_ofs} + {flow_spec, rest} + end + + # private functions + + defp codec_src(%NxFlowSpecLoad{src: src_field}) when is_atom(src_field) do + src_bin = Openflow.Match.codec_header(src_field) + {@learn_src_field, src_bin} + end + defp codec_src(%NxFlowSpecLoad{src: src, dst: dst_field}) do + src_bin = Openflow.Match.Field.codec(src, dst_field) + {@learn_src_immediate, src_bin} + end +end diff --git a/lib/openflow/actions/nx_flow_spec_match.ex b/lib/openflow/actions/nx_flow_spec_match.ex new file mode 100644 index 0000000..8312fb4 --- /dev/null +++ b/lib/openflow/actions/nx_flow_spec_match.ex @@ -0,0 +1,83 @@ +defmodule Openflow.Action.NxFlowSpecMatch do + defstruct( + src: nil, + dst: nil, + n_bits: 0, + src_offset: 0, + dst_offset: 0 + ) + + @learn_src_field 0 + @learn_src_immediate 1 + @learn_dst 0 + + alias __MODULE__ + + def new(options) do + src = Keyword.get(options, :src) + dst = Keyword.get(options, :dst) + default_n_bits = Openflow.Match.Field.n_bits_of(dst) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + src_ofs = Keyword.get(options, :src_offset, 0) + dst_ofs = Keyword.get(options, :dst_offset, 0) + %NxFlowSpecMatch{src: src, + dst: dst, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs} + end + + def to_binary(%NxFlowSpecMatch{} = fsm) do + %NxFlowSpecMatch{ + dst: dst_field, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs + } = fsm + {src_code, src_bin} = codec_src(fsm) + dst_bin = Openflow.Match.codec_header(dst_field) + case src_code do + @learn_src_immediate -> + <<0::2, src_code::1, @learn_dst::2, n_bits::11, + src_bin::bytes, dst_bin::4-bytes, dst_ofs::16>> + @learn_src_field -> + <<0::2, src_code::1, @learn_dst::2, n_bits::11, + src_bin::bytes, src_ofs::16, dst_bin::4-bytes, dst_ofs::16>> + end + end + + def read(<<_::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, + src_ofs::16, dst_bin::4-bytes, dst_ofs::16, rest::bitstring>>) do + src = Openflow.Match.codec_header(src_bin) + dst = Openflow.Match.codec_header(dst_bin) + flow_spec = %NxFlowSpecMatch{src: src, + dst: dst, + n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs} + {flow_spec, rest} + end + def read(<<_::2, @learn_src_immediate::1, @learn_dst::2, n_bits::11, binary::bitstring>>) do + rounded_up_len = Openflow.Utils.pad_length(n_bits, 8) + rounded_up_nbits = n_bits + rounded_up_len + <> = binary + dst = Openflow.Match.codec_header(dst_bin) + src = Openflow.Match.Field.codec(src_bin, dst) + flow_spec = %NxFlowSpecMatch{src: src, + dst: dst, + n_bits: n_bits, + dst_offset: dst_ofs} + {flow_spec, rest} + end + + # private functions + + defp codec_src(%NxFlowSpecMatch{src: src_field}) when is_atom(src_field) do + src_bin = Openflow.Match.codec_header(src_field) + {@learn_src_field, src_bin} + end + defp codec_src(%NxFlowSpecMatch{src: src, dst: dst_field}) do + src_bin = Openflow.Match.Field.codec(src, dst_field) + {@learn_src_immediate, src_bin} + end +end diff --git a/lib/openflow/actions/nx_flow_spec_output.ex b/lib/openflow/actions/nx_flow_spec_output.ex new file mode 100644 index 0000000..ae0b18f --- /dev/null +++ b/lib/openflow/actions/nx_flow_spec_output.ex @@ -0,0 +1,38 @@ +defmodule Openflow.Action.NxFlowSpecOutput do + defstruct( + n_bits: 0, + src: nil, + src_offset: 0 + ) + + @learn_src_field 0 + @learn_dst 2 + + alias __MODULE__ + + def new(options) do + src = Keyword.get(options, :src) + src_ofs = Keyword.get(options, :src_offset, 0) + default_n_bits = Openflow.Match.Field.n_bits_of(src) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + %NxFlowSpecOutput{n_bits: n_bits, + src: src, + src_offset: src_ofs} + end + + def to_binary(%NxFlowSpecOutput{n_bits: n_bits, + src: src, + src_offset: src_ofs}) do + src_bin = Openflow.Match.codec_header(src) + <<0::2, @learn_src_field::1, @learn_dst::2, n_bits::11, src_bin::4-bytes, src_ofs::16>> + end + + def read(<<0::2, @learn_src_field::1, @learn_dst::2, + n_bits::11, src_bin::4-bytes, src_ofs::16, rest::bitstring>>) do + src = Openflow.Match.codec_header(src_bin) + flow_spec = %NxFlowSpecOutput{n_bits: n_bits, + src: src, + src_offset: src_ofs} + {flow_spec, rest} + end +end diff --git a/lib/openflow/actions/nx_learn.ex b/lib/openflow/actions/nx_learn.ex new file mode 100644 index 0000000..df6932f --- /dev/null +++ b/lib/openflow/actions/nx_learn.ex @@ -0,0 +1,78 @@ +defmodule Openflow.Action.NxLearn do + defstruct( + idle_timeout: 0, + hard_timeout: 0, + priority: 0, + cookie: 0, + flags: [], + table_id: 0, + fin_idle_timeout: 0, + fin_hard_timeout: 0, + flow_specs: [] + ) + + @experimenter 0x00002320 + @nxast 16 + + alias __MODULE__ + + def new(options) do + idle = Keyword.get(options, :idle_timeout, 0) + hard = Keyword.get(options, :hard_timeout, 0) + prio = Keyword.get(options, :priority, 0) + cookie = Keyword.get(options, :cookie, 0) + flags = Keyword.get(options, :flags, []) + table_id = Keyword.get(options, :table_id, 0) + fin_idle = Keyword.get(options, :fin_idle_timeout, 0) + fin_hard = Keyword.get(options, :fin_hard_timeout, 0) + flow_specs = Keyword.get(options, :flow_specs, []) + %NxLearn{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + flow_specs: flow_specs} + end + + def to_binary(%NxLearn{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + flow_specs: flow_specs}) do + flags_int = Openflow.Enums.flags_to_int(flags, :nx_learn_flag) + flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(flow_specs) + exp_body = <<@experimenter::32, @nxast::16, idle::16, hard::16, + prio::16, cookie::64, flags_int::16, table_id::8, + 0::size(1)-unit(8), fin_idle::16, fin_hard::16, + flow_specs_bin::bitstring>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bitstring>>) do + <> = body + flags = Openflow.Enums.int_to_flags(flags_int, :nx_learn_flag) + flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin) + %NxLearn{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + flow_specs: flow_specs} + end +end diff --git a/lib/openflow/actions/nx_learn2.ex b/lib/openflow/actions/nx_learn2.ex new file mode 100644 index 0000000..4a47ef5 --- /dev/null +++ b/lib/openflow/actions/nx_learn2.ex @@ -0,0 +1,106 @@ +defmodule Openflow.Action.NxLearn2 do + defstruct( + idle_timeout: 0, + hard_timeout: 0, + priority: 0, + cookie: 0, + flags: [], + table_id: 0, + fin_idle_timeout: 0, + fin_hard_timeout: 0, + limit: 0, + result_dst_offset: 0, + result_dst: nil, + flow_specs: [] + ) + + @experimenter 0x00002320 + @nxast 45 + + alias __MODULE__ + + def new(options) do + idle = Keyword.get(options, :idle_timeout, 0) + hard = Keyword.get(options, :hard_timeout, 0) + prio = Keyword.get(options, :priority, 0) + cookie = Keyword.get(options, :cookie, 0) + flags = Keyword.get(options, :flags, []) + table_id = Keyword.get(options, :table_id, 0) + fin_idle = Keyword.get(options, :fin_idle_timeout, 0) + fin_hard = Keyword.get(options, :fin_hard_timeout, 0) + flow_specs = Keyword.get(options, :flow_specs, []) + limit = Keyword.get(options, :limit, 0) + result_dst_offset = Keyword.get(options, :result_dst_offset, 0) + result_dst = Keyword.get(options, :result_dst) + %NxLearn2{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + limit: limit, + result_dst_offset: result_dst_offset, + result_dst: result_dst, + flow_specs: flow_specs} + end + + def to_binary(%NxLearn2{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + limit: limit, + result_dst_offset: result_dst_ofs, + result_dst: result_dst, + flow_specs: flow_specs}) do + flags_int = Openflow.Enums.flags_to_int(flags, :nx_learn_flag) + result_dst_bin = if :write_result in flags do + Openflow.Match.codec_header(result_dst) + else + "" + end + flow_specs_bin = Openflow.Action.NxFlowSpec.to_binary(flow_specs) + exp_body = <<@experimenter::32, @nxast::16, idle::16, hard::16, + prio::16, cookie::64, flags_int::16, table_id::8, + 0::size(1)-unit(8), fin_idle::16, fin_hard::16, + limit::32, result_dst_ofs::16, 0::size(2)-unit(8), + result_dst_bin::bytes, flow_specs_bin::bitstring>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + flags = Openflow.Enums.int_to_flags(flags_int, :nx_learn_flag) + learn = %NxLearn2{idle_timeout: idle, + hard_timeout: hard, + priority: prio, + cookie: cookie, + flags: flags, + table_id: table_id, + fin_idle_timeout: fin_idle, + fin_hard_timeout: fin_hard, + limit: limit, + result_dst_offset: result_dst_ofs} + if :write_result in flags do + header_size = Openflow.Match.header_size(rest) + <> = rest + result_dst = Openflow.Match.codec_header(result_dst_bin) + flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin) + struct(learn, %{result_dst: result_dst, flow_specs: flow_specs}) + else + <> = rest + flow_specs = Openflow.Action.NxFlowSpec.read(flow_specs_bin) + struct(learn, %{flow_specs: flow_specs}) + end + end +end diff --git a/lib/openflow/actions/nx_multipath.ex b/lib/openflow/actions/nx_multipath.ex new file mode 100644 index 0000000..86c45c9 --- /dev/null +++ b/lib/openflow/actions/nx_multipath.ex @@ -0,0 +1,78 @@ +defmodule Openflow.Action.NxMultipath do + import Bitwise + + defstruct( + hash_field: :eth_src, + basis: 0, + algorithm: :modulo_n, + max_link: 0, + argument: 0, + offset: 0, + n_bits: 0, + dst_field: nil + ) + + @experimenter 0x00002320 + @nxast 10 + + alias __MODULE__ + + def new(options) do + hash_field = Keyword.get(options, :hash_field, :eth_src) + basis = Keyword.get(options, :basis, 0) + alg = Keyword.get(options, :algorithm, :modulo_n) + max_link = Keyword.get(options, :max_link, 0) + arg = Keyword.get(options, :argument, 0) + dst_field = Keyword.get(options, :dst_field) + default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + %NxMultipath{hash_field: hash_field, + basis: basis, + algorithm: alg, + max_link: max_link, + offset: ofs, + n_bits: n_bits, + argument: arg, + dst_field: dst_field} + end + + def to_binary(%NxMultipath{hash_field: hash_field, + basis: basis, + algorithm: alg, + max_link: max_link, + argument: arg, + offset: ofs, + n_bits: n_bits, + dst_field: dst_field}) do + hash_field_int = Openflow.Enums.to_int(hash_field, :nx_hash_fields) + alg_int = Openflow.Enums.to_int(alg, :nx_mp_algorithm) + dst_field_bin = Openflow.Match.codec_header(dst_field) + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + hash_field = Openflow.Enums.to_atom(hash_field_int, :nx_hash_fields) + alg = Openflow.Enums.to_atom(alg_int, :nx_mp_algorithm) + dst_field = Openflow.Match.codec_header(dst_field_bin) + %NxMultipath{hash_field: hash_field, + basis: basis, + algorithm: alg, + max_link: max_link, + argument: arg, + offset: ofs, + n_bits: n_bits + 1, + dst_field: dst_field} + end +end diff --git a/lib/openflow/actions/nx_nat.ex b/lib/openflow/actions/nx_nat.ex new file mode 100644 index 0000000..a8af26d --- /dev/null +++ b/lib/openflow/actions/nx_nat.ex @@ -0,0 +1,121 @@ +defmodule Openflow.Action.NxNat do + defstruct( + flags: [], + ipv4_min: nil, + ipv4_max: nil, + ipv6_min: nil, + ipv6_max: nil, + proto_min: nil, + proto_max: nil + ) + + @experimenter 0x00002320 + @nxast 36 + + alias __MODULE__ + + def new(options \\ []) do + flags = Keyword.get(options, :flags, []) + ipv4_min = Keyword.get(options, :ipv4_min) + ipv4_max = Keyword.get(options, :ipv4_max) + ipv6_min = Keyword.get(options, :ipv6_min) + ipv6_max = Keyword.get(options, :ipv6_max) + proto_min = Keyword.get(options, :proto_min) + proto_max = Keyword.get(options, :proto_max) + %NxNat{flags: flags, + ipv4_min: ipv4_min, + ipv4_max: ipv4_max, + ipv6_min: ipv6_min, + ipv6_max: ipv6_max, + proto_min: proto_min, + proto_max: proto_max} + end + + def to_binary(%NxNat{flags: flags} = nat) do + flags_int = Openflow.Enums.flags_to_int(flags, :nx_nat_flags) + range_flags = + nat + |> get_ranges + |> Openflow.Enums.flags_to_int(:nx_nat_range) + |> Openflow.Enums.int_to_flags(:nx_nat_range) + ranges_bin = encode_ranges("", range_flags, nat) + range_flags_int = Openflow.Enums.flags_to_int(range_flags, :nx_nat_range) + exp_body = <<@experimenter::32, @nxast::16, 0::size(2)-unit(8), + flags_int::16, range_flags_int::16, ranges_bin::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <<0::size(2)-unit(8), flags_int::16, range_flags_int::16, ranges_bin::bytes>> = body + flags = Openflow.Enums.int_to_flags(flags_int, :nx_nat_flags) + range_flags = Openflow.Enums.int_to_flags(range_flags_int, :nx_nat_range) + decode_ranges(%NxNat{flags: flags}, range_flags, ranges_bin) + end + + # private functions + + defp get_ranges(nat) do + nat + |> Map.from_struct + |> Map.delete(:flags) + |> Enum.map(fn({k, v}) -> if(not is_nil(v), do: k, else: nil) end) + |> Enum.filter(fn(v) -> not is_nil(v) end) + end + + defp encode_ranges(acc, [], _nat), do: acc + defp encode_ranges(acc, [range|rest], nat) do + cond do + range == :ipv4_min or range == :ipv4_max -> + case Map.get(nat, range) do + {a1, a2, a3, a4} -> + encode_ranges(<>, rest, nat) + "" -> + encode_ranges(<>, rest, nat) + end + range == :ipv6_min or range == :ipv6_max -> + case Map.get(nat, range) do + {a1, a2, a3, a4, a5, a6, a7, a8} -> + encode_ranges(<>, rest, nat) + "" -> + encode_ranges(<>, rest, nat) + end + range == :proto_min or range == :proto_max -> + case Map.get(nat, range) do + proto when is_integer(proto) and proto in (1..0xffff) -> + encode_ranges(<>, rest, nat) + _ -> + encode_ranges(<>, rest, nat) + end + end + end + + defp decode_ranges(nat, [], _), do: nat + defp decode_ranges(nat, [range|ranges], bin) do + cond do + range == :ipv4_min or range == :ipv4_max -> + case bin do + <> -> + decode_ranges(struct(nat, %{range => {a1, a2, a3, a4}}), ranges, rest) + rest -> + decode_ranges(struct(nat, %{range => ""}), ranges, rest) + end + range == :ipv6_min or range == :ipv6_max -> + case bin do + <> -> + decode_ranges(struct(nat, %{range => {a1, a2, a3, a4, a5, a6, a7, a8}}), ranges, rest) + rest -> + decode_ranges(struct(nat, %{range => ""}), ranges, rest) + end + range == :proto_min or range == :proto_max -> + case bin do + <> when proto in (1..0xffff) -> + decode_ranges(struct(nat, %{range => proto}), ranges, rest) + rest -> + decode_ranges(struct(nat, %{range => ""}), ranges, rest) + end + end + end +end diff --git a/lib/openflow/actions/nx_note.ex b/lib/openflow/actions/nx_note.ex new file mode 100644 index 0000000..2e281fe --- /dev/null +++ b/lib/openflow/actions/nx_note.ex @@ -0,0 +1,26 @@ +defmodule Openflow.Action.NxNote do + defstruct(note: "") + + @experimenter 0x00002320 + @nxast 8 + + alias __MODULE__ + + def new(note) do + %NxNote{note: note} + end + + def to_binary(%NxNote{note: note}) do + padding = Openflow.Utils.padding(byte_size(note) + 2, 8) + exp_body = <<@experimenter::32, @nxast::16, note::bytes, 0::size(padding)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, note_bin::bytes>>) do + note = Openflow.Utils.decode_string(note_bin) + %NxNote{note: note} + end +end diff --git a/lib/openflow/actions/nx_output_reg.ex b/lib/openflow/actions/nx_output_reg.ex new file mode 100644 index 0000000..f94c130 --- /dev/null +++ b/lib/openflow/actions/nx_output_reg.ex @@ -0,0 +1,52 @@ +defmodule Openflow.Action.NxOutputReg do + import Bitwise + + defstruct( + n_bits: 0, + offset: 0, + src_field: nil, + max_len: :no_buffer + ) + + @experimenter 0x00002320 + @nxast 15 + + alias __MODULE__ + + def new(options) do + src_field = Keyword.get(options, :src_field) + default_n_bits = Openflow.Match.Field.n_bits_of(src_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + max_len = Keyword.get(options, :max_len, :no_buffer) + %NxOutputReg{n_bits: n_bits, + offset: ofs, + src_field: src_field, + max_len: max_len} + end + + def to_binary(%NxOutputReg{n_bits: n_bits, + offset: ofs, + src_field: src_field, + max_len: max_len}) do + src_field_bin = Openflow.Match.codec_header(src_field) + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + src_field = Openflow.Match.codec_header(src_field_bin) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + %NxOutputReg{n_bits: n_bits + 1, + offset: ofs, + src_field: src_field, + max_len: max_len} + end +end diff --git a/lib/openflow/actions/nx_output_reg2.ex b/lib/openflow/actions/nx_output_reg2.ex new file mode 100644 index 0000000..d5c88c7 --- /dev/null +++ b/lib/openflow/actions/nx_output_reg2.ex @@ -0,0 +1,51 @@ +defmodule Openflow.Action.NxOutputReg2 do + import Bitwise + + defstruct( + n_bits: 0, + offset: 0, + src_field: nil, + max_len: :no_buffer + ) + + @experimenter 0x00002320 + @nxast 32 + + alias __MODULE__ + + def new(options) do + src_field = Keyword.get(options, :src_field) + default_n_bits = Openflow.Match.Field.n_bits_of(src_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + max_len = Keyword.get(options, :max_len, :no_buffer) + %NxOutputReg2{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len} + end + + def to_binary(%NxOutputReg2{n_bits: n_bits, offset: ofs, src_field: src_field, max_len: max_len}) do + src_field_bin = Openflow.Match.codec_header(src_field) + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + padding = Openflow.Utils.padding(byte_size(src_field_bin), 10) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + exp_size = match_header_size(oxm_header) - 4 + <> = rest + src_field = Openflow.Match.codec_header(<>) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + %NxOutputReg2{n_bits: n_bits + 1, offset: ofs, src_field: src_field, max_len: max_len} + end + + # private functions + + defp match_header_size(<<0xffff::16, _::bytes>>), do: 8 + defp match_header_size(<<_::16, _::bytes>>), do: 4 +end diff --git a/lib/openflow/actions/nx_output_trunc.ex b/lib/openflow/actions/nx_output_trunc.ex new file mode 100644 index 0000000..d8289b2 --- /dev/null +++ b/lib/openflow/actions/nx_output_trunc.ex @@ -0,0 +1,31 @@ +defmodule Openflow.Action.NxOutputTrunc do + defstruct( + port_number: 0, + max_len: :no_buffer + ) + + @experimenter 0x00002320 + @nxast 39 + + alias __MODULE__ + + def new(options) do + port_no = Keyword.get(options, :port_number) + max_len = Keyword.get(options, :max_len) + %NxOutputTrunc{port_number: port_no, max_len: max_len} + end + + def to_binary(%NxOutputTrunc{port_number: port_no, max_len: max_len}) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow10_port_no) + exp_body = <<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, port_no_int::16, max_len::32>>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow10_port_no) + %NxOutputTrunc{port_number: port_no, max_len: max_len} + end +end diff --git a/lib/openflow/actions/nx_pop_mpls.ex b/lib/openflow/actions/nx_pop_mpls.ex new file mode 100644 index 0000000..7aef5b0 --- /dev/null +++ b/lib/openflow/actions/nx_pop_mpls.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxPopMpls do + defstruct(ethertype: 0x8847) + + @experimenter 0x00002320 + @nxast 24 + + alias __MODULE__ + + def new(ethertype \\ 0x8847) do + %NxPopMpls{ethertype: ethertype} + end + + def to_binary(%NxPopMpls{ethertype: ethertype}) do + exp_body = <<@experimenter::32, @nxast::16, ethertype::16, 0::size(4)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ethertype::16, _::size(4)-unit(8)>>) do + %NxPopMpls{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/nx_pop_queue.ex b/lib/openflow/actions/nx_pop_queue.ex new file mode 100644 index 0000000..bda8339 --- /dev/null +++ b/lib/openflow/actions/nx_pop_queue.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxPopQueue do + defstruct([]) + + @experimenter 0x00002320 + @nxast 5 + + alias __MODULE__ + + def new do + %NxPopQueue{} + end + + def to_binary(%NxPopQueue{}) do + exp_body = <<@experimenter::32, @nxast::16, 0::48>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>>) do + %NxPopQueue{} + end +end diff --git a/lib/openflow/actions/nx_push_mpls.ex b/lib/openflow/actions/nx_push_mpls.ex new file mode 100644 index 0000000..241d6b3 --- /dev/null +++ b/lib/openflow/actions/nx_push_mpls.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxPushMpls do + defstruct(ethertype: 0x8847) + + @experimenter 0x00002320 + @nxast 23 + + alias __MODULE__ + + def new(ethertype \\ 0x8847) do + %NxPushMpls{ethertype: ethertype} + end + + def to_binary(%NxPushMpls{ethertype: ethertype}) do + exp_body = <<@experimenter::32, @nxast::16, ethertype::16, 0::size(4)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ethertype::16, _::size(4)-unit(8)>>) do + %NxPushMpls{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/nx_reg_load.ex b/lib/openflow/actions/nx_reg_load.ex new file mode 100644 index 0000000..ae5a6ac --- /dev/null +++ b/lib/openflow/actions/nx_reg_load.ex @@ -0,0 +1,54 @@ +defmodule Openflow.Action.NxRegLoad do + import Bitwise + + defstruct( + n_bits: 0, + offset: 0, + dst_field: nil, + value: nil + ) + + @experimenter 0x00002320 + @nxast 7 + + alias __MODULE__ + + def new(options) do + dst_field = Keyword.get(options, :dst_field) + default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + value = Keyword.get(options, :value) + %NxRegLoad{n_bits: n_bits, + offset: ofs, + dst_field: dst_field, + value: value} + end + + def to_binary(%NxRegLoad{n_bits: n_bits, + offset: ofs, + dst_field: dst_field, + value: value}) do + dst_field_bin = Openflow.Match.codec_header(dst_field) + value_bin0 = Openflow.Match.Field.codec(value, dst_field) + tmp_value = :binary.decode_unsigned(value_bin0, :big) + value_bin = <> + ofs_nbits = (ofs <<< 6) ||| (n_bits - 1) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + dst_field = Openflow.Match.codec_header(dst_field_bin) + value = Openflow.Match.Field.codec(value_bin, dst_field) + %NxRegLoad{n_bits: n_bits + 1, + offset: ofs, + dst_field: dst_field, + value: value} + end +end diff --git a/lib/openflow/actions/nx_reg_load2.ex b/lib/openflow/actions/nx_reg_load2.ex new file mode 100644 index 0000000..00d4cf6 --- /dev/null +++ b/lib/openflow/actions/nx_reg_load2.ex @@ -0,0 +1,40 @@ +defmodule Openflow.Action.NxRegLoad2 do + defstruct( + dst_field: nil, + value: nil + ) + + @experimenter 0x00002320 + @nxast 33 + + alias __MODULE__ + + def new(options) do + dst_field = Keyword.get(options, :dst_field) + value = Keyword.get(options, :value) + %NxRegLoad2{dst_field: dst_field, value: value} + end + + def to_binary(%NxRegLoad2{dst_field: dst_field, value: value}) do + match_bin = + [{dst_field, value}] + |> Openflow.Match.new + |> Openflow.Match.to_binary + <<1::16, _length::16, padded_field::bytes>> = match_bin + patial_len = 4 + 4 + 2 + 6 + byte_size(padded_field) + padding = Openflow.Utils.padding(patial_len, 8) + exp_body = <<@experimenter::32, @nxast::16, 0::48, padded_field::bytes, 0::size(padding)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, _::48, match_field_bin::bytes>>) do + <<_class::16, _field::7, _hm::1, flen::8, _rest::bytes>> = match_field_bin + match_len = 4 + 4 + flen + match_bin = <<1::16, match_len::16, match_field_bin::bytes, 0::size(4)-unit(8)>> + {[{dst_field, value}|_], _rest} = Openflow.Match.read(match_bin) + %NxRegLoad2{dst_field: dst_field, value: value} + end +end diff --git a/lib/openflow/actions/nx_reg_move.ex b/lib/openflow/actions/nx_reg_move.ex new file mode 100644 index 0000000..f50a3f7 --- /dev/null +++ b/lib/openflow/actions/nx_reg_move.ex @@ -0,0 +1,56 @@ +defmodule Openflow.Action.NxRegMove do + defstruct( + n_bits: 0, + src_offset: 0, + dst_offset: 0, + src_field: nil, + dst_field: nil + ) + + @experimenter 0x00002320 + @nxast 6 + + alias __MODULE__ + + def new(options) do + src_field = Keyword.get(options, :src_field) + dst_field = Keyword.get(options, :dst_field) + default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + src_ofs = Keyword.get(options, :src_offset, 0) + dst_ofs = Keyword.get(options, :dst_offset, 0) + %NxRegMove{n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs, + src_field: src_field, + dst_field: dst_field} + end + + def to_binary(%NxRegMove{n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs, + src_field: src_field, + dst_field: dst_field}) do + src_field_bin = Openflow.Match.codec_header(src_field) + dst_field_bin = Openflow.Match.codec_header(dst_field) + body = <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, body::bytes>>) do + <> = body + src_field = Openflow.Match.codec_header(src_field_bin) + dst_field = Openflow.Match.codec_header(dst_field_bin) + %NxRegMove{n_bits: n_bits, + src_offset: src_ofs, + dst_offset: dst_ofs, + src_field: src_field, + dst_field: dst_field} + end +end diff --git a/lib/openflow/actions/nx_resubmit.ex b/lib/openflow/actions/nx_resubmit.ex new file mode 100644 index 0000000..4167a19 --- /dev/null +++ b/lib/openflow/actions/nx_resubmit.ex @@ -0,0 +1,26 @@ +defmodule Openflow.Action.NxResubmit do + defstruct([in_port: :in_port]) + + @experimenter 0x00002320 + @nxast 1 + + alias __MODULE__ + + def new(in_port \\ :in_port) do + %NxResubmit{in_port: in_port} + end + + def to_binary(%NxResubmit{in_port: in_port}) do + in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no) + exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, 0::size(4)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, in_port_int::16, _::size(4)-unit(8)>>) do + in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no) + %NxResubmit{in_port: in_port} + end +end diff --git a/lib/openflow/actions/nx_resubmit_table.ex b/lib/openflow/actions/nx_resubmit_table.ex new file mode 100644 index 0000000..663ad28 --- /dev/null +++ b/lib/openflow/actions/nx_resubmit_table.ex @@ -0,0 +1,33 @@ +defmodule Openflow.Action.NxResubmitTable do + defstruct([in_port: :in_port, table_id: :all]) + + @experimenter 0x00002320 + @nxast 14 + + alias __MODULE__ + + def new(table_id) when is_atom(table_id) or is_integer(table_id) do + new(table_id: table_id) + end + def new(options) do + in_port = Keyword.get(options, :in_port, :in_port) + table_id = Keyword.get(options, :table_id, :all) + %NxResubmitTable{in_port: in_port, table_id: table_id} + end + + def to_binary(%NxResubmitTable{in_port: in_port, table_id: table_id}) do + in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no) + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::24>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::size(3)-unit(8)>>) do + in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + %NxResubmitTable{in_port: in_port, table_id: table_id} + end +end diff --git a/lib/openflow/actions/nx_resubmit_table_ct.ex b/lib/openflow/actions/nx_resubmit_table_ct.ex new file mode 100644 index 0000000..01398e1 --- /dev/null +++ b/lib/openflow/actions/nx_resubmit_table_ct.ex @@ -0,0 +1,30 @@ +defmodule Openflow.Action.NxResubmitTableCt do + defstruct(in_port: :in_port, table_id: :all) + + @experimenter 0x00002320 + @nxast 44 + + alias __MODULE__ + + def new(options) do + in_port = Keyword.get(options, :in_port, :in_port) + table_id = Keyword.get(options, :table_id, :all) + %NxResubmitTableCt{in_port: in_port, table_id: table_id} + end + + def to_binary(%NxResubmitTableCt{in_port: in_port, table_id: table_id}) do + in_port_int = Openflow.Utils.get_enum(in_port, :openflow10_port_no) + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + exp_body = <<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::24>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> +end + + def read(<<@experimenter::32, @nxast::16, in_port_int::16, table_id_int::8, 0::size(3)-unit(8)>>) do + in_port = Openflow.Utils.get_enum(in_port_int, :openflow10_port_no) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + %NxResubmitTableCt{in_port: in_port, table_id: table_id} + end +end diff --git a/lib/openflow/actions/nx_sample.ex b/lib/openflow/actions/nx_sample.ex new file mode 100644 index 0000000..b133e38 --- /dev/null +++ b/lib/openflow/actions/nx_sample.ex @@ -0,0 +1,44 @@ +defmodule Openflow.Action.NxSample do + defstruct( + probability: 0, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0 + ) + + @experimenter 0x00002320 + @nxast 29 + + alias __MODULE__ + + def new(options) do + probability = Keyword.get(options, :probability, 0) + collector_set_id = Keyword.get(options, :collector_set_id, 0) + obs_domain_id = Keyword.get(options, :obs_domain_id, 0) + obs_point_id = Keyword.get(options, :obs_point_id, 0) + %NxSample{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id} + end + + def to_binary(%NxSample{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id}) do + exp_body = <<@experimenter::32, @nxast::16, probability::16, + collector_set_id::32, obs_domain_id::32, obs_point_id::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, probability::16, + collector_set_id::32, obs_domain_id::32, obs_point_id::32>>) do + %NxSample{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id} + end +end diff --git a/lib/openflow/actions/nx_sample2.ex b/lib/openflow/actions/nx_sample2.ex new file mode 100644 index 0000000..fd15c5b --- /dev/null +++ b/lib/openflow/actions/nx_sample2.ex @@ -0,0 +1,49 @@ +defmodule Openflow.Action.NxSample2 do + defstruct( + probability: 0, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0, + sampling_port: 0 + ) + + @experimenter 0x00002320 + @nxast 38 + + alias __MODULE__ + + def new(options) do + probability = Keyword.get(options, :probability, 0) + collector_set_id = Keyword.get(options, :collector_set_id, 0) + obs_domain_id = Keyword.get(options, :obs_domain_id, 0) + obs_point_id = Keyword.get(options, :obs_point_id, 0) + sampling_port = Keyword.get(options, :sampling_port, 0) + %NxSample2{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port} + end + + def to_binary(%NxSample2{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port}) do + exp_body = <<@experimenter::32, @nxast::16, probability::16, + collector_set_id::32, obs_domain_id::32, obs_point_id::32, sampling_port::16, 0::size(6)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, + obs_domain_id::32, obs_point_id::32, sampling_port::16, 0::size(6)-unit(8)>>) do + %NxSample2{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port} + end +end diff --git a/lib/openflow/actions/nx_sample3.ex b/lib/openflow/actions/nx_sample3.ex new file mode 100644 index 0000000..f7e10ac --- /dev/null +++ b/lib/openflow/actions/nx_sample3.ex @@ -0,0 +1,56 @@ +defmodule Openflow.Action.NxSample3 do + defstruct( + probability: 0, + collector_set_id: 0, + obs_domain_id: 0, + obs_point_id: 0, + sampling_port: 0, + direction: :default + ) + + @experimenter 0x00002320 + @nxast 41 + + alias __MODULE__ + + def new(options) do + probability = Keyword.get(options, :probability, 0) + collector_set_id = Keyword.get(options, :collector_set_id, 0) + obs_domain_id = Keyword.get(options, :obs_domain_id, 0) + obs_point_id = Keyword.get(options, :obs_point_id, 0) + sampling_port = Keyword.get(options, :sampling_port, 0) + direction = Keyword.get(options, :direction, :default) + %NxSample3{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port, + direction: direction} + end + + def to_binary(%NxSample3{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port, + direction: direction}) do + direction_int = Openflow.Enums.to_int(direction, :nx_action_sample_direction) + exp_body = <<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, + obs_domain_id::32, obs_point_id::32, sampling_port::16, direction_int::8, 0::size(5)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, probability::16, collector_set_id::32, + obs_domain_id::32, obs_point_id::32, sampling_port::16, direction_int::8, 0::size(5)-unit(8)>>) do + direction = Openflow.Enums.to_atom(direction_int, :nx_action_sample_direction) + %NxSample3{probability: probability, + collector_set_id: collector_set_id, + obs_domain_id: obs_domain_id, + obs_point_id: obs_point_id, + sampling_port: sampling_port, + direction: direction} + end +end diff --git a/lib/openflow/actions/nx_set_mpls_label.ex b/lib/openflow/actions/nx_set_mpls_label.ex new file mode 100644 index 0000000..99436da --- /dev/null +++ b/lib/openflow/actions/nx_set_mpls_label.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetMplsLabel do + defstruct(label: 0) + + @experimenter 0x00002320 + @nxast 30 + + alias __MODULE__ + + def new(label) do + %NxSetMplsLabel{label: label} + end + + def to_binary(%NxSetMplsLabel{label: label}) do + exp_body = <<@experimenter::32, @nxast::16, 0::16, label::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, _::16, label::32>>) do + %NxSetMplsLabel{label: label} + end +end diff --git a/lib/openflow/actions/nx_set_mpls_tc.ex b/lib/openflow/actions/nx_set_mpls_tc.ex new file mode 100644 index 0000000..16414f2 --- /dev/null +++ b/lib/openflow/actions/nx_set_mpls_tc.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetMplsTc do + defstruct(tc: 0) + + @experimenter 0x00002320 + @nxast 31 + + alias __MODULE__ + + def new(tc) do + %NxSetMplsTc{tc: tc} + end + + def to_binary(%NxSetMplsTc{tc: tc}) do + exp_body = <<@experimenter::32, @nxast::16, tc::8, 0::size(5)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, tc::8, _::size(5)-unit(8)>>) do + %NxSetMplsTc{tc: tc} + end +end diff --git a/lib/openflow/actions/nx_set_mpls_ttl.ex b/lib/openflow/actions/nx_set_mpls_ttl.ex new file mode 100644 index 0000000..929398f --- /dev/null +++ b/lib/openflow/actions/nx_set_mpls_ttl.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetMplsTtl do + defstruct(ttl: 0) + + @experimenter 0x00002320 + @nxast 25 + + alias __MODULE__ + + def new(ttl) do + %NxSetMplsTtl{ttl: ttl} + end + + def to_binary(%NxSetMplsTtl{ttl: ttl}) do + exp_body = <<@experimenter::32, @nxast::16, ttl::8, 0::size(5)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ttl::8, _::size(5)-unit(8)>>) do + %NxSetMplsTtl{ttl: ttl} + end +end diff --git a/lib/openflow/actions/nx_set_queue.ex b/lib/openflow/actions/nx_set_queue.ex new file mode 100644 index 0000000..d133276 --- /dev/null +++ b/lib/openflow/actions/nx_set_queue.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetQueue do + defstruct([queue_id: 0]) + + @experimenter 0x00002320 + @nxast 4 + + alias __MODULE__ + + def new(queue_id) do + %NxSetQueue{queue_id: queue_id} + end + + def to_binary(%NxSetQueue{queue_id: queue_id}) do + exp_body = <<@experimenter::32, @nxast::16, 0::16, queue_id::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(2)-unit(8), queue_id::32>>) do + %NxSetQueue{queue_id: queue_id} + end +end diff --git a/lib/openflow/actions/nx_set_tunnel.ex b/lib/openflow/actions/nx_set_tunnel.ex new file mode 100644 index 0000000..0ed3d14 --- /dev/null +++ b/lib/openflow/actions/nx_set_tunnel.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetTunnel do + defstruct(tunnel_id: 0) + + @experimenter 0x00002320 + @nxast 2 + + alias __MODULE__ + + def new(tunnel_id) do + %NxSetTunnel{tunnel_id: tunnel_id} + end + + def to_binary(%NxSetTunnel{tunnel_id: tunnel_id}) do + exp_body = <<@experimenter::32, @nxast::16, 0::16, tunnel_id::32>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, _::16, tunnel_id::32>>) do + %NxSetTunnel{tunnel_id: tunnel_id} + end +end diff --git a/lib/openflow/actions/nx_set_tunnel64.ex b/lib/openflow/actions/nx_set_tunnel64.ex new file mode 100644 index 0000000..c70b230 --- /dev/null +++ b/lib/openflow/actions/nx_set_tunnel64.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Action.NxSetTunnel64 do + defstruct([tunnel_id: 0]) + + @experimenter 0x00002320 + @nxast 9 + + alias __MODULE__ + + def new(tunnel_id) do + %NxSetTunnel64{tunnel_id: tunnel_id} + end + + def to_binary(%NxSetTunnel64{tunnel_id: tunnel_id}) do + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), tunnel_id::64>>) do + %NxSetTunnel64{tunnel_id: tunnel_id} + end +end diff --git a/lib/openflow/actions/nx_stack_pop.ex b/lib/openflow/actions/nx_stack_pop.ex new file mode 100644 index 0000000..c106678 --- /dev/null +++ b/lib/openflow/actions/nx_stack_pop.ex @@ -0,0 +1,36 @@ +defmodule Openflow.Action.NxStackPop do + defstruct( + n_bits: 0, + offset: 0, + field: nil + ) + + @experimenter 0x00002320 + @nxast 28 + + alias __MODULE__ + + def new(options) do + field = Keyword.get(options, :field) + default_n_bits = Openflow.Match.Field.n_bits_of(field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + %NxStackPop{n_bits: n_bits, offset: ofs, field: field} + end + + def to_binary(%NxStackPop{n_bits: n_bits, offset: ofs, field: field}) do + field_bin = Openflow.Match.codec_header(field) + exp_body = <<@experimenter::32, @nxast::16, ofs::16, + field_bin::4-bytes, n_bits::16, 0::size(6)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ofs::16, + field_bin::4-bytes, n_bits::16, _::size(6)-unit(8)>>) do + field = Openflow.Match.codec_header(field_bin) + %NxStackPop{n_bits: n_bits, offset: ofs, field: field} + end +end diff --git a/lib/openflow/actions/nx_stack_push.ex b/lib/openflow/actions/nx_stack_push.ex new file mode 100644 index 0000000..21dc9c0 --- /dev/null +++ b/lib/openflow/actions/nx_stack_push.ex @@ -0,0 +1,36 @@ +defmodule Openflow.Action.NxStackPush do + defstruct( + n_bits: 0, + offset: 0, + field: nil + ) + + @experimenter 0x00002320 + @nxast 27 + + alias __MODULE__ + + def new(options) do + field = Keyword.get(options, :field) + default_n_bits = Openflow.Match.Field.n_bits_of(field) + n_bits = Keyword.get(options, :n_bits, default_n_bits) + ofs = Keyword.get(options, :offset, 0) + %NxStackPush{n_bits: n_bits, offset: ofs, field: field} + end + + def to_binary(%NxStackPush{n_bits: n_bits, offset: ofs, field: field}) do + field_bin = Openflow.Match.codec_header(field) + exp_body = <<@experimenter::32, @nxast::16, ofs::16, + field_bin::4-bytes, n_bits::16, 0::size(6)-unit(8)>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, ofs::16, + field_bin::4-bytes, n_bits::16, _::size(6)-unit(8)>>) do + field = Openflow.Match.codec_header(field_bin) + %NxStackPush{n_bits: n_bits, offset: ofs, field: field} + end +end diff --git a/lib/openflow/actions/nx_write_metadata.ex b/lib/openflow/actions/nx_write_metadata.ex new file mode 100644 index 0000000..5450257 --- /dev/null +++ b/lib/openflow/actions/nx_write_metadata.ex @@ -0,0 +1,32 @@ +defmodule Openflow.Action.NxWriteMetadata do + defstruct( + metadata: 0, + metadata_mask: 0xffffffffffffffff + ) + + @experimenter 0x00002320 + @nxast 22 + + alias __MODULE__ + + def new(metadata) when is_integer(metadata) do + new(metadata: metadata) + end + def new(options) when is_list(options) do + metadata = Keyword.get(options, :metadata, 0) + metadata_mask = Keyword.get(options, :metadata_mask, 0xffffffffffffffff) + %NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask} + end + + def to_binary(%NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask}) do + exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8), metadata::64, metadata_mask::64>> + exp_body_size = byte_size(exp_body) + padding_length = Openflow.Utils.padding(4 + exp_body_size, 8) + length = 4 + exp_body_size + padding_length + <<0xffff::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> + end + + def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8), metadata::64, metadata_mask::64>>) do + %NxWriteMetadata{metadata: metadata, metadata_mask: metadata_mask} + end +end diff --git a/lib/openflow/actions/output.ex b/lib/openflow/actions/output.ex new file mode 100644 index 0000000..116dc96 --- /dev/null +++ b/lib/openflow/actions/output.ex @@ -0,0 +1,31 @@ +defmodule Openflow.Action.Output do + defstruct( + port_number: 0, + max_len: :no_buffer + ) + + alias __MODULE__ + + def ofpat, do: 0 + + def new(port) when not is_list(port) do + new(port_number: port) + end + def new(options) when is_list(options) do + port_no = Keyword.get(options, :port_number) + max_len = Keyword.get(options, :max_len, :no_buffer) + %Output{port_number: port_no, max_len: max_len} + end + + def to_binary(%Output{port_number: port_no, max_len: max_len}) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + <<0::16, 16::16, port_no_int::32, max_len::16, 0::size(6)-unit(8)>> + end + + def read(<<0::16, 16::16, port_no_int::32, max_len::16, _pad::size(6)-unit(8)>>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + max_len = Openflow.Utils.get_enum(max_len, :controller_max_len) + %Output{port_number: port_no, max_len: max_len} + end +end diff --git a/lib/openflow/actions/pop_mpls.ex b/lib/openflow/actions/pop_mpls.ex new file mode 100644 index 0000000..7ea7950 --- /dev/null +++ b/lib/openflow/actions/pop_mpls.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PopMpls do + defstruct(ethertype: 0x8847) + + alias __MODULE__ + + def ofpat, do: 20 + + def new(ethertype) do + %PopMpls{ethertype: ethertype} + end + + def to_binary(%PopMpls{ethertype: ethertype}) do + <<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> + end + + def read(<<20::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do + %PopMpls{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/pop_pbb.ex b/lib/openflow/actions/pop_pbb.ex new file mode 100644 index 0000000..d2e4c0d --- /dev/null +++ b/lib/openflow/actions/pop_pbb.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PopPbb do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 27 + + def new do + %PopPbb{} + end + + def to_binary(%PopPbb{}) do + <<27::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<27::16, 8::16, _::size(4)-unit(8)>>) do + %PopPbb{} + end +end diff --git a/lib/openflow/actions/pop_vlan.ex b/lib/openflow/actions/pop_vlan.ex new file mode 100644 index 0000000..8ea4f26 --- /dev/null +++ b/lib/openflow/actions/pop_vlan.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PopVlan do + defstruct([]) + + alias __MODULE__ + + def ofpat, do: 18 + + def new do + %PopVlan{} + end + + def to_binary(%PopVlan{}) do + <<18::16, 8::16, 0::size(4)-unit(8)>> + end + + def read(<<18::16, 8::16, _::size(4)-unit(8)>>) do + %PopVlan{} + end +end diff --git a/lib/openflow/actions/push_mpls.ex b/lib/openflow/actions/push_mpls.ex new file mode 100644 index 0000000..f9c7fe0 --- /dev/null +++ b/lib/openflow/actions/push_mpls.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PushMpls do + defstruct(ethertype: 0x8847) + + alias __MODULE__ + + def ofpat, do: 19 + + def new(ethertype) do + %PushMpls{ethertype: ethertype} + end + + def to_binary(%PushMpls{ethertype: ethertype}) do + <<19::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> + end + + def read(<<19::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do + %PushMpls{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/push_pbb.ex b/lib/openflow/actions/push_pbb.ex new file mode 100644 index 0000000..f509b70 --- /dev/null +++ b/lib/openflow/actions/push_pbb.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PushPbb do + defstruct(ethertype: 0x88e7) + + alias __MODULE__ + + def ofpat, do: 26 + + def new(ethertype) do + %PushPbb{ethertype: ethertype} + end + + def to_binary(%PushPbb{ethertype: ethertype}) do + <<26::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> + end + + def read(<<26::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do + %PushPbb{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/push_vlan.ex b/lib/openflow/actions/push_vlan.ex new file mode 100644 index 0000000..056f033 --- /dev/null +++ b/lib/openflow/actions/push_vlan.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.PushVlan do + defstruct(ethertype: 0x8100) + + alias __MODULE__ + + def ofpat, do: 17 + + def new(ethertype) do + %PushVlan{ethertype: ethertype} + end + + def to_binary(%PushVlan{ethertype: ethertype}) do + <<17::16, 8::16, ethertype::16, 0::size(2)-unit(8)>> + end + + def read(<<17::16, 8::16, ethertype::16, 0::size(2)-unit(8)>>) do + %PushVlan{ethertype: ethertype} + end +end diff --git a/lib/openflow/actions/set_field.ex b/lib/openflow/actions/set_field.ex new file mode 100644 index 0000000..ed962ab --- /dev/null +++ b/lib/openflow/actions/set_field.ex @@ -0,0 +1,34 @@ +defmodule Openflow.Action.SetField do + defstruct(field: nil) + + alias __MODULE__ + + @set_field_size 8 + + def ofpat, do: 25 + + def new({_field, _value} = oxm_field) do + %SetField{field: oxm_field} + end + + def to_binary(%SetField{field: {field, value}}) do + match_bin = + [{field, value}] + |> Openflow.Match.new + |> Openflow.Match.to_binary + + <<1::16, _length::16, padded_field::bytes>> = match_bin + patial_len = @set_field_size - 4 + byte_size(padded_field) + padding = Openflow.Utils.padding(patial_len, 8) + length = patial_len + padding + <<25::16, length::16, padded_field::bytes, 0::size(padding)-unit(8)>> + end + + def read(<<25::16, _length::16, match_field_bin::bytes>>) do + <<_class::16, _field::7, _hm::1, flen::8, _rest::bytes>>= match_field_bin + match_len = 4 + 4 + flen + match_bin = <<1::16, match_len::16, match_field_bin::bytes>> + {[field|_], _rest} = Openflow.Match.read(match_bin) + %SetField{field: field} + end +end diff --git a/lib/openflow/actions/set_mpls_ttl.ex b/lib/openflow/actions/set_mpls_ttl.ex new file mode 100644 index 0000000..a82a0ec --- /dev/null +++ b/lib/openflow/actions/set_mpls_ttl.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.SetMplsTtl do + defstruct(ttl: 0) + + alias __MODULE__ + + def ofpat, do: 15 + + def new(ttl) do + %SetMplsTtl{ttl: ttl} + end + + def to_binary(%SetMplsTtl{ttl: ttl}) do + <<15::16, 8::16, ttl::8, 0::size(3)-unit(8)>> + end + + def read(<<15::16, 8::16, ttl::8, _::size(3)-unit(8)>>) do + %SetMplsTtl{ttl: ttl} + end +end diff --git a/lib/openflow/actions/set_nw_ttl.ex b/lib/openflow/actions/set_nw_ttl.ex new file mode 100644 index 0000000..48a296a --- /dev/null +++ b/lib/openflow/actions/set_nw_ttl.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.SetNwTtl do + defstruct(ttl: 0) + + alias __MODULE__ + + def ofpat, do: 23 + + def new(ttl) do + %SetNwTtl{ttl: ttl} + end + + def to_binary(%SetNwTtl{ttl: ttl}) do + <<23::16, 8::16, ttl::8, 0::size(3)-unit(8)>> + end + + def read(<<23::16, 8::16, ttl::8, _::size(3)-unit(8)>>) do + %SetNwTtl{ttl: ttl} + end +end diff --git a/lib/openflow/actions/set_queue.ex b/lib/openflow/actions/set_queue.ex new file mode 100644 index 0000000..001f4ff --- /dev/null +++ b/lib/openflow/actions/set_queue.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Action.SetQueue do + defstruct(id: 0) + + alias __MODULE__ + + def ofpat, do: 21 + + def new(id) do + %SetQueue{id: id} + end + + def to_binary(%SetQueue{id: id}) do + <<21::16, 8::16, id::32>> + end + + def read(<<21::16, 8::16, id::32>>) do + %SetQueue{id: id} + end +end diff --git a/lib/openflow/barrier/reply.ex b/lib/openflow/barrier/reply.ex new file mode 100644 index 0000000..a392f3d --- /dev/null +++ b/lib/openflow/barrier/reply.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Barrier.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 21 + + def new do + %Reply{} + end + + def read(_) do + %Reply{} + end + + def to_binary(%Reply{}) do + <<>> + end +end diff --git a/lib/openflow/barrier/request.ex b/lib/openflow/barrier/request.ex new file mode 100644 index 0000000..6ca6437 --- /dev/null +++ b/lib/openflow/barrier/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Barrier.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 20 + + def new do + %Request{} + end + + def read(_) do + %Request{} + end + + def to_binary(%Request{}) do + <<>> + end +end diff --git a/lib/openflow/buckets.ex b/lib/openflow/buckets.ex new file mode 100644 index 0000000..806f3b6 --- /dev/null +++ b/lib/openflow/buckets.ex @@ -0,0 +1,54 @@ +defmodule Openflow.Bucket do + defstruct( + weight: 0, + watch_port: 0, + watch_group: 0, + actions: [] + ) + + alias __MODULE__ + + @header_size 16 + + def new(options) do + weight = Keyword.get(options, :weight, 0) + watch_port = Keyword.get(options, :watch_port, :any) + watch_group = Keyword.get(options, :watch_group, :any) + actions = Keyword.get(options, :actions, []) + %Bucket{weight: weight, watch_port: watch_port, watch_group: watch_group, actions: actions} + end + + def read(buckets_bin) do + do_read([], buckets_bin) + end + + def to_binary(buckets) do + to_binary("", buckets) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do + actions_len = length - @header_size + <> = binary + watch_port = Openflow.Utils.get_enum(watch_port_int, :openflow13_port_no) + watch_group = Openflow.Utils.get_enum(watch_group_int, :group_id) + actions = Openflow.Action.read(actions_bin) + bucket = %Bucket{weight: weight, watch_port: watch_port, watch_group: watch_group, actions: actions} + do_read([bucket|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [bucket|rest]) do + %Bucket{weight: weight, watch_port: watch_port, watch_group: watch_group, actions: actions} = bucket + watch_port_int = Openflow.Utils.get_enum(watch_port, :openflow13_port_no) + watch_group_int = Openflow.Utils.get_enum(watch_group, :group_id) + actions_bin = Openflow.Action.to_binary(actions) + length = byte_size(actions_bin) + @header_size + bucket_bin = <> + to_binary(<>, rest) + end +end diff --git a/lib/openflow/echo.ex b/lib/openflow/echo.ex new file mode 100644 index 0000000..d0cea11 --- /dev/null +++ b/lib/openflow/echo.ex @@ -0,0 +1,2 @@ +defmodule Openflow.Echo do +end diff --git a/lib/openflow/echo/reply.ex b/lib/openflow/echo/reply.ex new file mode 100644 index 0000000..5955bf3 --- /dev/null +++ b/lib/openflow/echo/reply.ex @@ -0,0 +1,25 @@ +defmodule Openflow.Echo.Reply do + defstruct( + version: 4, + xid: 0, + data: "", + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 3 + + def new(data \\ "") do + %Reply{data: data} + end + + def read(data) do + %Reply{data: data} + end + + def to_binary(%Reply{data: data}) do + data + end +end diff --git a/lib/openflow/echo/request.ex b/lib/openflow/echo/request.ex new file mode 100644 index 0000000..248dede --- /dev/null +++ b/lib/openflow/echo/request.ex @@ -0,0 +1,26 @@ +defmodule Openflow.Echo.Request do + defstruct( + version: 4, + xid: 0, + data: "", + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 2 + + def new(data \\ "") do + %Request{data: data} + end + + def read(data) do + %Request{data: data} + end + + def to_binary(%Request{data: data}) do + data + end +end + diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex new file mode 100644 index 0000000..32f36d4 --- /dev/null +++ b/lib/openflow/enums.ex @@ -0,0 +1,1081 @@ +defmodule Openflow.Enums do + import Bitwise + + @enums [ + openflow_codec: [ + {Openflow.Hello, 0}, + {Openflow.ErrorMsg, 1}, + {Openflow.Echo.Request, 2}, + {Openflow.Echo.Reply, 3}, + {Openflow.Experimenter, 4}, + {Openflow.Features.Request, 5}, + {Openflow.Features.Reply, 6}, + {Openflow.GetConfig.Request, 7}, + {Openflow.GetConfig.Reply, 8}, + {Openflow.SetConfig, 9}, + {Openflow.PacketIn, 10}, + {Openflow.FlowRemoved, 11}, + {Openflow.PortStatus, 12}, + {Openflow.PacketOut, 13}, + {Openflow.FlowMod, 14}, + {Openflow.GroupMod, 15}, + {Openflow.PortMod, 16}, + {Openflow.TableMod, 17}, + {Openflow.Multipart.Request, 18}, + {Openflow.Multipart.Reply, 19}, + {Openflow.Barrier.Request, 20}, + {Openflow.Barrier.Reply, 21}, + {Openflow.Role.Request, 24}, + {Openflow.Role.Reply, 25}, + {Openflow.GetAsync.Request, 26}, + {Openflow.GetAsync.Reply, 27}, + {Openflow.SetAsync, 28}, + {Openflow.MeterMod, 29} + ], + + experimenter_id: [ + nicira_ext_message: 0x00002320, + onf_ext_message: 0x4f4e4600 + ], + + nicira_ext_message: [ + # {Openflow.NxRole.Request, 10}, /* Openflow 1.3 support role request/reply */ + # {Openflow.NxRole.Reply, 11}, + # {Openflow.NxSetFlowFormat, 12}, /* No special reason for implement this struct codec. */ + # {Openflow.NxFlowMod, 13}, /* Prefer use ofp_flow_mod to nx_flow_mod */ + # {Openflow.NxFlowRemoved, 14}, /* Prefer use ofp_flow_removed to nx_flow_removed */ + # {Openflow.NxSetFlowModTableId, 15}, /* OpenFlow 1.3 support multiple flow table. */ + {Openflow.NxSetPacketInFormat, 16}, + # {Openflow.NxPacketIn, 17}, /* No special reason for implement this struct codec. */ + # {Openflow.NxFlowAge, 18}, /* No special reason for implement this struct codec. */ + # {Openflow.NxSetAsyncConfig, 19}, /* Openflow 1.3 support async config. */ + {Openflow.NxSetControllerId, 20}, + {Openflow.NxFlowMonitor.Cancel, 21}, + {Openflow.NxFlowMonitor.Paused, 22}, + {Openflow.NxFlowMonitor.Resumed, 23}, + {Openflow.NxTLVTableMod, 24}, + {Openflow.NxTLVTable.Request, 25}, + {Openflow.NxTLVTable.Reply, 26}, + {Openflow.NxSetAsyncConfig2, 27}, + {Openflow.NxResume, 28}, + {Openflow.NxCtFlushZone, 29}, + {Openflow.NxPacketIn2, 30} + ], + + onf_ext_message: [ + {Openflow.OnfBundleControl, 2300}, + {Openflow.OnfBundleAddMessage, 2301}, + ], + + multipart_request_flags: [ + more: 1 <<< 0 + ], + + multipart_reply_flags: [ + more: 1 <<< 0 + ], + + multipart_request_codec: [ + {Openflow.Multipart.Desc.Request, 0}, + {Openflow.Multipart.Flow.Request, 1}, + {Openflow.Multipart.Aggregate.Request, 2}, + {Openflow.Multipart.Table.Request, 3}, + {Openflow.Multipart.PortStats.Request, 4}, + {Openflow.Multipart.Queue.Request, 5}, + {Openflow.Multipart.Group.Request, 6}, + {Openflow.Multipart.GroupDesc.Request, 7}, + {Openflow.Multipart.GroupFeatures.Request, 8}, + {Openflow.Multipart.Meter.Request, 9}, + {Openflow.Multipart.MeterConfig.Request, 10}, + {Openflow.Multipart.MeterFeatures.Request, 11}, + {Openflow.Multipart.TableFeatures.Request, 12}, + {Openflow.Multipart.PortDesc.Request, 13}, + {Openflow.Multipart.Experimenter.Request, 0xffff}, + ], + + multipart_reply_codec: [ + {Openflow.Multipart.Desc.Reply, 0}, + {Openflow.Multipart.Flow.Reply, 1}, + {Openflow.Multipart.Aggregate.Reply, 2}, + {Openflow.Multipart.Table.Reply, 3}, + {Openflow.Multipart.PortStats.Reply, 4}, + {Openflow.Multipart.Queue.Reply, 5}, + {Openflow.Multipart.Group.Reply, 6}, + {Openflow.Multipart.GroupDesc.Reply, 7}, + {Openflow.Multipart.GroupFeatures.Reply, 8}, + {Openflow.Multipart.Meter.Reply, 9}, + {Openflow.Multipart.MeterConfig.Reply, 10}, + {Openflow.Multipart.MeterFeatures.Reply, 11}, + {Openflow.Multipart.TableFeatures.Reply, 12}, + {Openflow.Multipart.PortDesc.Reply, 13}, + {Openflow.Multipart.Experimenter.Reply, 0xffff}, + ], + + nicira_ext_stats: [ + {Openflow.Multipart.NxFlow, 0}, + {Openflow.Multipart.NxAggregate, 1}, + {Openflow.Multipart.NxFlowMonitor, 2}, + {Openflow.Multipart.NxIPFIXBridge, 3}, + {Openflow.Multipart.NxIPFIXFlow, 4}, + ], + + hello_elem: [ + versionbitmap: 1 + ], + + error_type: [ + hello_failed: 0, + bad_request: 1, + bad_action: 2, + bad_instruction: 3, + bad_match: 4, + flow_mod_failed: 5, + group_mod_failed: 6, + port_mod_failed: 7, + table_mod_failed: 8, + queue_op_failed: 9, + switch_config_failed: 10, + role_request_failed: 11, + meter_mod_failed: 12, + table_features_failed: 13, + experimenter: 0xffff + ], + + hello_failed: [ + inconpatible: 0, + eperm: 1 + ], + + bad_request: [ + bad_version: 0, + bad_type: 1, + bad_multipart: 2, + bad_experimeter: 3, + bad_exp_type: 4, + eperm: 5, + bad_len: 6, + buffer_empty: 7, + buffer_unknown: 8, + bad_table_id: 9, + is_slave: 10, + bad_port: 11, + bad_packet: 12, + multipart_buffer_overflow: 13 + ], + + bad_action: [ + bad_type: 0, + bad_len: 1, + bad_experimeter: 2, + bad_exp_type: 3, + bad_out_port: 4, + bad_argument: 5, + eperm: 6, + too_many: 7, + bad_queue: 8, + bad_out_group: 9, + match_inconsistent: 10, + unsupported_order: 11, + bad_tag: 12, + bad_set_type: 13, + bad_set_len: 14, + bad_set_argument: 15 + ], + + bad_instruction: [ + unknown_instruction: 0, + unsupported_instruction: 1, + bad_table_id: 2, + unsupported_metadata: 3, + unsupported_metadata_mask: 4, + bad_experimeter: 5, + bad_exp_type: 6, + bad_len: 7, + eperm: 8 + ], + + bad_match: [ + bad_type: 0, + bad_len: 1, + bad_tag: 2, + bad_dl_addr_mask: 3, + bad_nw_addr_mask: 4, + bad_wildcards: 5, + bad_field: 6, + bad_value: 7, + bad_mask: 8, + bad_prereq: 9, + dup_field: 10, + eperm: 11 + ], + + flow_mod_failed: [ + unknown: 0, + table_full: 1, + bad_table_id: 2, + overlap: 3, + eperm: 4, + bad_timeout: 5, + bad_command: 6, + bad_flags: 7 + ], + + group_mod_failed: [ + group_exists: 0, + invalid_group: 1, + weight_unsupported: 2, + out_of_groups: 3, + ouf_of_buckets: 4, + chaining_unsupported: 5, + watch_unsupported: 6, + loop: 7, + unknown_group: 8, + chained_group: 9, + bad_type: 10, + bad_command: 11, + bad_bucket: 12, + bad_watch: 13, + eperm: 14 + ], + + port_mod_failed: [ + bad_port: 0, + bad_hw_addr: 1, + bad_config: 2, + bad_advertise: 3, + eperm: 4 + ], + + table_mod_failed: [ + bad_table: 0, + bad_config: 1, + eperm: 2 + ], + + queue_op_failed: [ + bad_port: 0, + bad_queue: 1, + eperm: 2 + ], + + switch_config_failed: [ + bad_flags: 0, + bad_len: 1, + eperm: 2 + ], + + role_request_failed: [ + stale: 0, + unsup: 1, + bad_role: 2 + ], + + meter_mod_failed: [ + unknown: 0, + meter_exists: 1, + invalid_meter: 2, + unknown_meter: 3, + bad_command: 4, + bad_flags: 5, + bad_rate: 6, + bad_burst: 7, + bad_band: 8, + bad_band_value: 9, + out_of_meters: 10, + out_of_bands: 11 + ], + + table_features_failed: [ + bad_table: 0, + bad_metadata: 1, + bad_type: 2, + bad_len: 3, + bad_argument: 4, + eperm: 5 + ], + + switch_capabilities: [ + flow_stats: 1 <<< 0, + table_stats: 1 <<< 1, + port_stats: 1 <<< 2, + group_stats: 1 <<< 3, + ip_reasm: 1 <<< 5, + queue_stats: 1 <<< 6, + arp_match_ip: 1 <<< 7, + port_blocked: 1 <<< 8 + ], + + config_flags: [ + drop: 1 <<< 0, + reasm: 1 <<< 1 + ], + + controller_max_len: [ + max: 0xffe5, + no_buffer: 0xffff + ], + + experimenter_oxm_vendors: [ + nicira_ext_match: 0x00002320, + onf_ext_match: 0x4f4e4600 + ], + + match_type: [ + standard: 0, + oxm: 1 + ], + + oxm_class: [ + nxm_0: 0x0000, + nxm_1: 0x0001, + openflow_basic: 0x8000, + packet_register: 0x8001, + experimenter: 0xffff + ], + + nxm_0: [ + nx_in_port: 0, + nx_eth_dst: 1, + nx_eth_src: 2, + nx_eth_type: 3, + nx_vlan_tci: 4, + nx_ip_tos: 5, + nx_ip_proto: 6, + nx_ipv4_src: 7, + nx_ipv4_dst: 8, + nx_tcp_src: 9, + nx_tcp_dst: 10, + nx_udp_src: 11, + nx_udp_dst: 12, + nx_icmpv4_type: 13, + nx_icmpv4_code: 14, + nx_arp_op: 15, + nx_arp_spa: 16, + nx_arp_tpa: 17, + nx_tcp_flags: 34, + ], + + nxm_1: [ + reg0: 0, + reg1: 1, + reg2: 2, + reg3: 3, + reg4: 4, + reg5: 5, + reg6: 6, + reg7: 7, + reg8: 8, + reg9: 9, + reg10: 10, + reg11: 11, + reg12: 12, + reg13: 13, + reg14: 14, + reg15: 15, + tun_id: 16, + nx_arp_sha: 17, + nx_arp_tha: 18, + nx_ipv6_src: 19, + nx_ipv6_dst: 20, + nx_icmpv6_type: 21, + nx_icmpv6_code: 22, + nx_ipv6_nd_target: 23, + nx_ipv6_nd_sll: 24, + nx_ipv6_nd_tll: 25, + nx_ip_frag: 26, + nx_ipv6_label: 27, + nx_ip_ecn: 28, + nx_ip_ttl: 29, + nx_mpls_ttl: 30, + tun_src: 31, + tun_dst: 32, + pkt_mark: 33, + dp_hash: 35, + recirc_id: 36, + conj_id: 37, + tun_gbp_id: 38, + tun_gbp_flags: 39, + tun_metadata0: 40, + tun_metadata1: 41, + tun_metadata2: 42, + tun_metadata3: 43, + tun_metadata4: 44, + tun_metadata5: 45, + tun_metadata6: 46, + tun_metadata7: 47, + tun_metadata8: 48, + tun_metadata9: 49, + tun_metadata10: 50, + tun_metadata11: 51, + tun_metadata12: 52, + tun_metadata13: 53, + tun_metadata14: 54, + tun_metadata15: 55, + tun_metadata16: 56, + tun_metadata17: 57, + tun_metadata18: 58, + tun_metadata19: 59, + tun_metadata20: 60, + tun_metadata21: 61, + tun_metadata22: 62, + tun_metadata23: 63, + tun_metadata24: 64, + tun_metadata25: 65, + tun_metadata26: 66, + tun_metadata27: 67, + tun_metadata28: 68, + tun_metadata29: 69, + tun_metadata30: 70, + tun_metadata31: 71, + tun_metadata32: 72, + tun_metadata33: 73, + tun_metadata34: 74, + tun_metadata35: 75, + tun_metadata36: 76, + tun_metadata37: 77, + tun_metadata38: 78, + tun_metadata39: 79, + tun_metadata40: 80, + tun_metadata41: 81, + tun_metadata42: 82, + tun_metadata43: 83, + tun_metadata44: 84, + tun_metadata45: 85, + tun_metadata46: 86, + tun_metadata47: 87, + tun_metadata48: 88, + tun_metadata49: 89, + tun_metadata50: 90, + tun_metadata51: 91, + tun_metadata52: 92, + tun_metadata53: 93, + tun_metadata54: 94, + tun_metadata55: 95, + tun_metadata56: 96, + tun_metadata57: 97, + tun_metadata58: 98, + tun_metadata59: 99, + tun_metadata60: 100, + tun_metadata61: 101, + tun_metadata62: 102, + tun_metadata63: 103, + tun_flags: 104, + ct_state: 105, + ct_zone: 106, + ct_mark: 107, + ct_label: 108, + tun_ipv6_src: 109, + tun_ipv6_dst: 110, + xxreg0: 111, + xxreg1: 112, + xxreg2: 113, + xxreg3: 114, + xxreg4: 115, + xxreg5: 116, + xxreg6: 117, + xxreg7: 118, + ct_nw_proto: 119, + ct_nw_src: 120, + ct_nw_dst: 121, + ct_ipv6_src: 122, + ct_ipv6_dst: 123, + ct_tp_src: 124, + ct_tp_dst: 125 + ], + + openflow_basic: [ + in_port: 0, + in_phy_port: 1, + metadata: 2, + eth_dst: 3, + eth_src: 4, + eth_type: 5, + vlan_vid: 6, + vlan_pcp: 7, + ip_dscp: 8, + ip_ecn: 9, + ip_proto: 10, + ipv4_src: 11, + ipv4_dst: 12, + tcp_src: 13, + tcp_dst: 14, + udp_src: 15, + udp_dst: 16, + sctp_src: 17, + sctp_dst: 18, + icmpv4_type: 19, + icmpv4_code: 20, + arp_op: 21, + arp_spa: 22, + arp_tpa: 23, + arp_sha: 24, + arp_tha: 25, + ipv6_src: 26, + ipv6_dst: 27, + ipv6_flabel: 28, + icmpv6_type: 29, + icmpv6_code: 30, + ipv6_nd_target: 31, + ipv6_nd_sll: 32, + ipv6_nd_tll: 33, + mpls_label: 34, + mpls_tc: 35, + mpls_bos: 36, + pbb_isid: 37, + tunnel_id: 38, + ipv6_exthdr: 39, + + # Lagopus extended match fields + pbb_uca: 41, + packet_type: 42, + gre_flags: 43, + gre_ver: 44, + gre_protocol: 45, + gre_key: 46, + gre_seqnum: 47, + lisp_flags: 48, + lisp_nonce: 49, + lisp_id: 50, + vxlan_flags: 51, + vxlan_vni: 52, + mpls_data_first_nibble: 53, + mpls_ach_version: 54, + mpls_ach_channel: 55, + mpls_pw_metadata: 56, + mpls_cw_flags: 57, + mpls_cw_fragment: 58, + mpls_cw_len: 59, + mpls_cw_seq_num: 60, + gtpu_flags: 61, + gtpu_ver: 62, + gtpu_msg_type: 63, + gtpu_teid: 64, + gtpu_extn_hdr: 65, + gtpu_extn_udp_port: 66, + gtpu_extn_sci: 67 + ], + + vlan_id: [ + present: 0x1000, + none: 0x0000 + ], + + ipv6exthdr_flags: [ + nonext: 1 <<< 0, + esp: 1 <<< 1, + auth: 1 <<< 2, + dest: 1 <<< 3, + frag: 1 <<< 4, + router: 1 <<< 5, + hop: 1 <<< 6, + unrep: 1 <<< 7, + unseq: 1 <<< 8 + ], + + tcp_flags: [ + fin: 1 <<< 0, + syn: 1 <<< 1, + rst: 1 <<< 2, + psh: 1 <<< 3, + ack: 1 <<< 4, + urg: 1 <<< 5, + ece: 1 <<< 6, + cwr: 1 <<< 7, + ns: 1 <<< 8 + ], + + ct_state_flags: [ + new: 1 <<< 0, # Beginning of a new connection. + est: 1 <<< 1, # Part of an existing connection. + rel: 1 <<< 2, # Related to an established connection. + rep: 1 <<< 3, # Flow is in the reply direction. + inv: 1 <<< 4, # Could not track connection. + trk: 1 <<< 5, # Conntrack has occurred. + snat: 1 <<< 6, # Packet's source address/port was mangled by NAT. + dnat: 1 <<< 7 # Packet's destination address/port was mangled by NAT. + ], + + packet_register: [ + xreg0: 0, + xreg1: 1, + xreg2: 2, + xreg3: 3, + xreg4: 4, + xreg5: 5, + xreg6: 6, + xreg7: 7 + ], + + nicira_ext_match: [ + nsh_flags: 1, + nsh_mdtype: 2, + nsh_np: 3, + nsh_spi: 4, + nsh_si: 5, + nsh_c1: 6, + nsh_c2: 7, + nsh_c3: 8, + nsh_c4: 9 + ], + + onf_ext_match: [ + onf_tcp_flags: 42, + onf_actset_output: 43, + onf_pbb_uca: 2560, + ], + + packet_in_reason: [ + no_match: 0, + action: 1, + invalid_ttl: 2 + ], + + buffer_id: [ + no_buffer: 0xffffffff + ], + + port_config: [ + port_down: 1 <<< 0, + no_receive: 1 <<< 2, + no_forward: 1 <<< 5, + no_packet_in: 1 <<< 6 + ], + + port_state: [ + link_down: 1 <<< 0, + blocked: 1 <<< 1, + live: 1 <<< 2 + ], + + port_features: [ + {:"10mb_hd", 1 <<< 0}, + {:"10mb_fd", 1 <<< 1}, + {:"100mb_hd", 1 <<< 2}, + {:"100mb_fd", 1 <<< 3}, + {:"1gb_hd", 1 <<< 4}, + {:"1gb_fd", 1 <<< 5}, + {:"10gb_fd", 1 <<< 6}, + {:"40gb_fd", 1 <<< 7}, + {:"100gb_fd", 1 <<< 8}, + {:"1tb_fd", 1 <<< 9}, + {:other, 1 <<< 10}, + {:copper, 1 <<< 11}, + {:fiber, 1 <<< 12}, + {:autoneg, 1 <<< 13}, + {:pause, 1 <<< 14}, + {:pause_asym, 1 <<< 15} + ], + + openflow10_port_no: [ + max: 0xff00, + in_port: 0xfff8, + table: 0xfff9, + normal: 0xfffa, + flood: 0xfffb, + all: 0xfffc, + controller: 0xfffd, + local: 0xfffe, + none: 0xffff + ], + + openflow13_port_no: [ + max: 0xffffff00, + in_port: 0xfffffff8, + table: 0xfffffff9, + normal: 0xfffffffa, + flood: 0xfffffffb, + all: 0xfffffffc, + controller: 0xfffffffd, + local: 0xfffffffe, + any: 0xffffffff + ], + + packet_in_reason: [ + no_match: 0, + action: 1, + invalid_ttl: 2, + action_set: 3, + group: 4, + packet_out: 5 + ], + + flow_mod_command: [ + add: 0, + modify: 1, + modify_strict: 2, + delete: 3, + delete_strict: 4 + ], + + flow_mod_flags: [ + send_flow_rem: 1 <<< 0, + check_overlap: 1 <<< 1, + reset_counts: 1 <<< 2, + no_packet_counts: 1 <<< 3, + no_byte_counts: 1 <<< 4 + ], + + flow_removed_reason: [ + idle_timeout: 0, + hard_timeout: 1, + delete: 2, + group_delete: 3, + meter_delete: 4, + eviction: 5 + ], + + port_reason: [ + add: 0, + delete: 1, + modify: 2 + ], + + group_mod_command: [ + add: 0, + modify: 1, + delete: 2 + ], + + group_type: [ + all: 0, + select: 1, + indirect: 2, + fast_failover: 3 + ], + + group_id: [ + max: 0xffffff00, + all: 0xfffffffc, + any: 0xffffffff + ], + + group_capabilities: [ + select_weight: 1 <<< 0, + select_liveness: 1 <<< 1, + chaining: 1 <<< 2, + chaining_checks: 1 <<< 3 + ], + + table_id: [ + max: 0xfe, + all: 0xff + ], + + queue_id: [ + all: 0xffffffff + ], + + meter_mod_command: [ + add: 0, + modify: 1, + delete: 2 + ], + + meter_id: [ + max: 0xffff0000, + slowpath: 0xfffffffd, + controller: 0xfffffffe, + all: 0xffffffff + ], + + meter_flags: [ + kbps: 1 <<< 0, + pktps: 1 <<< 1, + burst: 1 <<< 2, + stats: 1 <<< 3 + ], + + meter_band_type: [ + {Openflow.MeterBand.Drop, 1}, + {Openflow.MeterBand.Remark, 2}, + {Openflow.MeterBand.Experimenter, 0xffff}, + ], + + table_config: [ + table_miss_controller: 0 <<< 0, + table_miss_continue: 1 <<< 0, + table_miss_drop: 2 <<< 0, + table_miss_mask: 3 <<< 0, + eviction: 1 <<< 2, + vacancy_events: 1 <<< 3 + ], + + action_type: [ + {Openflow.Action.Output, 0}, + {Openflow.Action.CopyTtlOut, 11}, + {Openflow.Action.CopyTtlIn, 12}, + {Openflow.Action.SetMplsTtl, 15}, + {Openflow.Action.DecMplsTtl, 16}, + {Openflow.Action.PushVlan, 17}, + {Openflow.Action.PopVlan, 18}, + {Openflow.Action.PushMpls, 19}, + {Openflow.Action.PopMpls, 20}, + {Openflow.Action.SetQueue, 21}, + {Openflow.Action.Group, 22}, + {Openflow.Action.SetNwTtl, 23}, + {Openflow.Action.DecNwTtl, 24}, + {Openflow.Action.SetField, 25}, + {Openflow.Action.PushPbb, 26}, + {Openflow.Action.PopPbb, 27}, + {Openflow.Action.Encap, 28}, + {Openflow.Action.Decap, 29}, + {Openflow.Action.SetSequence, 30}, + {Openflow.Action.ValidateSequence, 31}, + {Openflow.Action.Experimenter, 0xffff} + ], + + action_vendor: [ + nicira_ext_action: 0x00002320, + onf_ext_action: 0x4f4e4600 + ], + + onf_ext_action: [ + {Openflow.Action.OnfCopyField, 3200} + ], + + nicira_ext_action: [ + {Openflow.Action.NxResubmit, 1}, + {Openflow.Action.NxSetTunnel, 2}, + {Openflow.Action.NxSetQueue, 4}, + {Openflow.Action.NxPopQueue, 5}, + {Openflow.Action.NxRegMove, 6}, + {Openflow.Action.NxRegLoad, 7}, + {Openflow.Action.NxNote, 8}, + {Openflow.Action.NxSetTunnel64, 9}, + {Openflow.Action.NxMultipath, 10}, + {Openflow.Action.NxBundle, 12}, + {Openflow.Action.NxBundleLoad, 13}, + {Openflow.Action.NxResubmitTable, 14}, + {Openflow.Action.NxOutputReg, 15}, + {Openflow.Action.NxLearn, 16}, + {Openflow.Action.NxExit, 17}, + {Openflow.Action.NxDecTtl, 18}, + {Openflow.Action.NxFinTimeout, 19}, + {Openflow.Action.NxController, 20}, + {Openflow.Action.NxDecTtlCntIds, 21}, + {Openflow.Action.NxWriteMetadata, 22}, + {Openflow.Action.NxPushMpls, 23}, + {Openflow.Action.NxPopMpls, 24}, + {Openflow.Action.NxSetMplsTtl, 25}, + {Openflow.Action.NxDecMplsTtl, 26}, + {Openflow.Action.NxStackPush, 27}, + {Openflow.Action.NxStackPop, 28}, + {Openflow.Action.NxSample, 29}, + {Openflow.Action.NxSetMplsLabel, 30}, + {Openflow.Action.NxSetMplsTc, 31}, + {Openflow.Action.NxOutputReg2, 32}, + {Openflow.Action.NxRegLoad2, 33}, + {Openflow.Action.NxConjunction, 34}, + {Openflow.Action.NxConntrack, 35}, + {Openflow.Action.NxNat, 36}, + {Openflow.Action.NxController2, 37}, + {Openflow.Action.NxSample2, 38}, + {Openflow.Action.NxOutputTrunc, 39}, + {Openflow.Action.NxGroup, 40}, + {Openflow.Action.NxSample3, 41}, + {Openflow.Action.NxClone, 42}, + {Openflow.Action.NxCtClear, 43}, + {Openflow.Action.NxResubmitTableCt, 44}, + {Openflow.Action.NxLearn2, 45}, + {Openflow.Action.NxEncap, 46}, + {Openflow.Action.NxDecap, 47}, + {Openflow.Action.NxDebugRecirc, 0xff}, + ], + + nx_mp_algorithm: [ + modulo_n: 0, + hash_threshold: 1, + highest_random_weight: 2, + iterative_hash: 3 + ], + + nx_hash_fields: [ + eth_src: 0, + symmetric_l4: 1, + symmetric_l3l4: 2, + symmetric_l3l4_udp: 3, + nw_src: 4, + nw_dst: 5, + ], + + nx_bd_algorithm: [ + active_backup: 0, + highest_random_weight: 1 + ], + + nx_learn_flag: [ + send_flow_rem: 1 <<< 0, + delete_learned: 1 <<< 1, + write_result: 1 <<< 2 + ], + + nx_conntrack_flags: [ + commit: 1 <<< 0, + force: 1 <<< 1 + ], + + nx_nat_flags: [ + src: 1 <<< 0, + dst: 1 <<< 1, + persistent: 1 <<< 2, + protocol_hash: 1 <<< 3, + protocol_random: 1 <<< 4 + ], + + nx_nat_range: [ + ipv4_min: 1 <<< 0, + ipv4_max: 1 <<< 1, + ipv6_min: 1 <<< 2, + ipv6_max: 1 <<< 3, + proto_min: 1 <<< 4, + proto_max: 1 <<< 5 + ], + + nx_action_controller2_prop_type: [ + max_len: 0, + controller_id: 1, + reason: 2, + userdata: 3, + pause: 4 + ], + + nx_action_sample_direction: [ + default: 0, + ingress: 1, + egress: 2 + ], + + nx_flow_spec_type: [ + {Openflow.Action.NxFlowSpecMatch, 0}, + {Openflow.Action.NxFlowSpecLoad, 1}, + {Openflow.Action.NxFlowSpecOutput, 2} + ], + + instruction_type: [ + {Openflow.Instruction.GotoTable, 1}, + {Openflow.Instruction.WriteMetadata, 2}, + {Openflow.Instruction.WriteActions, 3}, + {Openflow.Instruction.ApplyActions, 4}, + {Openflow.Instruction.ClearActions, 5}, + {Openflow.Instruction.Meter, 6}, + {Openflow.Instruction.Experimenter, 0xffff} + ], + + controller_role: [ + nochange: 0, + equal: 1, + master: 2, + slave: 3 + ], + + nx_role: [ + other: 0, + master: 1, + slave: 2 + ], + + packet_in_format: [ + standard: 0, + nxt_packet_in: 1, + nxt_packet_in2: 2 + ], + + flow_format: [ + openflow10: 0, + nxm: 1 + ], + + packet_in2_prop_type: [ + packet: 0, + full_len: 1, + buffer_id: 2, + table_id: 3, + cookie: 4, + reason: 5, + metadata: 6, + userdata: 7, + continuation: 8 + ], + + continuation_prop_type: [ + bridge: 0x8000, + stack: 0x8001, + mirrors: 0x8002, + conntracked: 0x8003, + table_id: 0x8004, + cookie: 0x8005, + actions: 0x8006, + action_set: 0x8007 + ], + + flow_monitor_flag: [ + initial: 1 <<< 0, + add: 1 <<< 1, + delete: 1 <<< 2, + modify: 1 <<< 3, + actions: 1 <<< 4, + own: 1 <<< 5 + ], + + flow_update_event: [ + added: 0, + deleted: 1, + modified: 2, + abbrev: 3 + ], + + tlv_table_mod_command: [ + add: 0, + delete: 1, + clear: 2 + ] + ] + + for {enum_name, enum_def} <- @enums do + enum_name = to_string(enum_name) + to_int_fn_name = String.to_atom(enum_name <> "_to_int") + to_atom_fn_name = String.to_atom(enum_name <> "_to_atom") + + for {key, value} <- enum_def do + def to_int(unquote(key), unquote(String.to_atom(enum_name))) do + try do + unquote(to_int_fn_name)(unquote(key)) + catch + _ -> unquote(key) + end + end + + def to_atom(unquote(value), unquote(String.to_atom(enum_name))) do + try do + unquote(to_atom_fn_name)(unquote(value)) + catch + _ -> unquote(value) + end + end + end + def to_int(_int, unquote(String.to_atom(enum_name))) do + throw(:bad_enum) + end + + def to_atom(_, unquote(String.to_atom(enum_name))) do + throw(:bad_enum) + end + + for {key, value} <- enum_def do + def unquote(to_int_fn_name)(unquote(key)), do: unquote(value) + def unquote(to_atom_fn_name)(unquote(value)), do: unquote(key) + end + def unquote(to_int_fn_name)(_), do: throw(:bad_enum) + def unquote(to_atom_fn_name)(_), do: throw(:bad_enum) + + def int_to_flags(int, unquote(String.to_atom(enum_name))) do + Openflow.Utils.int_to_flags([], int, enum_of(unquote(String.to_atom(enum_name)))) + end + + def flags_to_int(flags, unquote(String.to_atom(enum_name))) do + Openflow.Utils.flags_to_int(0, flags, enum_of(unquote(String.to_atom(enum_name)))) + end + + defp enum_of(unquote(String.to_atom(enum_name))), do: unquote(enum_def) + end +end diff --git a/lib/openflow/error_msg.ex b/lib/openflow/error_msg.ex new file mode 100644 index 0000000..4a6f354 --- /dev/null +++ b/lib/openflow/error_msg.ex @@ -0,0 +1,53 @@ +defmodule Openflow.ErrorMsg do + @moduledoc "OpenFlow Error codec module" + + defstruct( + version: 4, + xid: 0, + type: nil, + code: nil, + data: "", + exp_type: nil, + experimenter: nil, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 1 + + def read(<<0xffff::16, exp_type::16, experimenter::32, data::bytes>>) do + error_type = Openflow.Enums.to_atom(0xffff, :error_type) + %ErrorMsg{ + type: error_type, + exp_type: exp_type, + experimenter: experimenter, + data: data + } + end + def read(<>) do + error_type = Openflow.Enums.to_atom(type, :error_type) + error_code = Openflow.Enums.to_atom(code, error_type) + %ErrorMsg{ + type: error_type, + code: error_code, + data: data + } + end + + def to_binary(%ErrorMsg{type: :experimenter, + exp_type: exp_type, + experimenter: experimenter, + data: data}) do + error_type = Openflow.Enums.to_int(:experimenter, :error_type) + <> + end + def to_binary(%ErrorMsg{type: type, + code: code, + data: data}) do + error_type = Openflow.Enums.to_int(type, :error_type) + error_code = Openflow.Enums.to_int(code, type) + <> + end +end diff --git a/lib/openflow/experimenter.ex b/lib/openflow/experimenter.ex new file mode 100644 index 0000000..bfead7a --- /dev/null +++ b/lib/openflow/experimenter.ex @@ -0,0 +1,39 @@ +defmodule Openflow.Experimenter do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + exp_id: 0, + exp_type: 0, + data: "" + ) + + alias __MODULE__ + + def ofp_type, do: 4 + + def new(options) do + %Experimenter{exp_id: Keyword.get(options, :exp_id, 0), + exp_type: Keyword.get(options, :exp_type, 0), + data: Keyword.get(options, :data, "")} + end + + def to_binary(%Experimenter{exp_id: exp_id, exp_type: exp_type, data: data}) do + <> + end + + def read(<>) do + case Openflow.Utils.get_enum(exp_id, :experimenter_id) do + ^exp_id -> + %Experimenter{exp_id: exp_id, exp_type: exp_type, data: data} + experimenter when is_atom(experimenter) -> + case Openflow.Utils.get_enum(exp_type, experimenter) do + ^exp_type -> + %Experimenter{exp_id: exp_id, exp_type: exp_type, data: data} + codec when is_atom(codec) -> + codec.read(<>) + end + end + end +end diff --git a/lib/openflow/features.ex b/lib/openflow/features.ex new file mode 100644 index 0000000..43b26b8 --- /dev/null +++ b/lib/openflow/features.ex @@ -0,0 +1,2 @@ +defmodule Openflow.Features do +end diff --git a/lib/openflow/features/reply.ex b/lib/openflow/features/reply.ex new file mode 100644 index 0000000..1bac1a5 --- /dev/null +++ b/lib/openflow/features/reply.ex @@ -0,0 +1,39 @@ +defmodule Openflow.Features.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + n_buffers: 0, + n_tables: 0, + aux_id: 0, + capabilities: [], + ) + + alias __MODULE__ + + def ofp_type, do: 6 + + def read(<>) do + dpid = Openflow.Utils.to_hex_string(datapath_id) + flags = Openflow.Enums.int_to_flags(caps_int, :switch_capabilities) + %Reply{ + datapath_id: dpid, + n_buffers: n_buf, + n_tables: n_tab, + aux_id: aux_id, + capabilities: flags + } + end + + def to_binary(%Reply{ + datapath_id: datapath_id, + n_buffers: n_buf, + n_tables: n_tab, + aux_id: aux_id, + capabilities: flags}) do + dpid_int = String.to_integer(datapath_id, 16) + flags_int = Openflow.Enums.flags_to_int(flags, :switch_capabilities) + <> + end +end diff --git a/lib/openflow/features/request.ex b/lib/openflow/features/request.ex new file mode 100644 index 0000000..d23aee1 --- /dev/null +++ b/lib/openflow/features/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Features.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 5 + + def new do + %Request{} + end + + def read(_) do + %Request{} + end + + def to_binary(%Request{}) do + <<>> + end +end diff --git a/lib/openflow/flow_mod.ex b/lib/openflow/flow_mod.ex new file mode 100644 index 0000000..338b0c7 --- /dev/null +++ b/lib/openflow/flow_mod.ex @@ -0,0 +1,109 @@ +defmodule Openflow.FlowMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + cookie: 0, + cookie_mask: 0, + table_id: 0, + command: :add, + idle_timeout: 0, + hard_timeout: 0, + priority: 0, + buffer_id: :no_buffer, + out_port: :any, + out_group: :any, + flags: [], + match: [], + instructions: [] + ) + + alias __MODULE__ + + def ofp_type, do: 14 + + def new(options \\ []) do + cookie = Keyword.get(options, :cookie, 0) + cookie_mask = Keyword.get(options, :cookie_mask, 0) + table_id = Keyword.get(options, :table_id, 0) + command = Keyword.get(options, :command, :add) + idle = Keyword.get(options, :idle_timeout, 0) + hard = Keyword.get(options, :hard_timeout, 0) + priority = Keyword.get(options, :priority, 0) + buffer_id = Keyword.get(options, :buffer_id, :no_buffer) + out_port = Keyword.get(options, :out_port, :any) + out_group = Keyword.get(options, :out_group, :any) + flags = Keyword.get(options, :flags, []) + match = Keyword.get(options, :match, Openflow.Match.new) + instructions = Keyword.get(options, :instructions, []) + %FlowMod{cookie: cookie, + cookie_mask: cookie_mask, + priority: priority, + table_id: table_id, + command: command, + idle_timeout: idle, + hard_timeout: hard, + buffer_id: buffer_id, + out_port: out_port, + out_group: out_group, + flags: flags, + match: match, + instructions: instructions} + end + + def read(<>) do + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + command = Openflow.Utils.get_enum(command_int, :flow_mod_command) + buffer_id = Openflow.Utils.get_enum(buffer_id_int, :buffer_id) + out_port = Openflow.Utils.get_enum(out_port_int, :openflow13_port_no) + out_group = Openflow.Utils.get_enum(out_group_int, :group_id) + flags = Openflow.Enums.int_to_flags(flags_int, :flow_mod_flags) + {match_fields, instructions_bin} = Openflow.Match.read(rest) + match = Openflow.Match.new(match_fields) + instructions = Openflow.Instruction.read(instructions_bin) + %FlowMod{cookie: cookie, + cookie_mask: cookie_mask, + priority: prio, + table_id: table_id, + command: command, + idle_timeout: idle, + hard_timeout: hard, + buffer_id: buffer_id, + out_port: out_port, + out_group: out_group, + flags: flags, + match: match, + instructions: instructions} + end + + def to_binary(flow_mod) do + %FlowMod{cookie: cookie, + cookie_mask: cookie_mask, + priority: prio, + table_id: table_id, + command: command, + idle_timeout: idle, + hard_timeout: hard, + buffer_id: buffer_id, + out_port: out_port, + out_group: out_group, + flags: flags, + match: match_fields, + instructions: instructions} = flow_mod + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + command_int = Openflow.Utils.get_enum(command, :flow_mod_command) + buffer_id_int = Openflow.Utils.get_enum(buffer_id, :buffer_id) + out_port_int = Openflow.Utils.get_enum(out_port, :openflow13_port_no) + out_group_int = Openflow.Utils.get_enum(out_group, :group_id) + flags_int = Openflow.Enums.flags_to_int(flags, :flow_mod_flags) + match_fields_bin = Openflow.Match.to_binary(match_fields) + instructions_bin = Openflow.Instruction.to_binary(instructions) + <> + end +end diff --git a/lib/openflow/flow_removed.ex b/lib/openflow/flow_removed.ex new file mode 100644 index 0000000..3b3fe68 --- /dev/null +++ b/lib/openflow/flow_removed.ex @@ -0,0 +1,42 @@ +defmodule Openflow.FlowRemoved do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + cookie: 0, + priority: 0, + reason: :idle_timeout, + table_id: 0, + duration_sec: 0, + duration_nsec: 0, + idle_timeout: 0, + hard_timeout: 0, + packet_count: 0, + byte_count: 0, + match: [] + ) + + alias __MODULE__ + + def ofp_type, do: 11 + + def read(<>) do + reason = Openflow.Enums.to_atom(reason_int, :flow_removed_reason) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + {match_fields, _rest} = Openflow.Match.read(rest) + %FlowRemoved{cookie: cookie, + priority: priority, + reason: reason, + table_id: table_id, + duration_sec: dsec, + duration_nsec: dnsec, + idle_timeout: idle, + hard_timeout: hard, + packet_count: pkt, + byte_count: byt, + match: match_fields} + end +end diff --git a/lib/openflow/get_async/reply.ex b/lib/openflow/get_async/reply.ex new file mode 100644 index 0000000..58b8f45 --- /dev/null +++ b/lib/openflow/get_async/reply.ex @@ -0,0 +1,40 @@ +defmodule Openflow.GetAsync.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0, # virtual field + packet_in_mask_master: 0, + packet_in_mask_slave: 0, + port_status_mask_master: 0, + port_status_mask_slave: 0, + flow_removed_mask_master: 0, + flow_removed_mask_slave: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 27 + + def read(<>) do + %Reply{packet_in_mask_master: packet_in_mask_master, + packet_in_mask_slave: packet_in_mask_slave, + port_status_mask_master: port_status_mask_master, + port_status_mask_slave: port_status_mask_slave, + flow_removed_mask_master: flow_removed_mask_master, + flow_removed_mask_slave: flow_removed_mask_slave} + end + + def to_binary(%Reply{packet_in_mask_master: packet_in_mask_master, + packet_in_mask_slave: packet_in_mask_slave, + port_status_mask_master: port_status_mask_master, + port_status_mask_slave: port_status_mask_slave, + flow_removed_mask_master: flow_removed_mask_master, + flow_removed_mask_slave: flow_removed_mask_slave}) do + <> + end +end diff --git a/lib/openflow/get_async/request.ex b/lib/openflow/get_async/request.ex new file mode 100644 index 0000000..7433daf --- /dev/null +++ b/lib/openflow/get_async/request.ex @@ -0,0 +1,20 @@ +defmodule Openflow.GetAsync.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 26 + + def read(_) do + %Request{} + end + + def to_binary(%Request{}) do + <<>> + end +end diff --git a/lib/openflow/get_config.ex b/lib/openflow/get_config.ex new file mode 100644 index 0000000..ceb38e2 --- /dev/null +++ b/lib/openflow/get_config.ex @@ -0,0 +1,2 @@ +defmodule Openflow.GetConfig do +end diff --git a/lib/openflow/get_config/reply.ex b/lib/openflow/get_config/reply.ex new file mode 100644 index 0000000..db1870d --- /dev/null +++ b/lib/openflow/get_config/reply.ex @@ -0,0 +1,26 @@ +defmodule Openflow.GetConfig.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + flags: [], # default = "normal" is no special handling + miss_send_len: 128 + ) + + alias __MODULE__ + + def ofp_type, do: 8 + + def read(<>) do + flags = Openflow.Enums.int_to_flags(flags_int, :config_flags) + miss_send_len = Openflow.Utils.get_enum(miss_send_len0, :controller_max_len) + %Reply{flags: flags, miss_send_len: miss_send_len} + end + + def to_binary(%Reply{flags: flags, miss_send_len: miss_send_len0}) do + flags_int = Openflow.Enums.flags_to_int(flags, :config_flags) + miss_send_len = Openflow.Utils.get_enum(miss_send_len0, :controller_max_len) + <> + end +end diff --git a/lib/openflow/get_config/request.ex b/lib/openflow/get_config/request.ex new file mode 100644 index 0000000..c1ec4d9 --- /dev/null +++ b/lib/openflow/get_config/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.GetConfig.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0 # virtual field + ) + + alias __MODULE__ + + def ofp_type, do: 7 + + def new do + %Request{} + end + + def read(_) do + %Request{} + end + + def to_binary(%Request{}) do + <<>> + end +end diff --git a/lib/openflow/group_mod.ex b/lib/openflow/group_mod.ex new file mode 100644 index 0000000..276e9af --- /dev/null +++ b/lib/openflow/group_mod.ex @@ -0,0 +1,49 @@ +defmodule Openflow.GroupMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + command: :add, + type: :all, + group_id: 0, + buckets: [] + ) + + alias __MODULE__ + + def ofp_type, do: 15 + + def new(options \\ []) do + command = Keyword.get(options, :command, :add) + type = Keyword.get(options, :type, :all) + group_id = Keyword.get(options, :group_id, 0) + buckets = Keyword.get(options, :buckets, []) + %GroupMod{command: command, + type: type, + group_id: group_id, + buckets: buckets} + end + + def read(<>) do + command = Openflow.Utils.get_enum(command_int, :group_mod_command) + type = Openflow.Utils.get_enum(type_int, :group_type) + group_id = Openflow.Utils.get_enum(group_id_int, :group_id) + buckets = Openflow.Bucket.read(buckets_bin) + %GroupMod{command: command, + type: type, + group_id: group_id, + buckets: buckets} + end + + def to_binary(%GroupMod{command: command, + type: type, + group_id: group_id, + buckets: buckets}) do + command_int = Openflow.Utils.get_enum(command, :group_mod_command) + type_int = Openflow.Utils.get_enum(type, :group_type) + group_id_int = Openflow.Utils.get_enum(group_id, :group_id) + buckets_bin = Openflow.Bucket.to_binary(buckets) + <> + end +end diff --git a/lib/openflow/hello.ex b/lib/openflow/hello.ex new file mode 100644 index 0000000..a3f4a75 --- /dev/null +++ b/lib/openflow/hello.ex @@ -0,0 +1,103 @@ +defmodule Openflow.Hello do + @moduledoc "OpenFlow Hello codec module" + + import Bitwise + + @ofp_hello_size 4 + + defstruct(version: 4, xid: 0, elements: []) + + alias __MODULE__ + + def ofp_type, + do: 0 + + def new(version) when is_integer(version) do + %Hello{elements: [versionbitmap: [version]]} + end + def new(versions) when is_list(versions) do + %Hello{elements: [versionbitmap: versions]} + end + + def supported_version?(%Hello{version: 4, elements: []}), do: true + def supported_version?(%Hello{elements: []}), do: false + def supported_version?(%Hello{elements: elements}) do + versionbitmaps = for {:versionbitmap, versions} <- elements, do: versions + Enum.any?(versionbitmaps, fn(versions) -> 4 in versions end) + end + + def read(binary), + do: %Hello{elements: decode([], binary)} + + def to_binary(%Hello{elements: elements}), + do: encode([], elements) + + # private functions + + defp decode(acc, <<>>), do: acc + defp decode(acc, <>) do + data_len = length - @ofp_hello_size + <> = rest + + try do + typeint + |> Openflow.Enums.to_atom(:hello_elem) + |> decode_hello_elem(acc, data) + |> decode(rest2) + catch + :bad_enum -> + decode(acc, rest2) + end + end + + defp encode(acc, []), + do: to_string(acc) + defp encode(acc, [h|rest]), + do: encode([encode_hello_elem(h)|acc], rest) + + defp decode_hello_elem(:versionbitmap, acc, binary), + do: [{:versionbitmap, decode_bitmap([], binary, 0)}|acc] + defp decode_hello_elem(_, acc, _binary), + do: acc + + defp encode_hello_elem({:versionbitmap, versions}) do + bitmap_bin = encode_bitmap(versions) + type_int = Openflow.Enums.to_int(:versionbitmap, :hello_elem) + size_int = @ofp_hello_size + byte_size(bitmap_bin) + <> + end + defp encode_hello_elem(_) do + <<>> + end + + defp decode_bitmap(acc, "", _), do: acc + defp decode_bitmap(acc, <>, base) do + acc + |> decode_bitmap(int, 0, base) + |> decode_bitmap(rest, base + 32) + end + + defp encode_bitmap(list) do + size = + list + |> Enum.max + |> div(32) + encode_bitmap(0, list, size) + end + + defp decode_bitmap(acc, _, index, _) when index >= 32, + do: acc + defp decode_bitmap(acc, int, index, base) when (int &&& (1 <<< index)) == 0, + do: decode_bitmap(acc, int, index + 1, base) + defp decode_bitmap(acc, int, index, base), + do: decode_bitmap([base + index|acc], int, index + 1, base) + + defp encode_bitmap(acc, [], size) do + bytes = (size + 1) * 32 + <> + end + defp encode_bitmap(acc, [h|rest], size) do + index = (size - div(h, 32) * 32 + rem(h, 32)) + encode_bitmap(acc ||| (1 <<< index), rest, size) + end +end diff --git a/lib/openflow/instruction.ex b/lib/openflow/instruction.ex new file mode 100644 index 0000000..c33d631 --- /dev/null +++ b/lib/openflow/instruction.ex @@ -0,0 +1,28 @@ +defmodule Openflow.Instruction do + + def read(instruction_bin) do + do_read([], instruction_bin) + end + + def to_binary(instructions) when is_list(instructions) do + to_binary(<<>>, instructions) + end + def to_binary(instruction) do + to_binary([instruction]) + end + + # private functions + + defp do_read(acc, <<>>), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + codec = Openflow.Enums.to_atom(type, :instruction_type) + do_read([codec.read(instruction_bin)|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [instruction|rest]) do + codec = instruction.__struct__ + to_binary(<>, rest) + end +end diff --git a/lib/openflow/instructions/apply_actions.ex b/lib/openflow/instructions/apply_actions.ex new file mode 100644 index 0000000..b331419 --- /dev/null +++ b/lib/openflow/instructions/apply_actions.ex @@ -0,0 +1,20 @@ +defmodule Openflow.Instruction.ApplyActions do + defstruct(actions: []) + + alias __MODULE__ + + def new(actions) do + %ApplyActions{actions: actions} + end + + def to_binary(%ApplyActions{actions: actions}) do + actions_bin = Openflow.Action.to_binary(actions) + length = 8 + byte_size(actions_bin) + <<4::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>> + end + + def read(<<4::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do + actions = Openflow.Action.read(actions_bin) + %ApplyActions{actions: actions} + end +end diff --git a/lib/openflow/instructions/clear_actions.ex b/lib/openflow/instructions/clear_actions.ex new file mode 100644 index 0000000..6d52c08 --- /dev/null +++ b/lib/openflow/instructions/clear_actions.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Instruction.ClearActions do + defstruct([]) + + alias __MODULE__ + + def new do + %ClearActions{} + end + + def to_binary(%ClearActions{}) do + actions_bin = "" + length = 8 + byte_size(actions_bin) + <<5::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>> + end + + def read(<<5::16, _length::16, _::size(4)-unit(8), _::bytes>>) do + %ClearActions{} + end +end diff --git a/lib/openflow/instructions/experimenter.ex b/lib/openflow/instructions/experimenter.ex new file mode 100644 index 0000000..138b10b --- /dev/null +++ b/lib/openflow/instructions/experimenter.ex @@ -0,0 +1,18 @@ +defmodule Openflow.Instruction.Experimenter do + defstruct(exp_id: 0, data: "") + + alias __MODULE__ + + def new(exp_id, data) do + %Experimenter{exp_id: exp_id, data: data} + end + + def to_binary(%Experimenter{exp_id: exp_id, data: data}) do + length = 8 + byte_size(data) + <<0xffff::16, length::16, exp_id::32, data::bytes>> + end + + def read(<<0xffff::16, _::16, exp_id::32, data::bytes>>) do + %Experimenter{exp_id: exp_id, data: data} + end +end diff --git a/lib/openflow/instructions/goto_table.ex b/lib/openflow/instructions/goto_table.ex new file mode 100644 index 0000000..f78c2c9 --- /dev/null +++ b/lib/openflow/instructions/goto_table.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Instruction.GotoTable do + defstruct(table_id: 0) + + alias __MODULE__ + + def new(table_id) do + %GotoTable{table_id: table_id} + end + + def to_binary(%GotoTable{table_id: table_id}) do + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + <<1::16, 8::16, table_id_int::8, 0::size(3)-unit(8)>> + end + + def read(<<1::16, 8::16, table_id_int::8, _::size(3)-unit(8)>>) do + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + %GotoTable{table_id: table_id} + end +end diff --git a/lib/openflow/instructions/meter.ex b/lib/openflow/instructions/meter.ex new file mode 100644 index 0000000..f038fc9 --- /dev/null +++ b/lib/openflow/instructions/meter.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Instruction.Meter do + defstruct(meter_id: 0) + + alias __MODULE__ + + def new(meter_id) do + %Meter{meter_id: meter_id} + end + + def to_binary(%Meter{meter_id: meter_id}) do + meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) + <<6::16, 8::16, meter_id_int::32>> + end + + def read(<<6::16, _::16, meter_id_int::32>>) do + meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) + %Meter{meter_id: meter_id} + end +end diff --git a/lib/openflow/instructions/write_actions.ex b/lib/openflow/instructions/write_actions.ex new file mode 100644 index 0000000..fb9eeea --- /dev/null +++ b/lib/openflow/instructions/write_actions.ex @@ -0,0 +1,20 @@ +defmodule Openflow.Instruction.WriteActions do + defstruct(actions: []) + + alias __MODULE__ + + def new(actions) do + %WriteActions{actions: actions} + end + + def to_binary(%WriteActions{actions: actions}) do + actions_bin = Openflow.Action.to_binary(actions) + length = 8 + byte_size(actions_bin) + <<3::16, length::16, 0::size(4)-unit(8), actions_bin::bytes>> + end + + def read(<<3::16, _length::16, _::size(4)-unit(8), actions_bin::bytes>>) do + actions = Openflow.Action.read(actions_bin) + %WriteActions{actions: actions} + end +end diff --git a/lib/openflow/instructions/write_metadata.ex b/lib/openflow/instructions/write_metadata.ex new file mode 100644 index 0000000..034e8d7 --- /dev/null +++ b/lib/openflow/instructions/write_metadata.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Instruction.WriteMetadata do + defstruct(metadata: 0, metadata_mask: 0xffffffffffffffff) + + alias __MODULE__ + + def new(options) do + metadata = Keyword.get(options, :metadata, 0) + metadata_mask = Keyword.get(options, :metadata_mask, 0xffffffffffffffff) + %WriteMetadata{metadata: metadata, metadata_mask: metadata_mask} + end + + def to_binary(%WriteMetadata{metadata: metadata, metadata_mask: metadata_mask}) do + <<2::16, 24::16, 0::size(4)-unit(8), metadata::64, metadata_mask::64>> + end + + def read(<<2::16, 24::16, _::size(4)-unit(8), metadata::64, metadata_mask::64>>) do + %WriteMetadata{metadata: metadata, metadata_mask: metadata_mask} + end +end diff --git a/lib/openflow/match.ex b/lib/openflow/match.ex new file mode 100644 index 0000000..baac231 --- /dev/null +++ b/lib/openflow/match.ex @@ -0,0 +1,154 @@ +defmodule Openflow.Match do + @match_size 8 + @header_size 4 + + defstruct([ + type: :oxm, + fields: [] + ]) + + alias __MODULE__ + + def new(fields \\ []) do + oxm_fields = + fields + |> keyword_to_oxm_fields([]) + %Match{type: :oxm, fields: oxm_fields} + end + + def read(binary) do + <<1::16, no_pad_len::16, binary1::binary>> = binary + padding_length = @match_size - rem(no_pad_len, 8) + match_field_len = no_pad_len - @header_size + <> = binary1 + {decode_fields(match_fields, []), rest} + end + + def to_binary(%Match{fields: fields}) do + fields_bin = encode_fields(fields, <<>>) + length = byte_size(fields_bin) + @match_size - @header_size + type_int = Openflow.Enums.to_int(:oxm, :match_type) + padding = Openflow.Utils.padding(length, 8) + <> + end + + def codec_header(oxm_field) when is_atom(oxm_field) do + case Openflow.Match.Field.vendor_of(oxm_field) do + oxm_class when oxm_class in [:nxm_0, :nxm_1, :openflow_basic, :packet_register] -> + oxm_class_int = Openflow.Enums.to_int(oxm_class, :oxm_class) + oxm_field_int = Openflow.Enums.to_int(oxm_field, oxm_class) + oxm_length = div(Openflow.Match.Field.n_bits_of(oxm_field), 8) + <> + experimenter when experimenter in [:nicira_ext_match, :onf_ext_match] -> + oxm_class_int = 0xffff + experimenter_int = Openflow.Enums.to_int(experimenter, :experimenter_oxm_vendors) + oxm_field_int = Openflow.Enums.to_int(oxm_field, experimenter) + oxm_length = div(Openflow.Match.Field.n_bits_of(oxm_field) + 4, 8) + <> + end + end + def codec_header(<>) do + oxm_class = Openflow.Enums.to_atom(oxm_class_int, :oxm_class) + Openflow.Enums.to_atom(oxm_field_int, oxm_class) + end + def codec_header(<<0xffff::16, oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, experimenter_int::32>>) do + experimenter = Openflow.Enums.to_atom(experimenter_int, :experimenter_oxm_vendors) + Openflow.Enums.to_atom(oxm_field_int, experimenter) + end + + def header_size(<<_oxm_class_int::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _::bytes>>), + do: 4 + def header_size(<<0xffff::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _exp_int::32, _::bytes>>), + do: 8 + + # private functions + + defp decode_fields(<<>>, acc), do: Enum.reverse(acc) + defp decode_fields(<<0xffff::16, _::7, 1::1, length::8, vendor_int::32, field_int::16, binary::bytes>>, acc) do + length = length - 6 + field_len = div(length, 2) + <> = binary + experimenter = Openflow.Enums.to_atom(vendor_int, :experimenter_oxm_vendors) + field_name = Openflow.Enums.to_atom(field_int, experimenter) + value = Openflow.Match.Field.codec(value_bin, field_name) + mask = Openflow.Match.Field.codec(mask_bin, field_name) + decode_fields(rest, [{field_name, {value, mask}}|acc]) + end + defp decode_fields(<<0xffff::16, _::7, 0::1, length::8, vendor_int::32, field_int::16, binary::bytes>>, acc) do + length = length - 6 + <> = binary + experimenter = Openflow.Enums.to_atom(vendor_int, :experimenter_oxm_vendors) + field_name = Openflow.Enums.to_atom(field_int, experimenter) + value = Openflow.Match.Field.codec(value_bin, field_name) + decode_fields(rest, [{field_name, value}|acc]) + end + defp decode_fields(<>, acc) do + field_len = div(length, 2) + <> = binary + class = Openflow.Enums.to_atom(class_int, :oxm_class) + field_name = Openflow.Enums.to_atom(field_int, class) + value = Openflow.Match.Field.codec(value_bin, field_name) + mask = Openflow.Match.Field.codec(mask_bin, field_name) + decode_fields(rest, [{field_name, {value, mask}}|acc]) + end + defp decode_fields(<>, acc) do + <> = binary + class = Openflow.Enums.to_atom(class_int, :oxm_class) + field_name = Openflow.Enums.to_atom(field_int, class) + value = Openflow.Match.Field.codec(value_bin, field_name) + decode_fields(rest, [{field_name, value}|acc]) + end + + defp encode_fields([], acc), do: acc + defp encode_fields([field|fields], acc) do + encode_fields(fields, <>) + end + + defp encode_field(%{class: class, field: field, has_mask: true, value: value, mask: mask}) + when class == :nicira_ext_match or class == :onf_ext_match do + vendor_int = Openflow.Enums.to_int(class, :experimenter_oxm_vendors) + field_int = Openflow.Enums.to_int(field, class) + has_mask_int = 1 + length = (byte_size(value) * 2) + 6 + <<0xffff::16, 0::7, has_mask_int::1, length::8, vendor_int::32, field_int::16, value::bytes, mask::bytes>> + end + defp encode_field(%{class: class, field: field, has_mask: false, value: value}) + when class == :nicira_ext_match or class == :onf_ext_match do + vendor_int = Openflow.Enums.to_int(class, :experimenter_oxm_vendors) + field_int = Openflow.Enums.to_int(field, class) + has_mask_int = 0 + length = byte_size(value) + 6 + <<0xffff::16, 0::7, has_mask_int::1, length::8, vendor_int::32, field_int::16, value::bytes>> + end + defp encode_field(%{class: class, field: field, has_mask: true, value: value, mask: mask}) do + class_int = Openflow.Enums.to_int(class, :oxm_class) + field_int = Openflow.Enums.to_int(field, class) + has_mask_int = 1 + length = byte_size(value) * 2 + <> + end + defp encode_field(%{class: class, field: field, has_mask: false, value: value}) do + class_int = Openflow.Enums.to_int(class, :oxm_class) + field_int = Openflow.Enums.to_int(field, class) + has_mask_int = 0 + length = byte_size(value) + <> + end + + defp keyword_to_oxm_fields([], acc), do: Enum.reverse(acc) + defp keyword_to_oxm_fields([{field_name, field_value}|fields], acc) do + keyword_to_oxm_fields(fields, [oxm_field(field_name, field_value)|acc]) + end + + defp oxm_field(field_name, {value, mask}) do + value_bin = Openflow.Match.Field.codec(value, field_name) + mask_bin = Openflow.Match.Field.codec(mask, field_name) + match_class = Openflow.Match.Field.vendor_of(field_name) + %{class: match_class, field: field_name, has_mask: true, value: value_bin, mask: mask_bin} + end + defp oxm_field(field_name, value) do + value_bin = Openflow.Match.Field.codec(value, field_name) + match_class = Openflow.Match.Field.vendor_of(field_name) + %{class: match_class, field: field_name, has_mask: false, value: value_bin} + end +end diff --git a/lib/openflow/match/field.ex b/lib/openflow/match/field.ex new file mode 100644 index 0000000..4c45221 --- /dev/null +++ b/lib/openflow/match/field.ex @@ -0,0 +1,636 @@ +defmodule Openflow.Match.Field do + + def codec(value0, field) when is_binary(value0) do + {type, format} = format_of(field) + n_bits = n_bits_of(field) + bit_size = bit_size(value0) + value = if bit_size < n_bits do + head_pad_len = n_bits - bit_size + <<0::size(head_pad_len), value0::bytes>> + else + if bit_size > n_bits and type != :mac do + head_pad_len = bit_size - n_bits + <<_::size(head_pad_len), value::size(n_bits)-bits>> = value0 + value + else + value0 + end + end + formatting(value, type, format) + end + def codec(value, type) do + {type, format} = format_of(type) + formatting(value, type, format) + end + + def n_bits_of(field) do + field + |> format_of + |> bit_size_of + end + + def bit_size_of({:u8, _}), do: 8 + def bit_size_of({:u24, _}), do: 24 + def bit_size_of({:be16, _}), do: 16 + def bit_size_of({:be32, _}), do: 32 + def bit_size_of({:be64, _}), do: 64 + def bit_size_of({:be128, _}), do: 128 + def bit_size_of({:mac, _}), do: 48 + + # NXM0 + def vendor_of(:nx_in_port), do: :nxm_0 + def vendor_of(:nx_eth_dst), do: :nxm_0 + def vendor_of(:nx_eth_src), do: :nxm_0 + def vendor_of(:nx_eth_type), do: :nxm_0 + def vendor_of(:nx_vlan_tci), do: :nxm_0 + def vendor_of(:nx_ip_tos), do: :nxm_0 + def vendor_of(:nx_ip_proto), do: :nxm_0 + def vendor_of(:nx_ipv4_src), do: :nxm_0 + def vendor_of(:nx_ipv4_dst), do: :nxm_0 + def vendor_of(:nx_tcp_src), do: :nxm_0 + def vendor_of(:nx_tcp_dst), do: :nxm_0 + def vendor_of(:nx_udp_src), do: :nxm_0 + def vendor_of(:nx_udp_dst), do: :nxm_0 + def vendor_of(:nx_icmpv4_type), do: :nxm_0 + def vendor_of(:nx_icmpv4_code), do: :nxm_0 + def vendor_of(:nx_arp_op), do: :nxm_0 + def vendor_of(:nx_arp_spa), do: :nxm_0 + def vendor_of(:nx_arp_tpa), do: :nxm_0 + def vendor_of(:nx_tcp_flags), do: :nxm_0 + + # NXM1 + def vendor_of(:reg0), do: :nxm_1 + def vendor_of(:reg1), do: :nxm_1 + def vendor_of(:reg2), do: :nxm_1 + def vendor_of(:reg3), do: :nxm_1 + def vendor_of(:reg4), do: :nxm_1 + def vendor_of(:reg5), do: :nxm_1 + def vendor_of(:reg6), do: :nxm_1 + def vendor_of(:reg7), do: :nxm_1 + def vendor_of(:reg8), do: :nxm_1 + def vendor_of(:reg9), do: :nxm_1 + def vendor_of(:reg10), do: :nxm_1 + def vendor_of(:reg11), do: :nxm_1 + def vendor_of(:reg12), do: :nxm_1 + def vendor_of(:reg13), do: :nxm_1 + def vendor_of(:reg14), do: :nxm_1 + def vendor_of(:reg15), do: :nxm_1 + def vendor_of(:tun_id), do: :nxm_1 + def vendor_of(:nx_arp_sha), do: :nxm_1 + def vendor_of(:nx_arp_tha), do: :nxm_1 + def vendor_of(:nx_ipv6_src), do: :nxm_1 + def vendor_of(:nx_ipv6_dst), do: :nxm_1 + def vendor_of(:nx_icmpv6_type), do: :nxm_1 + def vendor_of(:nx_icmpv6_code), do: :nxm_1 + def vendor_of(:nx_ipv6_nd_target), do: :nxm_1 + def vendor_of(:nx_ipv6_nd_sll), do: :nxm_1 + def vendor_of(:nx_ipv6_nd_tll), do: :nxm_1 + def vendor_of(:nx_ip_frag), do: :nxm_1 + def vendor_of(:nx_ipv6_label), do: :nxm_1 + def vendor_of(:nx_ip_ecn), do: :nxm_1 + def vendor_of(:nx_ip_ttl), do: :nxm_1 + def vendor_of(:nx_mpls_ttl), do: :nxm_1 + def vendor_of(:tun_src), do: :nxm_1 + def vendor_of(:tun_dst), do: :nxm_1 + def vendor_of(:pkt_mark), do: :nxm_1 + def vendor_of(:dp_hash), do: :nxm_1 + def vendor_of(:recirc_id), do: :nxm_1 + def vendor_of(:conj_id), do: :nxm_1 + def vendor_of(:nx_tun_gbp_id), do: :nxm_1 + def vendor_of(:nx_tun_gbp_flags), do: :nxm_1 + def vendor_of(:tun_metadata0), do: :nxm_1 + def vendor_of(:tun_metadata1), do: :nxm_1 + def vendor_of(:tun_metadata2), do: :nxm_1 + def vendor_of(:tun_metadata3), do: :nxm_1 + def vendor_of(:tun_metadata4), do: :nxm_1 + def vendor_of(:tun_metadata5), do: :nxm_1 + def vendor_of(:tun_metadata6), do: :nxm_1 + def vendor_of(:tun_metadata7), do: :nxm_1 + def vendor_of(:tun_metadata8), do: :nxm_1 + def vendor_of(:tun_metadata9), do: :nxm_1 + def vendor_of(:tun_metadata10), do: :nxm_1 + def vendor_of(:tun_metadata11), do: :nxm_1 + def vendor_of(:tun_metadata12), do: :nxm_1 + def vendor_of(:tun_metadata13), do: :nxm_1 + def vendor_of(:tun_metadata14), do: :nxm_1 + def vendor_of(:tun_metadata15), do: :nxm_1 + def vendor_of(:tun_metadata16), do: :nxm_1 + def vendor_of(:tun_metadata17), do: :nxm_1 + def vendor_of(:tun_metadata18), do: :nxm_1 + def vendor_of(:tun_metadata19), do: :nxm_1 + def vendor_of(:tun_metadata20), do: :nxm_1 + def vendor_of(:tun_metadata21), do: :nxm_1 + def vendor_of(:tun_metadata22), do: :nxm_1 + def vendor_of(:tun_metadata23), do: :nxm_1 + def vendor_of(:tun_metadata24), do: :nxm_1 + def vendor_of(:tun_metadata25), do: :nxm_1 + def vendor_of(:tun_metadata26), do: :nxm_1 + def vendor_of(:tun_metadata27), do: :nxm_1 + def vendor_of(:tun_metadata28), do: :nxm_1 + def vendor_of(:tun_metadata29), do: :nxm_1 + def vendor_of(:tun_metadata30), do: :nxm_1 + def vendor_of(:tun_metadata31), do: :nxm_1 + def vendor_of(:tun_metadata32), do: :nxm_1 + def vendor_of(:tun_metadata33), do: :nxm_1 + def vendor_of(:tun_metadata34), do: :nxm_1 + def vendor_of(:tun_metadata35), do: :nxm_1 + def vendor_of(:tun_metadata36), do: :nxm_1 + def vendor_of(:tun_metadata37), do: :nxm_1 + def vendor_of(:tun_metadata38), do: :nxm_1 + def vendor_of(:tun_metadata39), do: :nxm_1 + def vendor_of(:tun_metadata40), do: :nxm_1 + def vendor_of(:tun_metadata41), do: :nxm_1 + def vendor_of(:tun_metadata42), do: :nxm_1 + def vendor_of(:tun_metadata43), do: :nxm_1 + def vendor_of(:tun_metadata44), do: :nxm_1 + def vendor_of(:tun_metadata45), do: :nxm_1 + def vendor_of(:tun_metadata46), do: :nxm_1 + def vendor_of(:tun_metadata47), do: :nxm_1 + def vendor_of(:tun_metadata48), do: :nxm_1 + def vendor_of(:tun_metadata49), do: :nxm_1 + def vendor_of(:tun_metadata50), do: :nxm_1 + def vendor_of(:tun_metadata51), do: :nxm_1 + def vendor_of(:tun_metadata52), do: :nxm_1 + def vendor_of(:tun_metadata53), do: :nxm_1 + def vendor_of(:tun_metadata54), do: :nxm_1 + def vendor_of(:tun_metadata55), do: :nxm_1 + def vendor_of(:tun_metadata56), do: :nxm_1 + def vendor_of(:tun_metadata57), do: :nxm_1 + def vendor_of(:tun_metadata58), do: :nxm_1 + def vendor_of(:tun_metadata59), do: :nxm_1 + def vendor_of(:tun_metadata60), do: :nxm_1 + def vendor_of(:tun_metadata61), do: :nxm_1 + def vendor_of(:tun_metadata62), do: :nxm_1 + def vendor_of(:tun_metadata63), do: :nxm_1 + def vendor_of(:tun_flags), do: :nxm_1 + def vendor_of(:ct_state), do: :nxm_1 + def vendor_of(:ct_zone), do: :nxm_1 + def vendor_of(:ct_mark), do: :nxm_1 + def vendor_of(:ct_label), do: :nxm_1 + def vendor_of(:tun_ipv6_src), do: :nxm_1 + def vendor_of(:tun_ipv6_dst), do: :nxm_1 + def vendor_of(:xxreg0), do: :nxm_1 + def vendor_of(:xxreg1), do: :nxm_1 + def vendor_of(:xxreg2), do: :nxm_1 + def vendor_of(:xxreg3), do: :nxm_1 + def vendor_of(:xxreg4), do: :nxm_1 + def vendor_of(:xxreg5), do: :nxm_1 + def vendor_of(:xxreg6), do: :nxm_1 + def vendor_of(:xxreg7), do: :nxm_1 + def vendor_of(:ct_ip_proto), do: :nxm_1 + def vendor_of(:ct_ipv4_src), do: :nxm_1 + def vendor_of(:ct_ipv4_dst), do: :nxm_1 + def vendor_of(:ct_ipv6_src), do: :nxm_1 + def vendor_of(:ct_ipv6_dst), do: :nxm_1 + def vendor_of(:ct_tp_src), do: :nxm_1 + def vendor_of(:ct_tp_dst), do: :nxm_1 + + # OpenFlow Basic + def vendor_of(:in_port), do: :openflow_basic + def vendor_of(:in_phy_port), do: :openflow_basic + def vendor_of(:metadata), do: :openflow_basic + def vendor_of(:eth_dst), do: :openflow_basic + def vendor_of(:eth_src), do: :openflow_basic + def vendor_of(:eth_type), do: :openflow_basic + def vendor_of(:vlan_vid), do: :openflow_basic + def vendor_of(:vlan_pcp), do: :openflow_basic + def vendor_of(:ip_dscp), do: :openflow_basic + def vendor_of(:ip_ecn), do: :openflow_basic + def vendor_of(:ip_proto), do: :openflow_basic + def vendor_of(:ipv4_src), do: :openflow_basic + def vendor_of(:ipv4_dst), do: :openflow_basic + def vendor_of(:tcp_src), do: :openflow_basic + def vendor_of(:tcp_dst), do: :openflow_basic + def vendor_of(:udp_src), do: :openflow_basic + def vendor_of(:udp_dst), do: :openflow_basic + def vendor_of(:sctp_src), do: :openflow_basic + def vendor_of(:sctp_dst), do: :openflow_basic + def vendor_of(:icmpv4_type), do: :openflow_basic + def vendor_of(:icmpv4_code), do: :openflow_basic + def vendor_of(:arp_op), do: :openflow_basic + def vendor_of(:arp_spa), do: :openflow_basic + def vendor_of(:arp_tpa), do: :openflow_basic + def vendor_of(:arp_sha), do: :openflow_basic + def vendor_of(:arp_tha), do: :openflow_basic + def vendor_of(:ipv6_src), do: :openflow_basic + def vendor_of(:ipv6_dst), do: :openflow_basic + def vendor_of(:ipv6_flabel), do: :openflow_basic + def vendor_of(:icmpv6_type), do: :openflow_basic + def vendor_of(:icmpv6_code), do: :openflow_basic + def vendor_of(:ipv6_nd_target), do: :openflow_basic + def vendor_of(:ipv6_nd_sll), do: :openflow_basic + def vendor_of(:ipv6_nd_tll), do: :openflow_basic + def vendor_of(:mpls_label), do: :openflow_basic + def vendor_of(:mpls_tc), do: :openflow_basic + def vendor_of(:mpls_bos), do: :openflow_basic + def vendor_of(:pbb_isid), do: :openflow_basic + def vendor_of(:tunnel_id), do: :openflow_basic + def vendor_of(:ipv6_exthdr), do: :openflow_basic + def vendor_of(:pbb_uca), do: :openflow_basic + def vendor_of(:packet_type), do: :openflow_basic + def vendor_of(:gre_flags), do: :openflow_basic + def vendor_of(:gre_ver), do: :openflow_basic + def vendor_of(:gre_protocol), do: :openflow_basic + def vendor_of(:gre_key), do: :openflow_basic + def vendor_of(:gre_seqnum), do: :openflow_basic + def vendor_of(:lisp_flags), do: :openflow_basic + def vendor_of(:lisp_nonce), do: :openflow_basic + def vendor_of(:lisp_id), do: :openflow_basic + def vendor_of(:vxlan_flags), do: :openflow_basic + def vendor_of(:vxlan_vni), do: :openflow_basic + def vendor_of(:mpls_data_first_nibble), do: :openflow_basic + def vendor_of(:mpls_ach_version), do: :openflow_basic + def vendor_of(:mpls_ach_channel), do: :openflow_basic + def vendor_of(:mpls_pw_metadata), do: :openflow_basic + def vendor_of(:mpls_cw_flags), do: :openflow_basic + def vendor_of(:mpls_cw_fragment), do: :openflow_basic + def vendor_of(:mpls_cw_len), do: :openflow_basic + def vendor_of(:mpls_cw_seq_num), do: :openflow_basic + def vendor_of(:gtpu_flags), do: :openflow_basic + def vendor_of(:gtpu_ver), do: :openflow_basic + def vendor_of(:gtpu_msg_type), do: :openflow_basic + def vendor_of(:gtpu_teid), do: :openflow_basic + def vendor_of(:gtpu_extn_hdr), do: :openflow_basic + def vendor_of(:gtpu_extn_udp_port), do: :openflow_basic + def vendor_of(:gtpu_extn_sci), do: :openflow_basic + + # Packet Register + def vendor_of(:xreg0), do: :packet_register + def vendor_of(:xreg1), do: :packet_register + def vendor_of(:xreg2), do: :packet_register + def vendor_of(:xreg3), do: :packet_register + def vendor_of(:xreg4), do: :packet_register + def vendor_of(:xreg5), do: :packet_register + def vendor_of(:xreg6), do: :packet_register + def vendor_of(:xreg7), do: :packet_register + + # Nicira Ext Match + def vendor_of(:nsh_flags), do: :nicira_ext_match + def vendor_of(:nsh_mdtype), do: :nicira_ext_match + def vendor_of(:nsh_np), do: :nicira_ext_match + def vendor_of(:nsh_spi), do: :nicira_ext_match + def vendor_of(:nsh_si), do: :nicira_ext_match + def vendor_of(:nsh_c1), do: :nicira_ext_match + def vendor_of(:nsh_c2), do: :nicira_ext_match + def vendor_of(:nsh_c3), do: :nicira_ext_match + def vendor_of(:nsh_c4), do: :nicira_ext_match + + # ONF Ext Match + def vendor_of(:onf_tcp_flags), do: :onf_ext_match + def vendor_of(:onf_actset_output), do: :onf_ext_match + def vendor_of(:onf_pbb_uca), do: :onf_ext_match + + # NXM0 + def format_of(:nx_in_port), do: {:be16, :openflow10_port} + def format_of(:nx_eth_dst), do: {:mac, :ethernet} + def format_of(:nx_eth_src), do: {:mac, :ethernet} + def format_of(:nx_eth_type), do: {:be16, :hexadecimal} + def format_of(:nx_vlan_tci), do: {:be16, :hexadecimal} + def format_of(:nx_ip_tos), do: {:u8, :decimal} + def format_of(:nx_ip_proto), do: {:u8, :decimal} + def format_of(:nx_ipv4_src), do: {:be32, :ipv4} + def format_of(:nx_ipv4_dst), do: {:be32, :ipv4} + def format_of(:nx_tcp_src), do: {:be16, :decimal} + def format_of(:nx_tcp_dst), do: {:be16, :decimal} + def format_of(:nx_udp_src), do: {:be16, :decimal} + def format_of(:nx_udp_dst), do: {:be16, :decimal} + def format_of(:nx_icmpv4_type), do: {:u8, :decimal} + def format_of(:nx_icmpv4_code), do: {:u8, :decimal} + def format_of(:nx_arp_op), do: {:be16, :decimal} + def format_of(:nx_arp_spa), do: {:be32, :ipv4} + def format_of(:nx_arp_tpa), do: {:be32, :ipv4} + def format_of(:nx_tcp_flags), do: {:be16, :tcp_flags} + + # NXM1 + def format_of(:reg0), do: {:be32, :hexadecimal} + def format_of(:reg1), do: {:be32, :hexadecimal} + def format_of(:reg2), do: {:be32, :hexadecimal} + def format_of(:reg3), do: {:be32, :hexadecimal} + def format_of(:reg4), do: {:be32, :hexadecimal} + def format_of(:reg5), do: {:be32, :hexadecimal} + def format_of(:reg6), do: {:be32, :hexadecimal} + def format_of(:reg7), do: {:be32, :hexadecimal} + def format_of(:reg8), do: {:be32, :hexadecimal} + def format_of(:reg9), do: {:be32, :hexadecimal} + def format_of(:reg10), do: {:be32, :hexadecimal} + def format_of(:reg11), do: {:be32, :hexadecimal} + def format_of(:reg12), do: {:be32, :hexadecimal} + def format_of(:reg13), do: {:be32, :hexadecimal} + def format_of(:reg14), do: {:be32, :hexadecimal} + def format_of(:reg15), do: {:be32, :hexadecimal} + def format_of(:tun_id), do: {:be64, :hexadecimal} + def format_of(:nx_arp_sha), do: {:mac, :ethernet} + def format_of(:nx_arp_tha), do: {:mac, :ethernet} + def format_of(:nx_ipv6_src), do: {:be128, :ipv6} + def format_of(:nx_ipv6_dst), do: {:be128, :ipv6} + def format_of(:nx_icmpv6_type), do: {:u8, :decimal} + def format_of(:nx_icmpv6_code), do: {:u8, :decimal} + def format_of(:nx_ipv6_nd_target), do: {:be128, :ipv6} + def format_of(:nx_ipv6_nd_sll), do: {:mac, :ethernet} + def format_of(:nx_ipv6_nd_tll), do: {:mac, :ethernet} + def format_of(:nx_ip_frag), do: {:u8, :decimal} + def format_of(:nx_ipv6_label), do: {:be32, :hexadecimal} + def format_of(:nx_ip_ecn), do: {:u8, :decimal} + def format_of(:nx_ip_ttl), do: {:u8, :decimal} + def format_of(:nx_mpls_ttl), do: {:u8, :decimal} + def format_of(:tun_src), do: {:be32, :ipv4} + def format_of(:tun_dst), do: {:be32, :ipv4} + def format_of(:pkt_mark), do: {:be32, :hexadecimal} + def format_of(:dp_hash), do: {:be32, :hexadecimal} + def format_of(:recirc_id), do: {:be32, :hexadecimal} + def format_of(:conj_id), do: {:be32, :hexadecimal} + def format_of(:nx_tun_gbp_id), do: {:be16, :decimal} + def format_of(:nx_tun_gbp_flags), do: {:u8, :decimal} + def format_of(:tun_metadata0), do: {:dynamic, :bytes} + def format_of(:tun_metadata1), do: {:dynamic, :bytes} + def format_of(:tun_metadata2), do: {:dynamic, :bytes} + def format_of(:tun_metadata3), do: {:dynamic, :bytes} + def format_of(:tun_metadata4), do: {:dynamic, :bytes} + def format_of(:tun_metadata5), do: {:dynamic, :bytes} + def format_of(:tun_metadata6), do: {:dynamic, :bytes} + def format_of(:tun_metadata7), do: {:dynamic, :bytes} + def format_of(:tun_metadata8), do: {:dynamic, :bytes} + def format_of(:tun_metadata9), do: {:dynamic, :bytes} + def format_of(:tun_metadata10), do: {:dynamic, :bytes} + def format_of(:tun_metadata11), do: {:dynamic, :bytes} + def format_of(:tun_metadata12), do: {:dynamic, :bytes} + def format_of(:tun_metadata13), do: {:dynamic, :bytes} + def format_of(:tun_metadata14), do: {:dynamic, :bytes} + def format_of(:tun_metadata15), do: {:dynamic, :bytes} + def format_of(:tun_metadata16), do: {:dynamic, :bytes} + def format_of(:tun_metadata17), do: {:dynamic, :bytes} + def format_of(:tun_metadata18), do: {:dynamic, :bytes} + def format_of(:tun_metadata19), do: {:dynamic, :bytes} + def format_of(:tun_metadata20), do: {:dynamic, :bytes} + def format_of(:tun_metadata21), do: {:dynamic, :bytes} + def format_of(:tun_metadata22), do: {:dynamic, :bytes} + def format_of(:tun_metadata23), do: {:dynamic, :bytes} + def format_of(:tun_metadata24), do: {:dynamic, :bytes} + def format_of(:tun_metadata25), do: {:dynamic, :bytes} + def format_of(:tun_metadata26), do: {:dynamic, :bytes} + def format_of(:tun_metadata27), do: {:dynamic, :bytes} + def format_of(:tun_metadata28), do: {:dynamic, :bytes} + def format_of(:tun_metadata29), do: {:dynamic, :bytes} + def format_of(:tun_metadata30), do: {:dynamic, :bytes} + def format_of(:tun_metadata31), do: {:dynamic, :bytes} + def format_of(:tun_metadata32), do: {:dynamic, :bytes} + def format_of(:tun_metadata33), do: {:dynamic, :bytes} + def format_of(:tun_metadata34), do: {:dynamic, :bytes} + def format_of(:tun_metadata35), do: {:dynamic, :bytes} + def format_of(:tun_metadata36), do: {:dynamic, :bytes} + def format_of(:tun_metadata37), do: {:dynamic, :bytes} + def format_of(:tun_metadata38), do: {:dynamic, :bytes} + def format_of(:tun_metadata39), do: {:dynamic, :bytes} + def format_of(:tun_metadata40), do: {:dynamic, :bytes} + def format_of(:tun_metadata41), do: {:dynamic, :bytes} + def format_of(:tun_metadata42), do: {:dynamic, :bytes} + def format_of(:tun_metadata43), do: {:dynamic, :bytes} + def format_of(:tun_metadata44), do: {:dynamic, :bytes} + def format_of(:tun_metadata45), do: {:dynamic, :bytes} + def format_of(:tun_metadata46), do: {:dynamic, :bytes} + def format_of(:tun_metadata47), do: {:dynamic, :bytes} + def format_of(:tun_metadata48), do: {:dynamic, :bytes} + def format_of(:tun_metadata49), do: {:dynamic, :bytes} + def format_of(:tun_metadata50), do: {:dynamic, :bytes} + def format_of(:tun_metadata51), do: {:dynamic, :bytes} + def format_of(:tun_metadata52), do: {:dynamic, :bytes} + def format_of(:tun_metadata53), do: {:dynamic, :bytes} + def format_of(:tun_metadata54), do: {:dynamic, :bytes} + def format_of(:tun_metadata55), do: {:dynamic, :bytes} + def format_of(:tun_metadata56), do: {:dynamic, :bytes} + def format_of(:tun_metadata57), do: {:dynamic, :bytes} + def format_of(:tun_metadata58), do: {:dynamic, :bytes} + def format_of(:tun_metadata59), do: {:dynamic, :bytes} + def format_of(:tun_metadata60), do: {:dynamic, :bytes} + def format_of(:tun_metadata61), do: {:dynamic, :bytes} + def format_of(:tun_metadata62), do: {:dynamic, :bytes} + def format_of(:tun_metadata63), do: {:dynamic, :bytes} + def format_of(:tun_flags), do: {:be16, :decimal} + def format_of(:ct_state), do: {:be32, :ct_state} + def format_of(:ct_zone), do: {:be16, :hexadecimal} + def format_of(:ct_mark), do: {:be32, :hexadecimal} + def format_of(:ct_label), do: {:be128, :hexadecimal} + def format_of(:tun_ipv6_src), do: {:be128, :ipv6} + def format_of(:tun_ipv6_dst), do: {:be128, :ipv6} + def format_of(:xxreg0), do: {:be128, :hexadecimal} + def format_of(:xxreg1), do: {:be128, :hexadecimal} + def format_of(:xxreg2), do: {:be128, :hexadecimal} + def format_of(:xxreg3), do: {:be128, :hexadecimal} + def format_of(:xxreg4), do: {:be128, :hexadecimal} + def format_of(:xxreg5), do: {:be128, :hexadecimal} + def format_of(:xxreg6), do: {:be128, :hexadecimal} + def format_of(:xxreg7), do: {:be128, :hexadecimal} + def format_of(:ct_ip_proto), do: {:u8, :decimal} + def format_of(:ct_ipv4_src), do: {:be32, :ipv4} + def format_of(:ct_ipv4_dst), do: {:be32, :ipv4} + def format_of(:ct_ipv6_src), do: {:be128, :ipv6} + def format_of(:ct_ipv6_dst), do: {:be128, :ipv6} + def format_of(:ct_tp_src), do: {:be16, :decimal} + def format_of(:ct_tp_dst), do: {:be16, :decimal} + + # OpenFlow Basic + def format_of(:in_port), do: {:be32, :openflow13_port} + def format_of(:in_phy_port), do: {:be32, :openflow13_port} + def format_of(:metadata), do: {:be64, :hexadecimal} + def format_of(:eth_dst), do: {:mac, :ethernet} + def format_of(:eth_src), do: {:mac, :ethernet} + def format_of(:eth_type), do: {:be16, :hexadecimal} + def format_of(:vlan_vid), do: {:be16, :hexadecimal} + def format_of(:vlan_pcp), do: {:u8, :decimal} + def format_of(:ip_dscp), do: {:u8, :decimal} + def format_of(:ip_ecn), do: {:u8, :decimal} + def format_of(:ip_proto), do: {:u8, :decimal} + def format_of(:ipv4_src), do: {:be32, :ipv4} + def format_of(:ipv4_dst), do: {:be32, :ipv4} + def format_of(:tcp_src), do: {:be16, :decimal} + def format_of(:tcp_dst), do: {:be16, :decimal} + def format_of(:udp_src), do: {:be16, :decimal} + def format_of(:udp_dst), do: {:be16, :decimal} + def format_of(:sctp_src), do: {:be16, :decimal} + def format_of(:sctp_dst), do: {:be16, :decimal} + def format_of(:icmpv4_type), do: {:u8, :decimal} + def format_of(:icmpv4_code), do: {:u8, :decimal} + def format_of(:arp_op), do: {:be16, :decimal} + def format_of(:arp_spa), do: {:be32, :ipv4} + def format_of(:arp_tpa), do: {:be32, :ipv4} + def format_of(:arp_sha), do: {:mac, :ethernet} + def format_of(:arp_tha), do: {:mac, :ethernet} + def format_of(:ipv6_src), do: {:be128, :ipv6} + def format_of(:ipv6_dst), do: {:be128, :ipv6} + def format_of(:ipv6_flabel), do: {:be32, :hexadecimal} + def format_of(:icmpv6_type), do: {:u8, :decimal} + def format_of(:icmpv6_code), do: {:u8, :decimal} + def format_of(:ipv6_nd_target), do: {:be128, :ipv6} + def format_of(:ipv6_nd_sll), do: {:mac, :ethernet} + def format_of(:ipv6_nd_tll), do: {:mac, :ethernet} + def format_of(:mpls_label), do: {:be32, :decimal} + def format_of(:mpls_tc), do: {:u8, :decimal} + def format_of(:mpls_bos), do: {:u8, :decimal} + def format_of(:pbb_isid), do: {:u24, :decimal} + def format_of(:tunnel_id), do: {:be64, :hexadecimal} + def format_of(:ipv6_exthdr), do: {:be16, :ipv6exthdr_flags} + def format_of(:pbb_uca), do: {:u8, :decimal} + def format_of(:packet_type), do: {:be32, :decimal} + def format_of(:gre_flags), do: {:be16, :decimal} + def format_of(:gre_ver), do: {:u8, :decimal} + def format_of(:gre_protocol), do: {:be16, :decimal} + def format_of(:gre_key), do: {:be32, :decimal} + def format_of(:gre_seqnum), do: {:be32, :decimal} + def format_of(:lisp_flags), do: {:u8, :decimal} + def format_of(:lisp_nonce), do: {:u24, :decimal} + def format_of(:lisp_id), do: {:be32, :decimal} + def format_of(:vxlan_flags), do: {:u8, :decimal} + def format_of(:vxlan_vni), do: {:u24, :decimal} + def format_of(:mpls_data_first_nibble), do: {:u8, :decimal} + def format_of(:mpls_ach_version), do: {:u8, :decimal} + def format_of(:mpls_ach_channel), do: {:be16, :decimal} + def format_of(:mpls_pw_metadata), do: {:u8, :decimal} + def format_of(:mpls_cw_flags), do: {:u8, :decimal} + def format_of(:mpls_cw_fragment), do: {:u8, :decimal} + def format_of(:mpls_cw_len), do: {:u8, :decimal} + def format_of(:mpls_cw_seq_num), do: {:be16, :decimal} + def format_of(:gtpu_flags), do: {:u8, :decimal} + def format_of(:gtpu_ver), do: {:u8, :decimal} + def format_of(:gtpu_msg_type), do: {:u8, :decimal} + def format_of(:gtpu_teid), do: {:be32, :decimal} + def format_of(:gtpu_extn_hdr), do: {:u8, :decimal} + def format_of(:gtpu_extn_udp_port), do: {:be16, :decimal} + def format_of(:gtpu_extn_sci), do: {:be16, :decimal} + + # Packet Register + def format_of(:xreg0), do: {:be64, :hexadecimal} + def format_of(:xreg1), do: {:be64, :hexadecimal} + def format_of(:xreg2), do: {:be64, :hexadecimal} + def format_of(:xreg3), do: {:be64, :hexadecimal} + def format_of(:xreg4), do: {:be64, :hexadecimal} + def format_of(:xreg5), do: {:be64, :hexadecimal} + def format_of(:xreg6), do: {:be64, :hexadecimal} + def format_of(:xreg7), do: {:be64, :hexadecimal} + + # Nicira Ext Match + def format_of(:nsh_flags), do: {:u8, :decimal} + def format_of(:nsh_mdtype), do: {:u8, :decimal} + def format_of(:nsh_np), do: {:u8, :decimal} + def format_of(:nsh_spi), do: {:be32, :decimal} + def format_of(:nsh_si), do: {:be32, :decimal} + def format_of(:nsh_c1), do: {:be32, :decimal} + def format_of(:nsh_c2), do: {:be32, :decimal} + def format_of(:nsh_c3), do: {:be32, :decimal} + def format_of(:nsh_c4), do: {:be32, :decimal} + + # ONF Ext Match + def format_of(:onf_tcp_flags), do: {:be16, :tcp_flags} + def format_of(:onf_actset_output), do: {:be32, :openflow13_port} + def format_of(:onf_pbb_uca), do: {:u8, :decimal} + + # Formatting = decimal + def formatting(<>, :u8, :decimal), do: value + def formatting(value, :u8, :decimal) when is_integer(value), do: <> + def formatting(<>, :be16, :decimal), do: value + def formatting(value, :be16, :decimal) when is_integer(value), do: <> + def formatting(<>, :u24, :decimal), do: value + def formatting(value, :u24, :decimal) when is_integer(value), do: <> + def formatting(<>, :be32, :decimal), do: value + def formatting(value, :be32, :decimal) when is_integer(value), do: <> + def formatting(<>, :be64, :decimal), do: value + def formatting(value, :be64, :decimal) when is_integer(value), do: <> + def formatting(<>, :be128, :decimal), do: value + def formatting(value, :be128, :decimal) when is_integer(value), do: <> + + # Formatting = hexadecimal + def formatting(<>, :be16, :hexadecimal), do: value + def formatting(value, :be16, :hexadecimal) when is_integer(value), do: <> + + def formatting(<>, :u24, :hexadecimal), do: value + def formatting(value, :u24, :hexadecimal) when is_integer(value), do: <> + + def formatting(<>, :be32, :hexadecimal), do: value + def formatting(value, :be32, :hexadecimal) when is_integer(value), do: <> + + def formatting(<>, :be64, :hexadecimal), do: value + def formatting(value, :be64, :hexadecimal) when is_integer(value), do: <> + + def formatting(<>, :be128, :hexadecimal), do: value + def formatting(value, :be128, :hexadecimal) when is_integer(value), do: <> + + # Formatting = ethernet + def formatting(<>, :mac, :ethernet), do: Openflow.Utils.to_hex_string(value) + def formatting(value, :mac, :ethernet), do: <<(String.to_integer(value, 16))::48>> + + # Formatting = IPv4 + def formatting(<>, :be32, :ipv4), do: {a1, a2, a3, a4} + def formatting({a1, a2, a3, a4}, :be32, :ipv4), do: <> + + # Formatting = IPv6 + def formatting(<>, :be128, :ipv6) do + {a1, a2, a3, a4, a5, a6, a7, a8} + end + def formatting({a1, a2, a3, a4, a5, a6, a7, a8},:be128, :ipv6) do + <> + end + + # Formatting = OpenFlow 1.0 port + def formatting(<>, :be16, :openflow10_port) do + try do + Openflow.Enums.to_atom(value, :openflow10_port_no) + catch + :bad_enum -> value + end + end + def formatting(value, :be16, :openflow10_port) do + port_no = + try do + Openflow.Enums.to_int(value, :openflow10_port_no) + catch + :bad_enum -> value + end + <> + end + + # Formatting = OpenFlow 1.3 port + def formatting(<>, :be32, :openflow13_port) do + try do + Openflow.Enums.to_atom(value, :openflow13_port_no) + catch + :bad_enum -> value + end + end + def formatting(value, :be32, :openflow13_port) do + port_no = + try do + Openflow.Enums.to_int(value, :openflow13_port_no) + catch + :bad_enum -> value + end + <> + end + + # TCP flags + def formatting(<>, :be16, :tcp_flags) do + Openflow.Enums.int_to_flags(value, :tcp_flags) + end + def formatting(value, :be16, :tcp_flags) do + <<(Openflow.Enums.flags_to_int(value, :tcp_flags))::16>> + end + + # CT State + def formatting(<>, :be32, :ct_state) do + Openflow.Enums.int_to_flags(value, :ct_state_flags) + end + def formatting(value, :be32, :ct_state) do + <<(Openflow.Enums.flags_to_int(value, :ct_state_flags))::32>> + end + + # CT State + def formatting(<>, :be16, :ipv6exthdr_flags) do + Openflow.Enums.int_to_flags(value, :ipv6exthdr_flags) + end + def formatting(value, :be16, :ipv6exthdr_flags) do + <<(Openflow.Enums.flags_to_int(value, :ipv6exthdr_flags))::16>> + end + + # Other + def formatting(value, _, _) do + value + end +end diff --git a/lib/openflow/meter_band.ex b/lib/openflow/meter_band.ex new file mode 100644 index 0000000..cbc4805 --- /dev/null +++ b/lib/openflow/meter_band.ex @@ -0,0 +1,28 @@ +defmodule Openflow.MeterBand do + + def read(meter_band_bin) do + do_read([], meter_band_bin) + end + + def to_binary(meter_bands) when is_list(meter_bands) do + to_binary(<<>>, meter_bands) + end + def to_binary(meter_band) do + to_binary([meter_band]) + end + + # private functions + + defp do_read(acc, <<>>), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + codec = Openflow.Enums.to_atom(type, :meter_band_type) + do_read([codec.read(meter_band_bin)|acc], rest) + end + + defp to_binary(acc, []), do: acc + defp to_binary(acc, [meter_band|rest]) do + codec = meter_band.__struct__ + to_binary(<>, rest) + end +end diff --git a/lib/openflow/meter_band/drop.ex b/lib/openflow/meter_band/drop.ex new file mode 100644 index 0000000..6336652 --- /dev/null +++ b/lib/openflow/meter_band/drop.ex @@ -0,0 +1,22 @@ +defmodule Openflow.MeterBand.Drop do + defstruct( + rate: 0, + burst_size: 0 + ) + + alias __MODULE__ + + def new(options) do + rate = Keyword.get(options, :rate, 0) + burst_size = Keyword.get(options, :burst_size, 0) + %Drop{rate: rate, burst_size: burst_size} + end + + def read(<<1::16, 16::16, rate::32, burst_size::32, _::size(4)-unit(8)>>) do + %Drop{rate: rate, burst_size: burst_size} + end + + def to_binary(%Drop{rate: rate, burst_size: burst_size}) do + <<1::16, 16::16, rate::32, burst_size::32, 0::size(4)-unit(8)>> + end +end diff --git a/lib/openflow/meter_band/experimenter.ex b/lib/openflow/meter_band/experimenter.ex new file mode 100644 index 0000000..fa70d4d --- /dev/null +++ b/lib/openflow/meter_band/experimenter.ex @@ -0,0 +1,24 @@ +defmodule Openflow.MeterBand.Experimenter do + defstruct( + rate: 0, + burst_size: 0, + experimenter: 0, + ) + + alias __MODULE__ + + def new(options) do + rate = Keyword.get(options, :rate, 0) + burst_size = Keyword.get(options, :burst_size, 0) + experimenter = Keyword.get(options, :experimenter, 0) + %Experimenter{rate: rate, burst_size: burst_size, experimenter: experimenter} + end + + def read(<<0xffff::16, _::16, rate::32, burst_size::32, experimenter::32>>) do + %Experimenter{rate: rate, burst_size: burst_size, experimenter: experimenter} + end + + def to_binary(%Experimenter{rate: rate, burst_size: burst_size, experimenter: experimenter}) do + <<0xffff::16, 16::16, rate::32, burst_size::32, experimenter::32>> + end +end diff --git a/lib/openflow/meter_band/remark.ex b/lib/openflow/meter_band/remark.ex new file mode 100644 index 0000000..6e0f4bf --- /dev/null +++ b/lib/openflow/meter_band/remark.ex @@ -0,0 +1,24 @@ +defmodule Openflow.MeterBand.Remark do + defstruct( + rate: 0, + burst_size: 0, + prec_level: 0 + ) + + alias __MODULE__ + + def new(options) do + rate = Keyword.get(options, :rate, 0) + burst_size = Keyword.get(options, :burst_size, 0) + prec_level = Keyword.get(options, :prec_level, 0) + %Remark{rate: rate, burst_size: burst_size, prec_level: prec_level} + end + + def read(<<2::16, 16::16, rate::32, burst_size::32, prec_level::8, _::size(3)-unit(8)>>) do + %Remark{rate: rate, burst_size: burst_size, prec_level: prec_level} + end + + def to_binary(%Remark{rate: rate, burst_size: burst_size, prec_level: prec_level}) do + <<2::16, 16::16, rate::32, burst_size::32, prec_level::8, 0::size(3)-unit(8)>> + end +end diff --git a/lib/openflow/meter_mod.ex b/lib/openflow/meter_mod.ex new file mode 100644 index 0000000..24aab63 --- /dev/null +++ b/lib/openflow/meter_mod.ex @@ -0,0 +1,32 @@ +defmodule Openflow.MeterMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + command: :add, + flags: [], + meter_id: 0, + bands: [] + ) + + alias __MODULE__ + + def ofp_type, do: 29 + + def read(<>) do + command = Openflow.Enums.to_atom(command_int, :meter_mod_command) + flags = Openflow.Enums.int_to_flags(flags_int, :meter_flags) + meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) + bands = Openflow.MeterBand.read(bands_bin) + %MeterMod{command: command, flags: flags, meter_id: meter_id, bands: bands} + end + + def to_binary(%MeterMod{command: command, flags: flags, meter_id: meter_id, bands: bands}) do + command_int = Openflow.Enums.to_int(command, :meter_mod_command) + flags_int = Openflow.Enums.flags_to_int(flags, :meter_flags) + meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) + bands_bin = Openflow.MeterBand.to_binary(bands) + <> + end +end diff --git a/lib/openflow/multipart/aggregate/reply.ex b/lib/openflow/multipart/aggregate/reply.ex new file mode 100644 index 0000000..bcc208f --- /dev/null +++ b/lib/openflow/multipart/aggregate/reply.ex @@ -0,0 +1,19 @@ +defmodule Openflow.Multipart.Aggregate.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + packet_count: 0, + byte_count: 0, + flow_count: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def read(<>) do + %Reply{packet_count: packet_count, byte_count: byte_count, flow_count: flow_count} + end +end diff --git a/lib/openflow/multipart/aggregate/request.ex b/lib/openflow/multipart/aggregate/request.ex new file mode 100644 index 0000000..e7fa041 --- /dev/null +++ b/lib/openflow/multipart/aggregate/request.ex @@ -0,0 +1,67 @@ +defmodule Openflow.Multipart.Aggregate.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + table_id: :all, + out_port: :any, + out_group: :any, + cookie: 0, + cookie_mask: 0, + match: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(options) do + table_id = Keyword.get(options, :table_id, :all) + out_port = Keyword.get(options, :out_port, :any) + out_group = Keyword.get(options, :out_group, :any) + cookie = Keyword.get(options, :cookie, 0) + cookie_mask = Keyword.get(options, :cookie, 0) + match = Keyword.get(options, :match, []) + %Request{table_id: table_id, + out_port: out_port, + out_group: out_group, + cookie: cookie, + cookie_mask: cookie_mask, + match: match} + end + + def read(<>) do + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + out_port = Openflow.Utils.get_enum(out_port_int, :openflow13_port_no) + out_group = Openflow.Utils.get_enum(out_group_int, :group_id) + {match, _rest} = Openflow.Match.read(match_bin) + %Request{table_id: table_id, + out_port: out_port, + out_group: out_group, + cookie: cookie, + cookie_mask: cookie_mask, + match: match} + end + + def to_binary(%Request{table_id: table_id, + out_port: out_port, + out_group: out_group, + cookie: cookie, + cookie_mask: cookie_mask, + match: match} = msg) do + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + out_port_int = Openflow.Utils.get_enum(out_port, :openflow13_port_no) + out_group_int = Openflow.Utils.get_enum(out_group, :group_id) + match_bin = Openflow.Match.to_binary(match) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/desc/reply.ex b/lib/openflow/multipart/desc/reply.ex new file mode 100644 index 0000000..db3c818 --- /dev/null +++ b/lib/openflow/multipart/desc/reply.ex @@ -0,0 +1,30 @@ +defmodule Openflow.Multipart.Desc.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + mfr_desc: "", + hw_desc: "", + sw_desc: "", + serial_num: "", + dp_desc: "" + ) + + alias __MODULE__ + + @desc_str_len 256 + @serial_num_len 32 + + def ofp_type, do: 19 + + def read(<>) do + %Reply{mfr_desc: Openflow.Utils.decode_string(mfr_desc), + hw_desc: Openflow.Utils.decode_string(hw_desc), + sw_desc: Openflow.Utils.decode_string(sw_desc), + serial_num: Openflow.Utils.decode_string(serial_num), + dp_desc: Openflow.Utils.decode_string(dp_desc)} + end +end diff --git a/lib/openflow/multipart/desc/request.ex b/lib/openflow/multipart/desc/request.ex new file mode 100644 index 0000000..ffd55e2 --- /dev/null +++ b/lib/openflow/multipart/desc/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.Desc.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new do + %Request{} + end + + def read("") do + %Request{} + end + + def to_binary(%Request{} = msg) do + Openflow.Multipart.Request.header(msg) + end +end diff --git a/lib/openflow/multipart/flow/reply.ex b/lib/openflow/multipart/flow/reply.ex new file mode 100644 index 0000000..5d41dbc --- /dev/null +++ b/lib/openflow/multipart/flow/reply.ex @@ -0,0 +1,75 @@ +defmodule Openflow.Multipart.Flow.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + flows: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(flows \\ []) do + %Reply{flows: flows} + end + + def read(<>) do + flows = Openflow.Multipart.FlowStats.read(flows_bin) + %Reply{flows: flows} + end +end + +defmodule Openflow.Multipart.FlowStats do + defstruct( + table_id: 0, + duration_sec: 0, + duration_nsec: 0, + priority: 0, + idle_timeout: 0, + hard_timeout: 0, + flags: 0, + cookie: 0, + packet_count: 0, + byte_count: 0, + match: [], + instructions: [] + ) + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + do_read([codec(flow_stats_bin)|acc], rest) + end + + defp codec(<<_length::16, table_id_int::8, 0::8, duration_sec::32, + duration_nsec::32, priority::16, idle::16, hard::16, + flags_int::16, _::size(4)-unit(8), cookie::64, + packet_count::64, byte_count::64, tail::bytes>>) do + {match, instructions_bin} = Openflow.Match.read(tail) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + flags = Openflow.Enums.int_to_flags(flags_int, :flow_mod_flags) + instructions = Openflow.Instruction.read(instructions_bin) + %FlowStats{table_id: table_id, + duration_sec: duration_sec, + duration_nsec: duration_nsec, + priority: priority, + idle_timeout: idle, + hard_timeout: hard, + flags: flags, + cookie: cookie, + packet_count: packet_count, + byte_count: byte_count, + match: match, + instructions: instructions} + end +end diff --git a/lib/openflow/multipart/flow/request.ex b/lib/openflow/multipart/flow/request.ex new file mode 100644 index 0000000..9485c63 --- /dev/null +++ b/lib/openflow/multipart/flow/request.ex @@ -0,0 +1,67 @@ +defmodule Openflow.Multipart.Flow.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + table_id: :all, + out_port: :any, + out_group: :any, + cookie: 0, + cookie_mask: 0, + match: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(options) do + table_id = Keyword.get(options, :table_id, :all) + out_port = Keyword.get(options, :out_port, :any) + out_group = Keyword.get(options, :out_group, :any) + cookie = Keyword.get(options, :cookie, 0) + cookie_mask = Keyword.get(options, :cookie, 0) + match = Keyword.get(options, :match, []) + %Request{table_id: table_id, + out_port: out_port, + out_group: out_group, + cookie: cookie, + cookie_mask: cookie_mask, + match: match} + end + + def read(<>) do + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + out_port = Openflow.Utils.get_enum(out_port_int, :openflow13_port_no) + out_group = Openflow.Utils.get_enum(out_group_int, :group_id) + {match, _rest} = Openflow.Match.read(match_bin) + %Request{table_id: table_id, + out_port: out_port, + out_group: out_group, + cookie: cookie, + cookie_mask: cookie_mask, + match: match} + end + + def to_binary(%Request{table_id: table_id, + out_port: out_port, + out_group: out_group, + cookie: cookie, + cookie_mask: cookie_mask, + match: match} = msg) do + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + out_port_int = Openflow.Utils.get_enum(out_port, :openflow13_port_no) + out_group_int = Openflow.Utils.get_enum(out_group, :group_id) + match_bin = Openflow.Match.to_binary(match) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/group/ *Minibuf-1* b/lib/openflow/multipart/group/ *Minibuf-1* new file mode 100644 index 0000000..04e6d8d --- /dev/null +++ b/lib/openflow/multipart/group/ *Minibuf-1* @@ -0,0 +1 @@ +Replace regexp Queue with: Group \ No newline at end of file diff --git a/lib/openflow/multipart/group/.# *Minibuf-1* b/lib/openflow/multipart/group/.# *Minibuf-1* new file mode 120000 index 0000000..7cdb737 --- /dev/null +++ b/lib/openflow/multipart/group/.# *Minibuf-1* @@ -0,0 +1 @@ +shun159@shun159.8967:1509553730 \ No newline at end of file diff --git a/lib/openflow/multipart/group/reply.ex b/lib/openflow/multipart/group/reply.ex new file mode 100644 index 0000000..8d96406 --- /dev/null +++ b/lib/openflow/multipart/group/reply.ex @@ -0,0 +1,69 @@ +defmodule Openflow.Multipart.Group.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + groups: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(groups \\ []) do + %Reply{groups: groups} + end + + def read(<>) do + groups = Openflow.Multipart.Group.read(groups_bin) + %Reply{groups: groups} + end +end + +defmodule Openflow.Multipart.Group do + defstruct( + group_id: 0, + ref_count: 0, + packet_count: 0, + byte_count: 0, + duration_sec: 0, + duration_nsec: 0, + bucket_stats: [] + ) + + @ofp_group_stats_size 40 + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + do_read([codec(group_bin)|acc], rest) + end + + defp codec(<>) do + bucket_stats_size = length - @ofp_group_stats_size + <> = tail + bucket_stats = for <> do + %{packet_count: packet_count, byte_count: byte_count} + end + %Group{group_id: group_id, + ref_count: ref_count, + packet_count: packet_count, + byte_count: byte_count, + duration_sec: duration_sec, + duration_nsec: duration_nsec, + bucket_stats: bucket_stats} + end +end diff --git a/lib/openflow/multipart/group/request.ex b/lib/openflow/multipart/group/request.ex new file mode 100644 index 0000000..288123a --- /dev/null +++ b/lib/openflow/multipart/group/request.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Multipart.Group.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + group_id: :all + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(group_id \\ :all) do + %Request{group_id: group_id} + end + + def read(<>) do + group_id = Openflow.Utils.get_enum(group_id_int, :group_id) + %Request{group_id: group_id} + end + + def to_binary(%Request{group_id: group_id} = msg) do + group_id_int = Openflow.Utils.get_enum(group_id, :group_id) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/group_desc/reply.ex b/lib/openflow/multipart/group_desc/reply.ex new file mode 100644 index 0000000..49bd412 --- /dev/null +++ b/lib/openflow/multipart/group_desc/reply.ex @@ -0,0 +1,54 @@ +defmodule Openflow.Multipart.GroupDesc.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + groups: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(groups \\ []) do + %Reply{groups: groups} + end + + def read(<>) do + groups = Openflow.Multipart.GroupDescStats.read(groups_bin) + %Reply{groups: groups} + end +end + +defmodule Openflow.Multipart.GroupDescStats do + defstruct( + type: :all, + group_id: 0, + buckets: [] + ) + + alias __MODULE__ + + @ofp_group_desc_size 8 + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + do_read([codec(group_stats_bin)|acc], rest) + end + + defp codec(<>) do + buckets_bin_len = length - @ofp_group_desc_size + <> = tail + type = Openflow.Enums.to_atom(type_int, :group_type) + buckets = Openflow.Bucket.read(buckets_bin) + %GroupDescStats{type: type, group_id: group_id, buckets: buckets} + end +end diff --git a/lib/openflow/multipart/group_desc/request.ex b/lib/openflow/multipart/group_desc/request.ex new file mode 100644 index 0000000..6f7f390 --- /dev/null +++ b/lib/openflow/multipart/group_desc/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.GroupDesc.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new do + %Request{} + end + + def read("") do + %Request{} + end + + def to_binary(%Request{} = msg) do + Openflow.Multipart.Request.header(msg) + end +end diff --git a/lib/openflow/multipart/group_features/reply.ex b/lib/openflow/multipart/group_features/reply.ex new file mode 100644 index 0000000..498d9ea --- /dev/null +++ b/lib/openflow/multipart/group_features/reply.ex @@ -0,0 +1,44 @@ +defmodule Openflow.Multipart.GroupFeatures.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + types: 0, + capabilities: [], + max_groups_for_all: 0, + max_groups_for_select: 0, + max_groups_for_indirect: 0, + max_groups_for_fast_failover: 0, + actions_for_all: 0, + actions_for_select: 0, + actions_for_indirect: 0, + actions_for_fast_failover: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def read(<>) do + capabilities = Openflow.Enums.int_to_flags(capabilities_int, :group_capabilities) + %Reply{types: types_int, + capabilities: capabilities, + max_groups_for_all: max_groups_for_all_int, + max_groups_for_select: max_groups_for_select_int, + max_groups_for_indirect: max_groups_for_indirect_int, + max_groups_for_fast_failover: max_groups_for_fast_failover_int, + actions_for_all: actions_for_all_int, + actions_for_select: actions_for_select_int, + actions_for_indirect: actions_for_indirect_int, + actions_for_fast_failover: actions_for_fast_failover_int} + end +end diff --git a/lib/openflow/multipart/group_features/request.ex b/lib/openflow/multipart/group_features/request.ex new file mode 100644 index 0000000..c51278b --- /dev/null +++ b/lib/openflow/multipart/group_features/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.GroupFeatures.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new do + %Request{} + end + + def read("") do + %Request{} + end + + def to_binary(%Request{} = msg) do + Openflow.Multipart.Request.header(msg) + end +end diff --git a/lib/openflow/multipart/meter/reply.ex b/lib/openflow/multipart/meter/reply.ex new file mode 100644 index 0000000..bafd74f --- /dev/null +++ b/lib/openflow/multipart/meter/reply.ex @@ -0,0 +1,63 @@ +defmodule Openflow.Multipart.Meter.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + meters: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def read(<>) do + meters = Openflow.Multipart.Meter.read(meters_bin) + %Reply{meters: meters} + end +end + +defmodule Openflow.Multipart.Meter do + defstruct( + meter_id: 0, + flow_count: 0, + packet_in_count: 0, + byte_in_count: 0, + duration_sec: 0, + duration_nsec: 0, + band_stats: [] + ) + + @ofp_meter_stats_size 40 + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <<_::32, length::16, _binary::bytes>> = binary) do + <> = binary + do_read([codec(meter_bin)|acc], rest) + end + + defp codec(<>) do + band_stats_size = length - @ofp_meter_stats_size + <> = tail + band_stats = for <> do + %{packet_band_count: packet_band_count,byte_band_count: byte_band_count} + end + %Meter{meter_id: meter_id, + flow_count: flow_count, + packet_in_count: packet_in_count, + byte_in_count: byte_in_count, + duration_sec: duration_sec, + duration_nsec: duration_nsec, + band_stats: band_stats} + end +end diff --git a/lib/openflow/multipart/meter/request.ex b/lib/openflow/multipart/meter/request.ex new file mode 100644 index 0000000..d9f1944 --- /dev/null +++ b/lib/openflow/multipart/meter/request.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Multipart.Meter.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + meter_id: :all + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(meter_id \\ :all) do + %Request{meter_id: meter_id} + end + + def read(<>) do + meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) + %Request{meter_id: meter_id} + end + + def to_binary(%Request{meter_id: meter_id} = msg) do + meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/meter_config/request.ex b/lib/openflow/multipart/meter_config/request.ex new file mode 100644 index 0000000..ced8c02 --- /dev/null +++ b/lib/openflow/multipart/meter_config/request.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Multipart.MeterConfig.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + meter_id: :all + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(meter_id \\ :all) do + %Request{meter_id: meter_id} + end + + def read(<>) do + meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) + %Request{meter_id: meter_id} + end + + def to_binary(%Request{meter_id: meter_id} = msg) do + meter_id_int = Openflow.Utils.get_enum(meter_id, :meter_id) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/port_stats/reply.ex b/lib/openflow/multipart/port_stats/reply.ex new file mode 100644 index 0000000..e51d0a3 --- /dev/null +++ b/lib/openflow/multipart/port_stats/reply.ex @@ -0,0 +1,78 @@ +defmodule Openflow.Multipart.PortStats.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + ports: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(ports \\ []) do + %Reply{ports: ports} + end + + def read(<>) do + ports = Openflow.Multipart.PortStats.read(ports_bin) + %Reply{ports: ports} + end +end + +defmodule Openflow.Multipart.PortStats do + defstruct( + port_number: 0, + rx_packets: 0, + tx_packets: 0, + rx_bytes: 0, + tx_bytes: 0, + rx_dropped: 0, + tx_dropped: 0, + rx_errors: 0, + tx_errors: 0, + rx_frame_err: 0, + rx_over_err: 0, + rx_crc_err: 0, + collisions: 0, + duration_sec: 0, + duration_nsec: 0 + ) + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do + do_read([codec(port_stats_bin)|acc], rest) + end + + defp codec(<>) do + %PortStats{port_number: port_no, + rx_packets: rx_packets, + tx_packets: tx_packets, + rx_bytes: rx_bytes, + tx_bytes: tx_bytes, + rx_dropped: rx_dropped, + tx_dropped: tx_dropped, + rx_errors: rx_errors, + tx_errors: tx_errors, + rx_frame_err: rx_frame_err, + rx_over_err: rx_over_err, + rx_crc_err: rx_crc_err, + collisions: collisions, + duration_sec: duration_sec, + duration_nsec: duration_nsec} + end +end diff --git a/lib/openflow/multipart/port_stats/request.ex b/lib/openflow/multipart/port_stats/request.ex new file mode 100644 index 0000000..e432be6 --- /dev/null +++ b/lib/openflow/multipart/port_stats/request.ex @@ -0,0 +1,29 @@ +defmodule Openflow.Multipart.PortStats.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + port_number: :any + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(port_no \\ :any) do + %Request{port_number: port_no} + end + + def read(<>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + %Request{port_number: port_no} + end + + def to_binary(%Request{port_number: port_no} = msg) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/queue/reply.ex b/lib/openflow/multipart/queue/reply.ex new file mode 100644 index 0000000..53d3284 --- /dev/null +++ b/lib/openflow/multipart/queue/reply.ex @@ -0,0 +1,59 @@ +defmodule Openflow.Multipart.Queue.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + queues: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(queues \\ []) do + %Reply{queues: queues} + end + + def read(<>) do + queues = Openflow.Multipart.Queue.read(queues_bin) + %Reply{queues: queues} + end +end + +defmodule Openflow.Multipart.Queue do + defstruct( + port_number: 0, + queue_id: 0, + tx_bytes: 0, + tx_packets: 0, + tx_errors: 0, + duration_sec: 0, + duration_nsec: 0 + ) + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do + do_read([codec(queue_bin)|acc], rest) + end + + defp codec(<>) do + %Queue{port_number: port_no, + queue_id: queue_id, + tx_bytes: tx_bytes, + tx_packets: tx_packets, + tx_errors: tx_errors, + duration_sec: duration_sec, + duration_nsec: duration_nsec} + end +end diff --git a/lib/openflow/multipart/queue/request.ex b/lib/openflow/multipart/queue/request.ex new file mode 100644 index 0000000..654a407 --- /dev/null +++ b/lib/openflow/multipart/queue/request.ex @@ -0,0 +1,34 @@ +defmodule Openflow.Multipart.Queue.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + port_number: :any, + queue_id: :all + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(options) do + port_no = Keyword.get(options, :port_number, :any) + queue_id = Keyword.get(options, :queue_id, :all) + %Request{port_number: port_no, queue_id: queue_id} + end + + def read(<>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + queue_id = Openflow.Utils.get_enum(queue_id_int, :queue_id) + %Request{port_number: port_no, queue_id: queue_id} + end + + def to_binary(%Request{port_number: port_no, queue_id: queue_id} = msg) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no) + queue_id_int = Openflow.Utils.get_enum(queue_id, :queue_id) + body_bin = <> + header_bin = Openflow.Multipart.Request.header(msg) + <> + end +end diff --git a/lib/openflow/multipart/reply.ex b/lib/openflow/multipart/reply.ex new file mode 100644 index 0000000..ad446cc --- /dev/null +++ b/lib/openflow/multipart/reply.ex @@ -0,0 +1,27 @@ +defmodule Openflow.Multipart.Reply do + def ofp_type, do: 19 + + def read(<>) do + codec = Openflow.Enums.to_atom(type_int, :multipart_reply_codec) + flags = Openflow.Enums.int_to_flags(flags_int, :multipart_reply_flags) + reply = codec.read(reply_bin) + %{reply|flags: flags} + end + + def to_binary(%{__struct__: codec, flags: flags} = msg) do + flags_int = Openflow.Enums.flags_to_int(flags, :multipart_reply_flags) + type_int = Openflow.Enums.to_int(codec, :multipart_reply_codec) + case codec.to_binary(msg) do + reply_bin when is_binary(reply_bin) -> + <> + {:error, reason} -> + {:error, reason} + end + end + + def header(%{__struct__: codec, flags: flags}) do + flags_int = Openflow.Enums.flags_to_int(flags, :multipart_reply_flags) + type_int = Openflow.Enums.to_int(codec, :multipart_reply_codec) + <> + end +end diff --git a/lib/openflow/multipart/request.ex b/lib/openflow/multipart/request.ex new file mode 100644 index 0000000..4f6f454 --- /dev/null +++ b/lib/openflow/multipart/request.ex @@ -0,0 +1,27 @@ +defmodule Openflow.Multipart.Request do + def ofp_type, do: 18 + + def read(<>) do + codec = Openflow.Enums.to_atom(type_int, :multipart_request_codec) + flags = Openflow.Enums.int_to_flags(flags_int, :multipart_request_flags) + request = codec.read(request_bin) + %{request|flags: flags} + end + + def to_binary(%{__struct__: codec, flags: flags} = msg) do + flags_int = Openflow.Enums.flags_to_int(flags, :multipart_request_flags) + type_int = Openflow.Enums.to_int(codec, :multipart_request_codec) + case codec.to_binary(msg) do + request_bin when is_binary(request_bin) -> + <> + {:error, reason} -> + {:error, reason} + end + end + + def header(%{__struct__: codec, flags: flags}) do + flags_int = Openflow.Enums.flags_to_int(flags, :multipart_request_flags) + type_int = Openflow.Enums.to_int(codec, :multipart_request_codec) + <> + end +end diff --git a/lib/openflow/multipart/table/reply.ex b/lib/openflow/multipart/table/reply.ex new file mode 100644 index 0000000..496450b --- /dev/null +++ b/lib/openflow/multipart/table/reply.ex @@ -0,0 +1,45 @@ +defmodule Openflow.Multipart.Table.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [], + tables: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def read(<>) do + tables = Openflow.Multipart.TableStats.read(tables_bin) + %Reply{tables: tables} + end +end + +defmodule Openflow.Multipart.TableStats do + defstruct( + table_id: 0, + active_count: 0, + lookup_count: 0, + matched_count: 0 + ) + + alias __MODULE__ + + def read(binary) do + do_read([], binary) + end + + # private functions + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do + do_read([codec(table_stats_bin)|acc], rest) + end + + defp codec(<>) do + %TableStats{table_id: table_id, active_count: active_count, + lookup_count: lookup_count, matched_count: matched_count} + end +end diff --git a/lib/openflow/multipart/table/request.ex b/lib/openflow/multipart/table/request.ex new file mode 100644 index 0000000..8607068 --- /dev/null +++ b/lib/openflow/multipart/table/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.Table.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + flags: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new do + %Request{} + end + + def read("") do + %Request{} + end + + def to_binary(%Request{} = msg) do + Openflow.Multipart.Request.header(msg) + end +end diff --git a/lib/openflow/nx_packet_in2.ex b/lib/openflow/nx_packet_in2.ex new file mode 100644 index 0000000..1ba67e0 --- /dev/null +++ b/lib/openflow/nx_packet_in2.ex @@ -0,0 +1,34 @@ +defmodule Openflow.NxPacketIn2 do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + # packet_in properties: + packet: nil, + full_len: nil, + buffer_id: nil, + table_id: nil, + cookie: nil, + reason: nil, + metadata: nil, + userdata: nil, + continuation: nil, + # continuation properties: + continuation_bridge: nil, + continuation_stack: nil, + continuation_conntracked: nil, + continuation_table_id: nil, + continuation_cookie: nil, + continuation_actions: nil, + continuation_action_set: nil + ) + + alias __MODULE__ + + @experimenter 0x00002320 + @nx_type 30 + + def ofp_type, do: 4 + +end diff --git a/lib/openflow/nx_packet_in_format.ex b/lib/openflow/nx_packet_in_format.ex new file mode 100644 index 0000000..c5f2dd5 --- /dev/null +++ b/lib/openflow/nx_packet_in_format.ex @@ -0,0 +1,30 @@ +defmodule Openflow.NxSetPacketInFormat do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + format: :standard + ) + + @experimenter 0x00002320 + @nx_type 16 + + alias __MODULE__ + + def ofp_type, do: 4 + + def new(format \\ :standard) do + %NxSetPacketInFormat{format: format} + end + + def read(<<@experimenter::32, @nx_type::32, format_int::32>>) do + format = Openflow.Enums.to_atom(format_int, :packet_in_format) + %NxSetPacketInFormat{format: format} + end + + def to_binary(%NxSetPacketInFormat{format: format}) do + format_int = Openflow.Enums.to_int(format, :packet_in_format) + <<@experimenter::32, @nx_type::32, format_int::32>> + end +end diff --git a/lib/openflow/nx_set_controller_id.ex b/lib/openflow/nx_set_controller_id.ex new file mode 100644 index 0000000..de9c95e --- /dev/null +++ b/lib/openflow/nx_set_controller_id.ex @@ -0,0 +1,26 @@ +defmodule Openflow.NxSetControllerId do + defstruct( + version: 4, + xid: 0, + id: 0 + ) + + @experimenter 0x00002320 + @nx_type 20 + + alias __MODULE__ + + def ofp_type, do: 4 + + def new(controller_id) do + %NxSetControllerId{id: controller_id} + end + + def read(<<@experimenter::32, @nx_type::32, _::size(6)-unit(8), controller_id::16>>) do + %NxSetControllerId{id: controller_id} + end + + def to_binary(%NxSetControllerId{id: controller_id}) do + <<@experimenter::32, @nx_type::32, 0::size(6)-unit(8), controller_id::16>> + end +end diff --git a/lib/openflow/packet_in.ex b/lib/openflow/packet_in.ex new file mode 100644 index 0000000..415aa92 --- /dev/null +++ b/lib/openflow/packet_in.ex @@ -0,0 +1,60 @@ +defmodule Openflow.PacketIn do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + buffer_id: :no_buffer, + total_len: 0, + reason: :no_match, + table_id: 0, + cookie: 0, + in_port: :controller, + match: [], + data: "" + ) + + alias __MODULE__ + + def ofp_type, do: 10 + + def read(<>) do + buffer_id = Openflow.Utils.get_enum(buffer_id_int, :buffer_id) + reason = Openflow.Utils.get_enum(reason_int, :packet_in_reason) + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + {match_fields0, rest1} = Openflow.Match.read(rest0) + <<_pad::size(2)-unit(8), data::bytes>> = rest1 + in_port = Keyword.get(match_fields0, :in_port, :any) + match_fields = Keyword.delete(match_fields0, :in_port) + %PacketIn{buffer_id: buffer_id, + total_len: total_len, + reason: reason, + table_id: table_id, + cookie: cookie, + in_port: in_port, + match: match_fields, + data: data} + end + + def to_binary(%PacketIn{} = packet_in) do + %PacketIn{buffer_id: buffer_id, + total_len: total_len, + reason: reason, + table_id: table_id, + cookie: cookie, + in_port: in_port, + match: match_fields, + data: data} = packet_in + buffer_id_int = Openflow.Utils.get_enum(buffer_id, :buffer_id) + reason_int = Openflow.Utils.get_enum(reason, :packet_in_reason) + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + match_fields_bin = + [{:in_port, in_port}|match_fields] + |> Openflow.Match.new + |> Openflow.Match.to_binary + <> + end +end diff --git a/lib/openflow/packet_out.ex b/lib/openflow/packet_out.ex new file mode 100644 index 0000000..dcadd43 --- /dev/null +++ b/lib/openflow/packet_out.ex @@ -0,0 +1,51 @@ +defmodule Openflow.PacketOut do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + buffer_id: :no_buffer, + in_port: :controller, + actions: [], + data: "" + ) + + alias __MODULE__ + + def ofp_type, do: 13 + + def new(options) do + buffer_id = Keyword.get(options, :buffer_id, :no_buffer) + in_port = Keyword.get(options, :in_port, :controller) + actions = Keyword.get(options, :actions, []) + data = Keyword.get(options, :data, "") + %PacketOut{buffer_id: buffer_id, + in_port: in_port, + actions: actions, + data: data} + end + + def read(<>) do + buffer_id = Openflow.Utils.get_enum(buffer_id_int, :buffer_id) + in_port = Openflow.Utils.get_enum(in_port_int, :openflow13_port_no) + actions = Openflow.Action.read(actions_bin) + %PacketOut{buffer_id: buffer_id, + in_port: in_port, + actions: actions, + data: data} + end + + def to_binary(%PacketOut{} = packet_out) do + %PacketOut{buffer_id: buffer_id, + in_port: in_port, + actions: actions, + data: data} = packet_out + buffer_id_int = Openflow.Utils.get_enum(buffer_id, :buffer_id) + in_port_int = Openflow.Utils.get_enum(in_port, :openflow13_port_no) + actions_bin = Openflow.Action.to_binary(actions) + actions_len = byte_size(actions_bin) + <> + end +end diff --git a/lib/openflow/port.ex b/lib/openflow/port.ex new file mode 100644 index 0000000..1f71180 --- /dev/null +++ b/lib/openflow/port.ex @@ -0,0 +1,47 @@ +defmodule Openflow.Port do + defstruct( + number: 0, + hw_addr: "000000000000", + name: "", + config: [], + state: [], + current_features: [], + advertised_features: [], + supported_features: [], + peer_features: [], + current_speed: :"10mb_hd", + max_speed: :"10mb_hd" + ) + + @ofp_max_port_name_len 16 + + alias __MODULE__ + + def read(<>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + hw_addr = Openflow.Utils.to_hex_string(hw_addr_bin) + name = Openflow.Utils.decode_string(name_bin) + config = Openflow.Enums.int_to_flags(config_int, :port_config) + state = Openflow.Enums.int_to_flags(state_int, :port_state) + curr = Openflow.Enums.int_to_flags(curr_int, :port_features) + adv = Openflow.Enums.int_to_flags(advertised_int, :port_features) + supp = Openflow.Enums.int_to_flags(supp_int, :port_features) + peer = Openflow.Enums.int_to_flags(peer_int, :port_features) + %Port{number: port_no, + hw_addr: hw_addr, + name: name, + config: config, + state: state, + current_features: curr, + advertised_features: adv, + supported_features: supp, + peer_features: peer, + current_speed: curr_speed, + max_speed: max_speed} + end +end diff --git a/lib/openflow/port_mod.ex b/lib/openflow/port_mod.ex new file mode 100644 index 0000000..01506f5 --- /dev/null +++ b/lib/openflow/port_mod.ex @@ -0,0 +1,48 @@ +defmodule Openflow.PortMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + number: 0, + hw_addr: "000000000000", + config: [], + mask: [], + advertise: [] + ) + + alias __MODULE__ + + def ofp_type, do: 16 + + def new(options \\ []) do + port_no = Keyword.get(options, :number, :all) + hw_addr = Keyword.get(options, :hw_adddr, "000000000000") + config = Keyword.get(options, :config, []) + mask = Keyword.get(options, :mask, []) + advertise = Keyword.get(options, :advertise, []) + %PortMod{number: port_no, hw_addr: hw_addr, config: config, mask: mask, advertise: advertise} + end + + def read(<>) do + port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) + hw_addr = Openflow.Utils.to_hex_string(hw_addr_bin) + config = Openflow.Enums.int_to_flags(config_int, :port_config) + mask = Openflow.Enums.int_to_flags(mask_int, :port_config) + adv = Openflow.Enums.int_to_flags(advertised_int, :port_features) + %PortMod{number: port_no, hw_addr: hw_addr, config: config, mask: mask, advertise: adv} + end + + def to_binary(%PortMod{number: port_no, hw_addr: hw_addr, config: config, mask: mask, advertise: adv}) do + port_no_int = Openflow.Utils.get_enum(port_no, :openflow13_port_no) + hw_addr_bin = <<(String.to_integer(hw_addr, 16))::48>> + config_int = Openflow.Enums.flags_to_int(config, :port_config) + mask_int = Openflow.Enums.flags_to_int(mask, :port_config) + advertised_int = Openflow.Enums.flags_to_int(adv, :port_features) + <> + end +end diff --git a/lib/openflow/port_status.ex b/lib/openflow/port_status.ex new file mode 100644 index 0000000..83ea182 --- /dev/null +++ b/lib/openflow/port_status.ex @@ -0,0 +1,20 @@ +defmodule Openflow.PortStatus do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + reason: :add, + port: nil + ) + + alias __MODULE__ + + def ofp_type, do: 12 + + def read(<>) do + reason = Openflow.Enums.to_atom(reason_int, :port_reason) + port = Openflow.Port.read(port_bin) + %PortStatus{reason: reason, port: port} + end +end diff --git a/lib/openflow/role/reply.ex b/lib/openflow/role/reply.ex new file mode 100644 index 0000000..9e81329 --- /dev/null +++ b/lib/openflow/role/reply.ex @@ -0,0 +1,30 @@ +defmodule Openflow.Role.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0, # virtual field + role: :nochange, + generation_id: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 25 + + def new(options \\ []) do + role = Keyword.get(options, :role, :nochange) + generation_id = Keyword.get(options, :generation_id, 0) + %Reply{role: role, generation_id: generation_id} + end + + def read(<>) do + role = Openflow.Enums.to_atom(role_int, :controller_role) + %Reply{role: role, generation_id: generation_id} + end + + def to_binary(%Reply{role: role, generation_id: generation_id}) do + role_int = Openflow.Enums.to_int(role, :controller_role) + <> + end +end diff --git a/lib/openflow/role/request.ex b/lib/openflow/role/request.ex new file mode 100644 index 0000000..e2ed43e --- /dev/null +++ b/lib/openflow/role/request.ex @@ -0,0 +1,30 @@ +defmodule Openflow.Role.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0, # virtual field + role: :nochange, + generation_id: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 24 + + def new(options \\ []) do + role = Keyword.get(options, :role, :nochange) + generation_id = Keyword.get(options, :generation_id, 0) + %Request{role: role, generation_id: generation_id} + end + + def read(<>) do + role = Openflow.Enums.to_atom(role_int, :controller_role) + %Request{role: role, generation_id: generation_id} + end + + def to_binary(%Request{role: role, generation_id: generation_id}) do + role_int = Openflow.Enums.to_int(role, :controller_role) + <> + end +end diff --git a/lib/openflow/set_async.ex b/lib/openflow/set_async.ex new file mode 100644 index 0000000..fb245d8 --- /dev/null +++ b/lib/openflow/set_async.ex @@ -0,0 +1,40 @@ +defmodule Openflow.SetAsync do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: 0, # virtual field + packet_in_mask_master: 0, + packet_in_mask_slave: 0, + port_status_mask_master: 0, + port_status_mask_slave: 0, + flow_removed_mask_master: 0, + flow_removed_mask_slave: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 28 + + def read(<>) do + %SetAsync{packet_in_mask_master: packet_in_mask_master, + packet_in_mask_slave: packet_in_mask_slave, + port_status_mask_master: port_status_mask_master, + port_status_mask_slave: port_status_mask_slave, + flow_removed_mask_master: flow_removed_mask_master, + flow_removed_mask_slave: flow_removed_mask_slave} + end + + def to_binary(%SetAsync{packet_in_mask_master: packet_in_mask_master, + packet_in_mask_slave: packet_in_mask_slave, + port_status_mask_master: port_status_mask_master, + port_status_mask_slave: port_status_mask_slave, + flow_removed_mask_master: flow_removed_mask_master, + flow_removed_mask_slave: flow_removed_mask_slave}) do + <> + end +end diff --git a/lib/openflow/set_config.ex b/lib/openflow/set_config.ex new file mode 100644 index 0000000..0a08ff5 --- /dev/null +++ b/lib/openflow/set_config.ex @@ -0,0 +1,32 @@ +defmodule Openflow.SetConfig do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + flags: [], # default = "normal" is no special handling + miss_send_len: 128 + ) + + alias __MODULE__ + + def ofp_type, do: 9 + + def new(options \\ []) do + flags = Keyword.get(options, :flags, []) + miss_send_len = Keyword.get(options, :miss_send_len, :no_buffer) + %SetConfig{flags: flags, miss_send_len: miss_send_len} + end + + def read(<>) do + flags = Openflow.Enums.int_to_flags(flags_int, :config_flags) + miss_send_len = Openflow.Utils.get_enum(miss_send_len0, :controller_max_len) + %SetConfig{flags: flags, miss_send_len: miss_send_len} + end + + def to_binary(%SetConfig{flags: flags, miss_send_len: miss_send_len0}) do + flags_int = Openflow.Enums.flags_to_int(flags, :config_flags) + miss_send_len = Openflow.Utils.get_enum(miss_send_len0, :controller_max_len) + <> + end +end diff --git a/lib/openflow/table_mod.ex b/lib/openflow/table_mod.ex new file mode 100644 index 0000000..d06f45b --- /dev/null +++ b/lib/openflow/table_mod.ex @@ -0,0 +1,28 @@ +defmodule Openflow.TableMod do + defstruct( + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + table_id: 0, + config: 0 + ) + + alias __MODULE__ + + def ofp_type, do: 17 + + def new(table_id) do + %TableMod{table_id: table_id} + end + + def read(<>) do + table_id = Openflow.Utils.get_enum(table_id_int, :table_id) + %TableMod{table_id: table_id, config: config} + end + + def to_binary(%TableMod{table_id: table_id, config: config}) do + table_id_int = Openflow.Utils.get_enum(table_id, :table_id) + <> + end +end diff --git a/lib/openflow/utils.ex b/lib/openflow/utils.ex new file mode 100644 index 0000000..afe8cf8 --- /dev/null +++ b/lib/openflow/utils.ex @@ -0,0 +1,73 @@ +defmodule Openflow.Utils do + import Bitwise + + def int_to_flags(acc, int, [{type, flagint}|rest]) when (int &&& flagint) == flagint do + int_to_flags([type|acc], int, rest) + end + def int_to_flags(acc, int, [_h|rest]) do + int_to_flags(acc, int, rest) + end + def int_to_flags(acc, _int, []), do: Enum.reverse(acc) + + def flags_to_int(acc, [], _enum), do: acc + def flags_to_int(acc0, [flag|rest], enum) do + acc = acc0 ||| Keyword.get(enum, flag, 0) + flags_to_int(acc, rest, enum) + end + + def to_hex_string(binary), do: datapath_id(binary) + + def padding(length, padding) do + case (padding - rem(length, padding)) do + ^padding -> 0 + pad_len -> pad_len + end + end + + def pad_length(length, width) do + rem((width - rem(length, width)), width) + end + + def decode_string(binary) do + String.trim_trailing(binary, <<0>>) + end + + def encode_string(binary, length) do + String.pad_trailing(binary, length, <<0>>) + end + + def get_enum(int, type) when is_integer(int) do + try do + Openflow.Enums.to_atom(int, type) + catch + :bad_enum -> int + end + end + def get_enum(int, type) do + try do + Openflow.Enums.to_int(int, type) + catch + :bad_enum -> int + end + end + + # private functions + + defp datapath_id(binary) do + binary + |> split_to_hex_string + |> Enum.join("") + |> String.downcase + end + + defp split_to_hex_string(binary) do + for <>, do: integer_to_hex(int) + end + + defp integer_to_hex(int) do + case Integer.to_string(int, 16) do + <> -> <<48, d>> + dd -> dd + end + end +end diff --git a/lib/tres.ex b/lib/tres.ex new file mode 100644 index 0000000..36ddade --- /dev/null +++ b/lib/tres.ex @@ -0,0 +1,2 @@ +defmodule Tres do +end diff --git a/lib/tres/application.ex b/lib/tres/application.ex new file mode 100644 index 0000000..5dc3b16 --- /dev/null +++ b/lib/tres/application.ex @@ -0,0 +1,17 @@ +defmodule Tres.Application do + @moduledoc false + + use Application + + alias Tres.SwitchRegistry + + def start(_type, _args) do + import Supervisor.Spec + + children = [worker(Registry, [[keys: :unique, name: SwitchRegistry]], id: SwitchRegistry), + supervisor(Tres.MessageHandlerSup, [], id: MessageHandlerSup)] + opts = [strategy: :one_for_one, name: Tres.Supervisor] + {:ok, _connection_pid} = Tres.Utils.start_openflow_listener + Supervisor.start_link(children, opts) + end +end diff --git a/lib/tres/message_handler.ex b/lib/tres/message_handler.ex new file mode 100644 index 0000000..7f01ed8 --- /dev/null +++ b/lib/tres/message_handler.ex @@ -0,0 +1,52 @@ +defmodule Tres.MessageHandler do + use GenServer + + defmodule State do + defstruct [ + ip_addr: nil, + port: nil, + datapath_id: nil, + conn_pid: nil, + conn_ref: nil, + handler_pid: nil, + handler_ref: nil + ] + end + + alias Tres.MessageHandler.State + + @process_flags [trap_exit: true] + + def start_link({ip_addr, port}, conn_pid) do + GenServer.start_link(__MODULE__, [{ip_addr, port}, conn_pid]) + end + + def init([{ip_addr, port}, conn_pid]) do + init_process() + conn_ref = Process.monitor(conn_pid) + state = %State{ + conn_pid: conn_pid, + conn_ref: conn_ref, + ip_addr: ip_addr, + port: port + } + {:ok, state} + end + + def handle_info({:'EXIT', _pid, _reason}, state) do + {:stop, :normal, state} + end + def handle_info({:'DOWN', conn_ref, :process, _conn_pid, _reason}, %State{conn_ref: conn_ref} = state) do + {:stop, :normal, state} + end + + def terminate(_reason, state) do + {:shutdown, state} + end + + ## private functions + + defp init_process do + for {flag, value} <- @process_flags, do: Process.flag(flag, value) + end +end diff --git a/lib/tres/message_handler_sup.ex b/lib/tres/message_handler_sup.ex new file mode 100644 index 0000000..ca6d393 --- /dev/null +++ b/lib/tres/message_handler_sup.ex @@ -0,0 +1,16 @@ +defmodule Tres.MessageHandlerSup do + use Supervisor + + def start_link do + Supervisor.start_link(__MODULE__, [], name: __MODULE__) + end + + def init(_) do + children = [worker(Tres.MessageHandler, [], restart: :temporary)] + supervise(children, strategy: :simple_one_for_one) + end + + def start_child({ip_addr, port}) do + Supervisor.start_child(__MODULE__, [{ip_addr, port}, self()]) + end +end diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex new file mode 100644 index 0000000..013e731 --- /dev/null +++ b/lib/tres/secure_channel.ex @@ -0,0 +1,385 @@ +defmodule Tres.SecureChannel do + @behaviour :gen_statem + + import Logger + + alias Tres.SecureChannelState + alias Tres.SwitchRegistry + alias Tres.MessageHandlerSup + + @process_flags [ + trap_exit: true, + message_queue_data: :on_heap + ] + + @supported_version 4 + + @hello_handshake_timeout 1000 + @features_handshake_timeout 1000 + @ping_timeout 5000 + @transaction_timeout 5000 + + @ping_interval 5000 + @ping_fail_max_count 10 + + def callback_mode do + [:handle_event_function, :state_enter] + end + + def start_link(ref, socket, transport, opts \\ []) do + init_args = [[ref, socket, transport, opts]] + :proc_lib.start_link(__MODULE__, :init, init_args) + end + + def init([ref, socket, transport, _opts]) do + state_data = + ref + |> init_secure_channel(socket, transport) + |> init_handler + info("[#{__MODULE__}] TCP connected to Switch on #{state_data.ip_addr}:#{state_data.port} on #{inspect(self())}") + :gen_statem.enter_loop(__MODULE__, [debug: []], :INIT, state_data, []) + end + + # TCP handler + def handle_event(:info, {:tcp, socket, packet}, state, + %SecureChannelState{socket: socket, transport: transport} = state_data) do + transport.setopts(socket, [active: :once]) + handle_packet(packet, state_data, state, []) + end + def handle_event(:info, {:tcp_closed, socket}, _state, + %SecureChannelState{socket: socket} = state_data) do + close_connection(:tcp_closed, state_data) + end + def handle_event(:info, {:tcp_error, socket, reason}, _state, + %SecureChannelState{socket: socket} = state_data) do + close_connection({:tcp_error, reason}, state_data) + end + def handle_event(:info, {:'DOWN', _ref, :process, _main_pid, _reason} = signal, _state, state_data) do + handle_signal(signal, state_data) + end + def handle_event(:info, {:'EXIT', _pid, _reason} = signal, _state, state_data) do + handle_signal(signal, state_data) + end + def handle_event(type, message, :INIT, state_data) do + handle_INIT(type, message, state_data) + end + def handle_event(type, message, :CONNECTING, state_data) do + handle_CONNECTING(type, message, state_data) + end + def handle_event(type, message, :CONNECTED, state_data) do + handle_CONNECTED(type, message, state_data) + end + def handle_event(type, message, :WAITING, state_data) do + handle_WATING(type, message, state_data) + end + + def terminate(reason, state, %SecureChannelState{datapath_id: datapath_id, aux_id: aux_id}) do + warn("[#{__MODULE__}] termiate: #{inspect(reason)} state = #{inspect(state)}") + :ok = SwitchRegistry.unregister({datapath_id, aux_id}) + end + + # private functions + + defp init_secure_channel(ref, socket, transport) do + init_process(ref) + :ok = transport.setopts(socket, [active: :once]) + SecureChannelState.new(ref: ref, socket: socket, transport: transport) + end + + defp init_process(ref) do + :ok = :proc_lib.init_ack({:ok, self()}) + :ok = :ranch.accept_ack(ref) + for {flag, value} <- @process_flags, do: Process.flag(flag, value) + end + + defp init_handler(state_data) do + %SecureChannelState{ip_addr: ip_addr, port: port} = state_data + {:ok, pid} = MessageHandlerSup.start_child({ip_addr, port}) + ref = Process.monitor(pid) + %{state_data|handler_pid: pid, handler_ref: ref} + end + + # INIT state + defp handle_INIT(:enter, _old_state, state_data) do + debug("[#{__MODULE__}] Initiate HELLO handshake: #{state_data.ip_addr}:#{state_data.port}") + initiate_hello_handshake(state_data) + end + defp handle_INIT(:info, :hello_timeout, state_data) do + close_connection(:hello_handshake_timeout, state_data) + end + defp handle_INIT(:internal, {:openflow, %Openflow.Hello{} = hello}, state_data) do + handle_hello_handshake_1(hello, state_data) + end + defp handle_INIT(:internal, message, _state_data) do + debug("[#{__MODULE__}] Hello handshake in progress, dropping message: #{inspect(message)}") + :keep_state_and_data + end + + # CONNECTING state + defp handle_CONNECTING(:enter, :INIT, state_data) do + debug("[#{__MODULE__}] Initiate FEATURES handshake: #{state_data.ip_addr}:#{state_data.port}") + initiate_features_handshake(state_data) + end + defp handle_CONNECTING(:enter, :WAITING, state_data) do + debug("[#{__MODULE__}] Re-entered features handshake") + initiate_features_handshake(state_data) + end + defp handle_CONNECTING(:info, :features_timeout, state_data) do + close_connection(:features_handshake_timeout, state_data) + end + defp handle_CONNECTING(:internal, {:openflow, %Openflow.Features.Reply{} = features}, state_data) do + # TODO: Send to handler + info("[#{__MODULE__}] Switch connected datapath_id: #{features.datapath_id} auxiliary_id: #{features.aux_id}") + _ = maybe_cancel_timer(state_data.timer_ref) + handle_features_handshake(features, state_data) + end + defp handle_CONNECTING(:internal, {:openflow, message}, _state_data) do + debug("[#{__MODULE__}] Features handshake in progress, dropping message: #{inspect(message.__struct__)}") + :keep_state_and_data + end + defp handle_CONNECTING(type, _message, state_data) when type == :cast or type == :call do + {:keep_state, state_data, [{:postpone, true}]} + end + + # CONNECTED state + defp handle_CONNECTED(:enter, :CONNECTING, _state_data) do + start_periodic_idle_check() + :keep_state_and_data + end + defp handle_CONNECTED(:info, :idle_check, state_data) do + start_periodic_idle_check() + new_state_data = maybe_ping(state_data) + {:keep_state, new_state_data} + end + defp handle_CONNECTED(:info, :ping_timeout, state_data) do + handle_ping_timeout(state_data, :CONNECTED) + end + defp handle_CONNECTED(:internal, {:openflow, %Openflow.Echo.Reply{xid: xid}}, + %SecureChannelState{ping_xid: xid} = state_data) do + handle_ping_reply(state_data) + end + defp handle_CONNECTED(:internal, {:openflow, %Openflow.Echo.Request{} = echo}, state_data) do + send_echo_reply(echo.xid, echo.data, state_data) + :keep_state_and_data + end + defp handle_CONNECTED(:internal, {:openflow, _message}, _state_data) do + # TODO: Send to handler + :keep_state_and_data + end + defp handle_CONNECTED(:cast, {:send_message, message}, state_data) do + message + |> send_message(state_data) + :keep_state_and_data + end + + # WATING state + defp handle_WATING(:enter, :CONNECTING, state_data) do + warn("[#{__MODULE__}] Possible HANG Detected on datapath_id: #{state_data.datapath_id} !") + start_periodic_idle_check() + :keep_state_and_data + end + defp handle_WATING(:info, :idle_check, state_data) do + start_periodic_idle_check() + new_state_data = maybe_ping(state_data) + {:keep_state, new_state_data} + end + defp handle_WATING(:info, :ping_timeout, state_data) do + handle_ping_timeout(state_data, :WAITING) + end + defp handle_WATING(:internal, {:openflow, _message}, state_data) do + # TODO: Send to handler + {:next_state, :CONNECTING, state_data} + end + defp handle_WATING(type, message, state_data) + when type == :cast or type == :call do + debug("[#{__MODULE__}] Postponed: #{inspect(message)}, now WATING") + {:keep_state, state_data, [{:postpone, true}]} + end + + defp handle_packet("", state_data, _state, actions) do + {:keep_state, state_data, Enum.reverse(actions)} + end + defp handle_packet(packet, %SecureChannelState{buffer: buffer} = state_data, state, actions) do + binary = <> + case Openflow.read(binary) do + {:ok, message, leftovers} -> + debug("[#{__MODULE__}] Received: #{inspect(message.__struct__)}(xid: #{message.xid}) in #{state}") + action = {:next_event, :internal, {:openflow, message}} + new_state_data = %{state_data|buffer: "", last_received: :os.timestamp} + handle_packet(leftovers, new_state_data, state, [action|actions]) + {:error, :binary_too_small} -> + handle_packet("", %{state_data|buffer: binary}, state, actions) + {:error, _reason} -> + handle_packet("", state_data, state, actions) + end + end + + defp initiate_hello_handshake(state_data) do + send_hello(state_data) + ref = :erlang.send_after(@hello_handshake_timeout, self(), :hello_timeout) + {:keep_state, %{state_data|timer_ref: ref}} + end + + defp handle_hello_handshake_1(hello, state_data) do + maybe_cancel_timer(state_data.timer_ref) + SecureChannelState.set_transaction_id(state_data.xid, hello.xid) + if Openflow.Hello.supported_version?(hello) do + {:next_state, :CONNECTING, %{state_data|timer_ref: nil}} + else + close_connection(:failed_version_negotiation, state_data) + end + end + + defp initiate_features_handshake(state_data) do + new_xid = SecureChannelState.increment_transaction_id(state_data.xid) + send_features(new_xid, state_data) + ref = :erlang.send_after(@features_handshake_timeout, self(), :features_timeout) + {:keep_state, %{state_data|timer_ref: ref}} + end + + defp handle_features_handshake(%Openflow.Features.Reply{datapath_id: datapath_id, aux_id: aux_id}, state_data) do + {:ok, _} = SwitchRegistry.register({datapath_id, aux_id}) + new_state_data = %{ + state_data| + datapath_id: datapath_id, + aux_id: aux_id, + timer_ref: nil, + main_monitor_ref: monitor_connection(datapath_id, aux_id) + } + {:next_state, :CONNECTED, new_state_data} + end + + defp monitor_connection(datapath_id, aux_id) when aux_id > 0 do + datapath_id + |> SwitchRegistry.lookup_pid + |> Process.monitor + end + defp monitor_connection(_datapath_id, _aux_id), do: nil + + defp send_hello(state_data) do + @supported_version + |> Openflow.Hello.new + |> send_message(state_data) + end + + defp send_features(xid, state_data) do + %{Openflow.Features.Request.new|xid: xid} + |> send_message(state_data) + end + + defp send_echo_reply(xid, data, state_data) do + %{Openflow.Echo.Reply.new(data)|xid: xid} + |> send_message(state_data) + end + + defp send_echo_request(xid, data, state_data) do + %{Openflow.Echo.Request.new(data)|xid: xid} + |> send_message(state_data) + end + + defp start_periodic_idle_check do + :erlang.send_after(@ping_interval, self(), :idle_check) + end + + defp maybe_ping(state_data) do + case should_be_ping?(state_data) do + true -> send_ping(state_data) + false -> state_data + end + end + + defp should_be_ping?(%SecureChannelState{last_received: last_received, aux_id: 0}) do + :timer.now_diff(:os.timestamp(), last_received) > (1000 * @ping_interval) + end + defp should_be_ping?(_) do + false + end + + defp send_ping(%SecureChannelState{xid: x_agent} = state_data) do + xid = SecureChannelState.increment_transaction_id(x_agent) + send_echo_request(xid, "", state_data) + ping_ref = :erlang.send_after(@ping_timeout, self(), :ping_timeout) + %{state_data|ping_timer_ref: ping_ref, ping_xid: xid} + end + + defp handle_ping_timeout(%SecureChannelState{ping_fail_count: fail_count} = state_data, :CONNECTING) + when fail_count > @ping_fail_max_count do + {:next_state, :WAITING, state_data} + end + defp handle_ping_timeout(%SecureChannelState{ping_fail_count: fail_count} = state_data, :WAITING) + when fail_count > @ping_fail_max_count do + close_connection(:ping_failed, state_data) + end + defp handle_ping_timeout(state_data, _) do + new_state_data = maybe_ping(state_data) + {:keep_state, new_state_data} + end + + defp handle_ping_reply(state_data) do + {:keep_state, %{state_data|ping_timer_ref: nil, ping_xid: nil}} + end + + defp send_message(message, %SecureChannelState{socket: socket, transport: transport}) do + debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + Tres.Utils.send_message(message, socket, transport) + end + + defp maybe_cancel_timer(ref) when not is_reference(ref), do: :ok + defp maybe_cancel_timer(ref) do + :erlang.cancel_timer(ref) + :ok + end + + defp handle_signal({:'DOWN', mon_ref, :process, _main_pid, reason}, + %SecureChannelState{main_monitor_ref: mon_ref} = state_data) do + close_connection({:main_closed, reason}, state_data) + end + defp handle_signal({:'DOWN', handler_ref, :process, _main_pid, reason}, + %SecureChannelState{handler_ref: handler_ref} = state_data) do + close_connection({:handler_down, reason}, state_data) + end + defp handle_signal({:'EXIT', _pid, reason}, state_data) do + close_connection({:trap_detected, reason}, state_data) + end + + defp close_connection(:failed_version_negotiation, state_data) do + warn("[#{__MODULE__}] connection terminated: Version negotiation failed") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:hello_handshake_timeout, state_data) do + warn("[#{__MODULE__}] connection terminated: Hello handshake timed out") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:features_timeout, state_data) do + warn("[#{__MODULE__}] connection terminated: Features handshake timed out") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:handler_error, state_data) do + warn("[#{__MODULE__}] connection terminated: Got handler error") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:ping_failed, state_data) do + warn("[#{__MODULE__}] connection terminated: Exceeded to max_ping_fail_count") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection({:main_closed, reason}, state_data) do + warn("[#{__MODULE__}] connection terminated: Main connection down by #{reason}") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection({:handler_down, reason}, state_data) do + warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection({:trap_detected, reason}, state_data) do + warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection(:tcp_closed, state_data) do + warn("[#{__MODULE__}] connection terminated: TCP Closed by peer") + {:stop, :normal, %{state_data|socket: nil}} + end + defp close_connection({:tcp_error, reason}, state_data) do + warn("[#{__MODULE__}] connection terminated: TCP Error occured: #{reason}") + {:stop, :normal, %{state_data|socket: nil}} + end +end diff --git a/lib/tres/secure_channel_state.ex b/lib/tres/secure_channel_state.ex new file mode 100644 index 0000000..f2c163d --- /dev/null +++ b/lib/tres/secure_channel_state.ex @@ -0,0 +1,51 @@ +defmodule Tres.SecureChannelState do + defstruct( + handler_pid: nil, + handler_ref: nil, + ref: nil, + socket: nil, + transport: nil, + buffer: "", + ip_addr: nil, + port: "", + datapath_id: "", + aux_id: "", + timer_ref: nil, + xid: nil, + main_monitor_ref: nil, + ping_xid: 0, + ping_timer_ref: nil, + ping_fail_count: 0, + last_received: 0 + ) + + alias __MODULE__ + + def new(options) do + ref = Keyword.get(options, :ref) + socket = Keyword.get(options, :socket) + transport = Keyword.get(options, :transport) + {:ok, {ip_addr, port}} = :inet.peername(socket) + {:ok, xid_agent} = Agent.start_link(fn -> 0 end) + %SecureChannelState{ + ref: ref, + socket: socket, + transport: transport, + ip_addr: :inet.ntoa(ip_addr), + port: port, + xid: xid_agent + } + end + + def increment_transaction_id(xid_agent) do + Agent.get_and_update(xid_agent, &({&1 + 1, &1 + 1})) + end + + def set_transaction_id(xid_agent, xid) do + Agent.update(xid_agent, fn(_) -> xid end) + end + + def get_transaction_id(xid_agent) do + Agent.get(xid_agent, &(&1)) + end +end diff --git a/lib/tres/switch_registry.ex b/lib/tres/switch_registry.ex new file mode 100644 index 0000000..f7b8a19 --- /dev/null +++ b/lib/tres/switch_registry.ex @@ -0,0 +1,32 @@ +defmodule Tres.SwitchRegistry do + def register({_dpid, _aux_id} = datapath_id) do + {:ok, _} = Registry.register(__MODULE__, datapath_id, []) + end + + def unregister({_dpid, _aux_id} = datapath_id) do + :ok = Registry.unregister(__MODULE__, datapath_id) + end + + def lookup_pid({_dpid, _aux_id} = datapath_id) do + case Registry.lookup(__MODULE__, datapath_id) do + [{pid, _}] -> pid + [] -> nil + end + end + def lookup_pid(datapath_id) do + lookup_pid({datapath_id, 0}) + end + + def send_message(message, {_dpid, _aux_id} = datapath_id) do + Registry.dispatch(__MODULE__, datapath_id, &dispatch(&1, message)) + end + def send_message(message, dpid) when is_binary(dpid) do + send_message(message, {dpid, 0}) + end + + # private function + + defp dispatch(entries, message) do + for {pid, _} <- entries, do: :gen_statem.cast(pid, {:send_message, message}) + end +end diff --git a/lib/tres/utils.ex b/lib/tres/utils.ex new file mode 100644 index 0000000..358e44f --- /dev/null +++ b/lib/tres/utils.ex @@ -0,0 +1,26 @@ +defmodule Tres.Utils do + import Logger + + @connection_manager Tres.SecureChannel + @default_max_connections 10 + @default_num_acceptors 10 + @default_openflow_port 6633 + + def start_openflow_listener do + max_connections = Application.get_env(:tres, :max_connections, @default_max_connections) + num_acceptors = Application.get_env(:tres, :num_acceptors, @default_num_acceptors) + port = Application.get_env(:tres, :port, @default_openflow_port) + options = [max_connections: max_connections, num_acceptors: num_acceptors, port: port] + :ranch.start_listener(Tres, :ranch_tcp, options, @connection_manager, []) + end + + def send_message(message, socket, transport) do + try do + packet = Openflow.to_binary(message) + transport.send(socket, packet) + catch + _ -> + error("[#{__MODULE__}] Unencodable error: #{inspect(message)}") + end + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..7a68614 --- /dev/null +++ b/mix.exs @@ -0,0 +1,24 @@ +defmodule Tres.Mixfile do + use Mix.Project + + def project do + [app: :tres, + version: "0.1.0", + elixir: "~> 1.5", + start_permanent: Mix.env == :prod, + deps: deps()] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [extra_applications: [:logger, :ranch], + mod: {Tres.Application, []}] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [{:ranch, "~> 1.4.0"}, + {:binpp, github: "jtendo/binpp", branch: "master"}, + {:pkt, github: "msantos/pkt", ref: "3afb196"}] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..e371c67 --- /dev/null +++ b/mix.lock @@ -0,0 +1,6 @@ +%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, + "flow": {:hex, :flow, "0.12.0", "32c5a5f3ff6693e004b6c17a8c64dce2f8cdaf9564912d79427176013a586ab6", [], [{:gen_stage, "~> 0.12.0", [hex: :gen_stage, repo: "hexpm", optional: false]}], "hexpm"}, + "gen_stage": {:hex, :gen_stage, "0.12.2", "e0e347cbb1ceb5f4e68a526aec4d64b54ad721f0a8b30aa9d28e0ad749419cbb", [:mix], [], "hexpm"}, + "gen_state_machine": {:hex, :gen_state_machine, "2.0.1", "85efd5a0376929c3a4246dd943e17564a2908c7ddd7acd242d84594e785d83f8", [], [], "hexpm"}, + "pkt": {:git, "https://github.com/msantos/pkt.git", "3afb1967f34324c1dec5035a6e36232da815c2e6", [ref: "3afb196"]}, + "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"}} diff --git a/test/ofp_action_test.exs b/test/ofp_action_test.exs new file mode 100644 index 0000000..061d036 --- /dev/null +++ b/test/ofp_action_test.exs @@ -0,0 +1,566 @@ +defmodule OfpActionTest do + use ExUnit.Case + doctest Openflow + + test "Openflow.Action.NxBundle" do + test_file = "test/packet_data/nx_bundle.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + bundle = + Openflow.Action.NxBundle.new( + algorithm: :highest_random_weight, + slaves: [4, 8] + ) + actions_bin = Openflow.Action.to_binary(bundle) + assert actions_bin == packet + assert actions == [bundle] + end + + test "Openflow.Action.NxBundleLoad" do + test_file = "test/packet_data/nx_bundle_load.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + bundle_load = + Openflow.Action.NxBundleLoad.new( + algorithm: :highest_random_weight, + slaves: [4, 8], + dst_field: :reg0 + ) + actions_bin = Openflow.Action.to_binary(bundle_load) + assert actions_bin == packet + assert actions == [bundle_load] + end + + test "Openflow.Action.NxController" do + test_file = "test/packet_data/nx_controller.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + controller = Openflow.Action.NxController.new( + max_len: 1234, + reason: :invalid_ttl, + id: 5678 + ) + actions_bin = Openflow.Action.to_binary(controller) + assert actions_bin == packet + assert actions == [controller] + end + + test "Openflow.Action.NxController2" do + test_file = "test/packet_data/nx_controller2.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + controller2 = Openflow.Action.NxController2.new( + max_len: 1234, + reason: :invalid_ttl, + userdata: <<1,2,3,4,5>>, + pause: true + ) + assert actions == [controller2] + end + + describe "Openflow.Action.NxConntrack" do + test "with ct(alg=ftp)" do + test_file = "test/packet_data/nx_ct(alg=ftp).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(alg: 21) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(alg=tftp)" do + test_file = "test/packet_data/nx_ct(alg=tftp).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(alg: 69) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit)" do + test_file = "test/packet_data/nx_ct(commit).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(flags: [:commit]) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,force)" do + test_file = "test/packet_data/nx_ct(commit, force).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(flags: [:commit, :force]) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63]))" do + test_file = + "test/packet_data/nx_ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxRegLoad.new(dst_field: :ct_label, value: 0, offset: 64, n_bits: 64), + Openflow.Action.NxRegLoad.new(dst_field: :ct_label, value: 0x1d, n_bits: 64)] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[]))" do + test_file = "test/packet_data/nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxRegLoad.new(dst_field: :ct_mark, value: 0xf009)] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[]))" do + test_file = "test/packet_data/nx_ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit, :force], + exec: [Openflow.Action.NxRegLoad.new(dst_field: :ct_mark, value: 0xf009)] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(dst))" do + test_file = "test/packet_data/nx_ct(commit,nat(dst)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxNat.new(flags: [:dst])] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash))" do + test_file = "test/packet_data/nx_ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv4_min} = :inet.parse_ipv4_address('10.0.0.128') + {:ok, ipv4_max} = :inet.parse_ipv4_address('10.0.0.254') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:dst, :protocol_hash], + ipv4_min: ipv4_min, + ipv4_max: ipv4_max + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src))" do + test_file = "test/packet_data/nx_ct(commit,nat(src)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxNat.new(flags: [:src])] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=10.0.0.240,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=10.0.0.240,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv4_min} = :inet.parse_ipv4_address('10.0.0.240') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :protocol_random], + ipv4_min: ipv4_min + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv4_min} = :inet.parse_ipv4_address('10.0.0.240') + {:ok, ipv4_max} = :inet.parse_ipv4_address('10.0.0.254') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :persistent], + ipv4_min: ipv4_min, + ipv4_max: ipv4_max, + proto_min: 32_768, + proto_max: 65_535 + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=10.0.0.240:32768-65535,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=10.0.0.240:32768-65535,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv4_min} = :inet.parse_ipv4_address('10.0.0.240') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :protocol_random], + ipv4_min: ipv4_min, + proto_min: 32_768, + proto_max: 65_535 + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv6_min} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:1') + {:ok, ipv6_max} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:a18b') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :protocol_random], + ipv6_min: ipv6_min, + ipv6_max: ipv6_max, + proto_min: 255, + proto_max: 4096 + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv6_min} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:1') + {:ok, ipv6_max} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:a18b') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [ + Openflow.Action.NxNat.new( + flags: [:src, :protocol_random], + ipv6_min: ipv6_min, + ipv6_max: ipv6_max + ) + ] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random))" do + test_file = "test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + {:ok, ipv6_min} = :inet.parse_ipv6_address('fe80::20c:29ff:fe88:a18b') + ct = Openflow.Action.NxConntrack.new( + flags: [:commit], + exec: [Openflow.Action.NxNat.new(flags: [:src, :protocol_random], ipv6_min: ipv6_min)] + ) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(nat)" do + test_file = "test/packet_data/nx_ct(nat).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(exec: [Openflow.Action.NxNat.new]) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(table=10)" do + test_file = "test/packet_data/nx_ct(table=10).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(recirc_table: 10) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(zone=10)" do + test_file = "test/packet_data/nx_ct(zone=10).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(zone_imm: 10) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct(zone=NXM_NX_REG0[0..15])" do + test_file = "test/packet_data/nx_ct(zone=NXM_NX_REG0[0..15]).raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new(zone_src: :reg0, zone_offset: 0, zone_n_bits: 16) + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct" do + test_file = "test/packet_data/nx_ct.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxConntrack.new + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with ct_clear" do + test_file = "test/packet_data/nx_ct_clear.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + ct = Openflow.Action.NxCtClear.new + actions_bin = Openflow.Action.to_binary(ct) + assert actions_bin == packet + assert actions == [ct] + end + + test "with dec_ttl" do + test_file = "test/packet_data/nx_dec_ttl.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + dec_ttl = Openflow.Action.NxDecTtl.new + actions_bin = Openflow.Action.to_binary(dec_ttl) + assert actions_bin == packet + assert actions == [dec_ttl] + end + + test "with dec_ttl_cnt_ids" do + test_file = "test/packet_data/nx_dec_ttl_cnt_ids.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + dec_ttl = Openflow.Action.NxDecTtlCntIds.new([32_768, 12_345, 90, 765, 1024]) + actions_bin = Openflow.Action.to_binary(dec_ttl) + assert actions_bin == packet + assert actions == [dec_ttl] + end + + test "with exit" do + test_file = "test/packet_data/nx_exit.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + exit = Openflow.Action.NxExit.new + actions_bin = Openflow.Action.to_binary(exit) + assert actions_bin == packet + assert actions == [exit] + end + + test "with fin_timeout" do + test_file = "test/packet_data/nx_fin_timeout.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + fin_timeout = Openflow.Action.NxFinTimeout.new(idle_timeout: 10, hard_timeout: 20) + actions_bin = Openflow.Action.to_binary(fin_timeout) + assert actions_bin == packet + assert actions == [fin_timeout] + end + + test "with learn" do + test_file = "test/packet_data/nx_learn.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + learn = Openflow.Action.NxLearn.new( + idle_timeout: 10, + hard_timeout: 20, + priority: 80, + cookie: 0x123456789abcdef0, + flags: [], + table_id: 2, + fin_idle_timeout: 2, + fin_hard_timeout: 4, + flow_specs: [ + Openflow.Action.NxFlowSpecMatch.new(src: :nx_vlan_tci, dst: :nx_vlan_tci, n_bits: 12), + Openflow.Action.NxFlowSpecMatch.new(src: :nx_eth_src, dst: :nx_eth_dst), + Openflow.Action.NxFlowSpecOutput.new(src: :nx_in_port) + ] + ) + actions_bin = Openflow.Action.to_binary(learn) + assert actions_bin == packet + assert actions == [learn] + end + + test "with learn2" do + test_file = "test/packet_data/nx_learn2.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + learn2 = Openflow.Action.NxLearn2.new( + idle_timeout: 10, + hard_timeout: 20, + priority: 80, + cookie: 0x123456789abcdef0, + flags: [:write_result], + table_id: 2, + fin_idle_timeout: 2, + fin_hard_timeout: 4, + limit: 1, + result_dst: :reg0, + result_dst_offset: 8, + flow_specs: [ + Openflow.Action.NxFlowSpecMatch.new(src: :nx_vlan_tci, dst: :nx_vlan_tci, n_bits: 12), + Openflow.Action.NxFlowSpecMatch.new(src: :nx_eth_src, dst: :nx_eth_dst), + Openflow.Action.NxFlowSpecOutput.new(src: :nx_in_port) + ] + ) + actions_bin = Openflow.Action.to_binary(learn2) + assert actions_bin == packet + assert actions == [learn2] + end + + test "with multipath" do + test_file = "test/packet_data/nx_multipath.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + multipath = Openflow.Action.NxMultipath.new( + algorithm: :modulo_n, + basis: 50, + dst_field: :reg0 + ) + actions_bin = Openflow.Action.to_binary(multipath) + assert actions_bin == packet + assert actions == [multipath] + end + + test "with note" do + test_file = "test/packet_data/nx_note.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + note = Openflow.Action.NxNote.new(<<0x11, 0xe9, 0x9a, 0xad, 0x67, 0xf3>>) + actions_bin = Openflow.Action.to_binary(note) + assert actions_bin == packet + assert actions == [note] + end + + test "with output_reg" do + test_file = "test/packet_data/nx_output_reg.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + output_reg = Openflow.Action.NxOutputReg.new(src_field: :reg1, n_bits: 6, offset: 5) + actions_bin = Openflow.Action.to_binary(output_reg) + assert actions_bin == packet + assert actions == [output_reg] + end + + test "with output_trunc" do + test_file = "test/packet_data/nx_output_trunc.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + output_trunc = Openflow.Action.NxOutputTrunc.new(port_number: 1, max_len: 100) + actions_bin = Openflow.Action.to_binary(output_trunc) + assert actions_bin == packet + assert actions == [output_trunc] + end + + test "with pop_queue" do + test_file = "test/packet_data/nx_pop_queue.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + pop_queue = Openflow.Action.NxPopQueue.new + actions_bin = Openflow.Action.to_binary(pop_queue) + assert actions_bin == packet + assert actions == [pop_queue] + end + + test "with reg_load" do + test_file = "test/packet_data/nx_reg_load.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + reg_load = Openflow.Action.NxRegLoad.new(dst_field: :nx_vlan_tci, value: 0xf009) + actions_bin = Openflow.Action.to_binary(reg_load) + assert actions_bin == packet + assert actions == [reg_load] + end + + test "with reg_move" do + test_file = "test/packet_data/nx_reg_move.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + reg_move = Openflow.Action.NxRegMove.new(src_field: :nx_in_port, dst_field: :nx_vlan_tci) + actions_bin = Openflow.Action.to_binary(reg_move) + assert actions_bin == packet + assert actions == [reg_move] + end + + test "with resubmit" do + test_file = "test/packet_data/nx_resubmit.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + resubmit = Openflow.Action.NxResubmit.new(5) + actions_bin = Openflow.Action.to_binary(resubmit) + assert actions_bin == packet + assert actions == [resubmit] + end + + test "with resubmit_table" do + test_file = "test/packet_data/nx_resubmit_table.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + resubmit_table = Openflow.Action.NxResubmitTable.new(in_port: 10, table_id: 5) + actions_bin = Openflow.Action.to_binary(resubmit_table) + assert actions_bin == packet + assert actions == [resubmit_table] + end + + test "with resubmit_table_ct" do + test_file = "test/packet_data/nx_resubmit_table_ct.raw" + packet = File.read!(test_file) + actions = Openflow.Action.read(packet) + resubmit_table_ct = Openflow.Action.NxResubmitTableCt.new(in_port: 10, table_id: 5) + actions_bin = Openflow.Action.to_binary(resubmit_table_ct) + assert actions_bin == packet + assert actions == [resubmit_table_ct] + end + end +end diff --git a/test/ofp_echo_test.exs b/test/ofp_echo_test.exs new file mode 100644 index 0000000..13aa181 --- /dev/null +++ b/test/ofp_echo_test.exs @@ -0,0 +1,52 @@ +defmodule OfpEchoTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_ECHO_REQUEST packet" do + {:ok, %Openflow.Echo.Request{} = echo, ""} = + "test/packet_data/ofp_echo_request.raw" + |> File.read! + |> Openflow.read + assert echo.version == 4 + assert echo.xid == 0 + assert echo.data == "" + end + + test "with OFP_ECHO_REPLY packet" do + {:ok, %Openflow.Echo.Reply{} = echo, ""} = + "test/packet_data/ofp_echo_reply.raw" + |> File.read! + |> Openflow.read + assert echo.version == 4 + assert echo.xid == 0 + assert echo.data == "" + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.Echo.Request{}" do + echo = %Openflow.Echo.Request{ + version: 4, + xid: 0, + data: "" + } + expect = + "test/packet_data/ofp_echo_request.raw" + |> File.read! + assert Openflow.to_binary(echo) == expect + end + + test "with %Openflow.Echo.Reply{}" do + echo = %Openflow.Echo.Reply{ + version: 4, + xid: 0, + data: "" + } + expect = + "test/packet_data/ofp_echo_reply.raw" + |> File.read! + assert Openflow.to_binary(echo) == expect + end + end +end diff --git a/test/ofp_error_test.exs b/test/ofp_error_test.exs new file mode 100644 index 0000000..a7c9de1 --- /dev/null +++ b/test/ofp_error_test.exs @@ -0,0 +1,56 @@ +defmodule OfpErrorTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_ERROR packet" do + {:ok, error, ""} = + "test/packet_data/ofp_error.raw" + |> File.read! + |> Openflow.read + assert error.version == 4 + assert error.xid == 0 + assert error.type == :bad_action + assert error.code == :unsupported_order + assert error.data == "fugafuga" + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.Error{}" do + error = %Openflow.ErrorMsg{ + version: 4, + xid: 0, + type: :bad_action, + code: :unsupported_order, + data: "fugafuga", + } + + expect = + "test/packet_data/ofp_error.raw" + |> File.read! + + assert Openflow.to_binary(error) == expect + end + + test "with experimenter %Openflow.Error{}" do + error = %Openflow.ErrorMsg{ + version: 4, + xid: 0, + type: :experimenter, + exp_type: 1, + experimenter: 0xdeadbeef, + data: "hogehoge", + } + + expect = << + 4, 1, 0, 24, 0, 0, 0, + 0, 255, 255, 0, 1, 222, + 173, 190, 239, 104, 111, 103, + 101, 104, 111, 103, 101 + >> + + assert Openflow.to_binary(error) == expect + end + end +end diff --git a/test/ofp_features_test.exs b/test/ofp_features_test.exs new file mode 100644 index 0000000..b03960c --- /dev/null +++ b/test/ofp_features_test.exs @@ -0,0 +1,58 @@ +defmodule OfpFeaturesTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_FEATURES_REQUEST packet" do + {:ok, %Openflow.Features.Request{} = features, ""} = + "test/packet_data/ofp_features_request.raw" + |> File.read! + |> Openflow.read + assert features.version == 4 + assert features.xid == 0 + end + + test "with OFP_FEATURES_REPLY packet" do + {:ok, %Openflow.Features.Reply{} = features, ""} = + "test/packet_data/ofp_features_reply.raw" + |> File.read! + |> Openflow.read + assert features.version == 4 + assert features.xid == 0 + assert features.datapath_id == "0000000000000001" + assert features.n_buffers == 255 + assert features.n_tables == 255 + assert features.aux_id == 0 + assert features.capabilities == [:flow_stats, :table_stats, :port_stats, :group_stats, :queue_stats] + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.Features.Request{}" do + features = %Openflow.Features.Request{ + version: 4, + xid: 0 + } + expect = + "test/packet_data/ofp_features_request.raw" + |> File.read! + assert Openflow.to_binary(features) == expect + end + end + + test "with %Openflow.Features.Reply{}" do + features = %Openflow.Features.Reply{ + version: 4, + xid: 0, + datapath_id: "0000000000000001", + n_buffers: 255, + n_tables: 255, + aux_id: 0, + capabilities: [:flow_stats, :table_stats, :port_stats, :group_stats, :queue_stats], + } + expect = + "test/packet_data/ofp_features_reply.raw" + |> File.read! + assert Openflow.to_binary(features) == expect + end +end diff --git a/test/ofp_flow_mod_test.exs b/test/ofp_flow_mod_test.exs new file mode 100644 index 0000000..81661a2 --- /dev/null +++ b/test/ofp_flow_mod_test.exs @@ -0,0 +1,280 @@ +defmodule OfpFlowModTest do + use ExUnit.Case + doctest Openflow + + @flow_mod1 "test/packet_data/4-2-ofp_flow_mod.packet" + @flow_mod2 "test/packet_data/4-3-ofp_flow_mod.packet" + @flow_mod3 "test/packet_data/4-46-ofp_flow_mod.packet" + @flow_mod4 "test/packet_data/4-60-ofp_flow_mod.packet" + @flow_mod5 "test/packet_data/libofproto-OFP13-flow_mod.packet" + @flow_mod6 "test/packet_data/libofproto-OFP13-flow_mod.truncated64" + @flow_mod7 "test/packet_data/libofproto-OFP13-flow_mod_conjunction.packet" + @flow_mod8 "test/packet_data/libofproto-OFP13-flow_mod_match_conj.packet" + + describe "Openflow.read/1" do + test "with OFP_FLOW_MOD packet(1)" do + binary = File.read!(@flow_mod1) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0 + assert fm.cookie_mask == 0 + assert fm.table_id == 1 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 123 + assert fm.buffer_id == 0xffff + assert fm.out_port == :any + assert fm.out_group == :any + assert fm.flags == [] + assert fm.match == Openflow.Match.new(eth_dst: "f20ba47df8ea") + assert fm.instructions == [ + Openflow.Instruction.WriteActions.new([ + Openflow.Action.SetField.new({:vlan_vid, 258}), + Openflow.Action.CopyTtlOut.new, + Openflow.Action.CopyTtlIn.new, + Openflow.Action.CopyTtlIn.new, + Openflow.Action.PopPbb.new, + Openflow.Action.PushPbb.new(4660), + Openflow.Action.PopMpls.new(39030), + Openflow.Action.PushMpls.new(34887), + Openflow.Action.PopVlan.new, + Openflow.Action.PushVlan.new(33024), + Openflow.Action.DecMplsTtl.new, + Openflow.Action.SetMplsTtl.new(10), + Openflow.Action.DecNwTtl.new, + Openflow.Action.SetNwTtl.new(10), + Openflow.Action.Experimenter.new(101, <<0, 1, 2, 3, 4, 5, 6, 7>>), + Openflow.Action.SetQueue.new(3), + Openflow.Action.Group.new(99), + Openflow.Action.Output.new(6) + ]), + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.SetField.new({:eth_src, "010203040506"}), + Openflow.Action.SetField.new({:onf_pbb_uca, 1}) + ]) + ] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(2)" do + binary = File.read!(@flow_mod2) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0 + assert fm.cookie_mask == 0 + assert fm.table_id == 0 + assert fm.cookie == 0 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 123 + assert fm.buffer_id == 0xffff + assert fm.out_port == :any + assert fm.out_group == :any + assert fm.flags == [] + assert fm.match == Openflow.Match.new(in_port: 6, eth_src: "f20ba47df8ea") + assert fm.instructions == [Openflow.Instruction.GotoTable.new(1)] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(3)" do + binary = File.read!(@flow_mod3) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0 + assert fm.cookie_mask == 0 + assert fm.table_id == 1 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 123 + assert fm.buffer_id == 0xffff + assert fm.out_port == :any + assert fm.out_group == :any + assert fm.flags == [] + assert fm.match == Openflow.Match.new(eth_dst: "f20ba47df8ea") + assert fm.instructions == [ + Openflow.Instruction.Meter.new(1), + Openflow.Instruction.WriteActions.new([Openflow.Action.Output.new(6)]) + ] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(4)" do + binary = File.read!(@flow_mod4) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0 + assert fm.cookie_mask == 0 + assert fm.table_id == 1 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 123 + assert fm.buffer_id == 0xffff + assert fm.out_port == :any + assert fm.out_group == :any + assert fm.flags == [] + assert fm.match == Openflow.Match.new( + in_port: 84_281_096, + in_phy_port: 16_909_060, + metadata: 283_686_952_306_183, + eth_type: 2054, + eth_dst: "ffffffffffff", + eth_src: "f20ba47df8ea", + vlan_vid: 999, + ip_dscp: 9, + ip_ecn: 3, + ip_proto: 99, + ipv4_src: {1, 2, 3, 4}, + ipv4_dst: {1, 2, 3, 4}, + tcp_src: 8080, + tcp_dst: 18_080, + udp_src: 28_080, + udp_dst: 55_936, + sctp_src: 48_080, + sctp_dst: 59_328, + icmpv4_type: 100, + icmpv4_code: 101, + arp_op: 1, + arp_spa: {10, 0, 0, 1}, + arp_tpa: {10, 0, 0, 3}, + arp_sha: "f20ba47df8ea", + arp_tha: "000000000000", + ipv6_src: {65152, 0, 0, 0, 61451, 42239, 65096, 10405}, + ipv6_dst: {65152, 0, 0, 0, 61451, 42239, 65029, 47068}, + ipv6_flabel: 541_473, + icmpv6_type: 200, + icmpv6_code: 201, + ipv6_nd_target: {65152, 0, 0, 0, 2656, 28415, 65151, 29927}, + ipv6_nd_sll: "00000000029a", + ipv6_nd_tll: "00000000022b", + mpls_label: 624_485, + mpls_tc: 5, + mpls_bos: 1, + pbb_isid: 11_259_375, + tunnel_id: 651061555542690057, + ipv6_exthdr: [:auth, :frag, :router, :hop, :unrep, :unseq], + onf_pbb_uca: 1, + tun_src: {1, 2, 3, 4}, + tun_dst: {1, 2, 3, 4} + ) + assert fm.instructions == [] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(5)" do + binary = File.read!(@flow_mod5) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0x123456789abcdef0 + assert fm.cookie_mask == 0xffffffffffffffff + assert fm.table_id == 2 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 0 + assert fm.buffer_id == 0 + assert fm.out_port == 0 + assert fm.out_group == 0 + assert fm.flags == [] + assert fm.match == Openflow.Match.new( + in_port: 43981, + eth_dst: "aabbcc998877", + eth_type: 2048, + vlan_vid: 5095, + ipv4_dst: {192, 168, 2, 1}, + tunnel_id: 50_000, + tun_src: {192, 168, 2, 3}, + tun_dst: {192, 168, 2, 4} + ) + assert fm.instructions == [ + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:ipv4_dst, {192, 168, 2, 9}}), + Openflow.Action.NxLearn.new( + hard_timeout: 300, + priority: 1, + table_id: 99, + flow_specs: [ + Openflow.Action.NxFlowSpecMatch.new(src: :vlan_vid, dst: :vlan_vid, n_bits: 12), + Openflow.Action.NxFlowSpecMatch.new(src: :nx_eth_src, dst: :nx_eth_dst), + Openflow.Action.NxFlowSpecLoad.new(src: 0, dst: :vlan_vid, n_bits: 12), + Openflow.Action.NxFlowSpecLoad.new(src: :tun_id, dst: :tun_id), + Openflow.Action.NxFlowSpecOutput.new(src: :in_port) + ] + ) + ]), + Openflow.Instruction.GotoTable.new(100) + ] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(6)" do\ + {:error, :binary_too_small} = + @flow_mod6 + |> File.read! + |> Openflow.read + end + + test "with OFP_FLOW_MOD packet(7)" do + binary = File.read!(@flow_mod7) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0x123456789abcdef0 + assert fm.cookie_mask == 0xffffffffffffffff + assert fm.table_id == 4 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 0 + assert fm.buffer_id == 0 + assert fm.out_port == 0 + assert fm.out_group == 0 + assert fm.flags == [] + assert fm.match == Openflow.Match.new( + in_port: 43981, + eth_dst: "aabbcc998877", + eth_type: 2048, + vlan_vid: 5095, + ipv4_dst: {192, 168, 2, 1}, + tunnel_id: 50_000, + tun_src: {192, 168, 2, 3}, + tun_dst: {192, 168, 2, 4} + ) + assert fm.instructions == [ + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.NxConjunction.new(clause: 1, id: 0xabcdef, n_clauses: 2) + ]) + ] + assert Openflow.to_binary(fm) == binary + end + + test "with OFP_FLOW_MOD packet(8)" do + binary = File.read!(@flow_mod8) + {:ok, fm, ""} = Openflow.read(binary) + + assert fm.cookie == 0x123456789abcdef0 + assert fm.cookie_mask == 0xffffffffffffffff + assert fm.table_id == 3 + assert fm.command == :add + assert fm.idle_timeout == 0 + assert fm.hard_timeout == 0 + assert fm.priority == 0 + assert fm.buffer_id == 0 + assert fm.out_port == 0 + assert fm.out_group == 0 + assert fm.flags == [] + assert fm.match == Openflow.Match.new(conj_id: 0xabcdef) + assert fm.instructions == [ + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:ipv4_dst, {192, 168, 2, 9}}) + ]), + Openflow.Instruction.GotoTable.new(100) + ] + assert Openflow.to_binary(fm) == binary + end + end +end diff --git a/test/ofp_flow_removed_test.exs b/test/ofp_flow_removed_test.exs new file mode 100644 index 0000000..4a01473 --- /dev/null +++ b/test/ofp_flow_removed_test.exs @@ -0,0 +1,57 @@ +defmodule OfpFlowRemovedTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_FLOW_REMOVED packet(with a match field)" do + {:ok, flow_removed, ""} = + "test/packet_data/4-40-ofp_flow_removed.packet" + |> File.read! + |> Openflow.read + + assert flow_removed.version == 4 + assert flow_removed.xid == 0 + assert flow_removed.cookie == 0 + assert flow_removed.priority == 0xffff + assert flow_removed.reason == :idle_timeout + assert flow_removed.table_id == 0 + assert flow_removed.duration_sec == 3 + assert flow_removed.duration_nsec == 48_825_000 + assert flow_removed.idle_timeout == 3 + assert flow_removed.hard_timeout == 0 + assert flow_removed.packet_count == 1 + assert flow_removed.byte_count == 86 + assert flow_removed.match == [eth_dst: "f20ba47df8ea"] + end + + test "with OFP_FLOW_REMOVED packet(with match fields)" do + {:ok, flow_removed, ""} = + "test/packet_data/libofproto-OFP13-flow_removed.packet" + |> File.read! + |> Openflow.read + + assert flow_removed.version == 4 + assert flow_removed.xid == 0 + assert flow_removed.cookie == 0x123456789abcdef0 + assert flow_removed.priority == 100 + assert flow_removed.reason == :idle_timeout + assert flow_removed.table_id == 1 + assert flow_removed.duration_sec == 600 + assert flow_removed.duration_nsec == 500 + assert flow_removed.idle_timeout == 400 + assert flow_removed.hard_timeout == 300 + assert flow_removed.packet_count == 200 + assert flow_removed.byte_count == 100 + assert flow_removed.match == [ + in_port: 43_981, + eth_dst: "aabbcc998877", + eth_type: 2048, + vlan_vid: 5095, + ipv4_dst: {192, 168, 2, 1}, + tunnel_id: 50_000, + tun_src: {192, 168, 2, 3}, + tun_dst: {192, 168, 2, 4} + ] + end + end +end diff --git a/test/ofp_get_config_test.exs b/test/ofp_get_config_test.exs new file mode 100644 index 0000000..524e99f --- /dev/null +++ b/test/ofp_get_config_test.exs @@ -0,0 +1,52 @@ +defmodule OfpGetConfigTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_GET_CONFIG_REQUEST packet" do + {:ok, %Openflow.GetConfig.Request{} = config, ""} = + "test/packet_data/ofp_get_config_request.raw" + |> File.read! + |> Openflow.read + assert config.version == 4 + assert config.xid == 0 + end + + test "with OFP_GET_CONFIG_REPLY packet" do + {:ok, %Openflow.GetConfig.Reply{} = config, ""} = + "test/packet_data/ofp_get_config_reply.raw" + |> File.read! + |> Openflow.read + assert config.version == 4 + assert config.xid == 0 + assert config.flags == [] + assert config.miss_send_len == 128 + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.GetConfig.Request{}" do + config = %Openflow.GetConfig.Request{ + version: 4, + xid: 0 + } + expect = + "test/packet_data/ofp_get_config_request.raw" + |> File.read! + assert Openflow.to_binary(config) == expect + end + + test "with %Openflow.GetConfig.Reply{}" do + config = %Openflow.GetConfig.Reply{ + version: 4, + xid: 0, + flags: [], + miss_send_len: 128 + } + expect = + "test/packet_data/ofp_get_config_reply.raw" + |> File.read! + assert Openflow.to_binary(config) == expect + end + end +end diff --git a/test/ofp_group_mod_test.exs b/test/ofp_group_mod_test.exs new file mode 100644 index 0000000..cdf7e04 --- /dev/null +++ b/test/ofp_group_mod_test.exs @@ -0,0 +1,26 @@ +defmodule OfpGroupModTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_GROUP_MOD packet" do + binary = File.read!("test/packet_data/4-21-ofp_group_mod.packet") + {:ok, group_mod, ""} = Openflow.read(binary) + + assert group_mod.version == 4 + assert group_mod.xid == 0 + assert group_mod.command == :add + assert group_mod.type == :all + assert group_mod.group_id == 1 + assert group_mod.buckets == [ + Openflow.Bucket.new( + weight: 1, + watch_port: 1, + watch_group: 1, + actions: [Openflow.Action.Output.new(2)] + ) + ] + assert Openflow.to_binary(group_mod) == binary + end + end +end diff --git a/test/ofp_hello_test.exs b/test/ofp_hello_test.exs new file mode 100644 index 0000000..0fd4b1e --- /dev/null +++ b/test/ofp_hello_test.exs @@ -0,0 +1,26 @@ +defmodule OfpHelloTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_HELLO packet" do + {:ok, hello, ""} = + "test/packet_data/ofp_hello.raw" + |> File.read! + |> Openflow.read + assert hello.version == 4 + assert hello.xid == 0 + assert hello.elements == [versionbitmap: [30, 10, 9, 3, 2, 1]] + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.Hello{}" do + hello = Openflow.Hello.new([30, 10, 9, 3, 2, 1]) + expect = + "test/packet_data/ofp_hello.raw" + |> File.read! + assert Openflow.to_binary(hello) == expect + end + end +end diff --git a/test/ofp_packet_in_test.exs b/test/ofp_packet_in_test.exs new file mode 100644 index 0000000..a59e22e --- /dev/null +++ b/test/ofp_packet_in_test.exs @@ -0,0 +1,87 @@ +defmodule OfpPacketInTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_PACKET_IN packet(with simple matches)" do + {:ok, pktin, ""} = + "test/packet_data/4-4-ofp_packet_in.packet" + |> File.read! + |> Openflow.read + + assert pktin.version == 4 + assert pktin.xid == 0 + assert pktin.total_len == 42 + assert pktin.table_id == 1 + assert pktin.reason == :action + assert pktin.in_port == 6 + assert pktin.match == [ + eth_type: 2054, + eth_dst: "ffffffffffff", + eth_src: "f20ba47df8ea", + arp_op: 1, + arp_spa: {10, 0, 0, 1}, + arp_tpa: {10, 0, 0, 3}, + arp_sha: "f20ba47df8ea", + arp_tha: "000000000000" + ] + end + + test "with OFP_PACKET_IN packet(with complex matches)" do + {:ok, pktin, ""} = + "test/packet_data/4-59-ofp_packet_in.packet" + |> File.read! + |> Openflow.read + + assert pktin.version == 4 + assert pktin.xid == 0 + assert pktin.total_len == 0 + assert pktin.table_id == 200 + assert pktin.reason == :no_match + assert pktin.in_port == 84281096 + assert pktin.match == [ + in_phy_port: 16909060, + metadata: 283686952306183, + eth_type: 2054, + eth_dst: "ffffffffffff", + eth_src: "f20ba47df8ea", + vlan_vid: 999, + ip_dscp: 9, + ip_ecn: 3, + ip_proto: 99, + ipv4_src: {1, 2, 3, 4}, + ipv4_dst: {1, 2, 3, 4}, + tcp_src: 8080, + tcp_dst: 18080, + udp_src: 28080, + udp_dst: 55936, + sctp_src: 48080, + sctp_dst: 59328, + icmpv4_type: 100, + icmpv4_code: 101, + arp_op: 1, + arp_spa: {10, 0, 0, 1}, + arp_tpa: {10, 0, 0, 3}, + arp_sha: "f20ba47df8ea", + arp_tha: "000000000000", + ipv6_src: {65152, 0, 0, 0, 61451, 42239, 65096, 10405}, + ipv6_dst: {65152, 0, 0, 0, 61451, 42239, 65029, 47068}, + ipv6_flabel: 541473, + icmpv6_type: 200, + icmpv6_code: 201, + ipv6_nd_target: {65152, 0, 0, 0, 2656, 28415, 65151, 29927}, + ipv6_nd_sll: "00000000029a", + ipv6_nd_tll: "00000000022b", + mpls_label: 624485, + mpls_tc: 5, + mpls_bos: 1, + pbb_isid: 11259375, + tunnel_id: 651061555542690057, + ipv6_exthdr: [:auth, :frag, :router, :hop, :unrep, :unseq], + onf_pbb_uca: 1, + tun_src: {1, 2, 3, 4}, + tun_dst: {1, 2, 3, 4} + ] + end + end +end diff --git a/test/ofp_packet_out_test.exs b/test/ofp_packet_out_test.exs new file mode 100644 index 0000000..332ba10 --- /dev/null +++ b/test/ofp_packet_out_test.exs @@ -0,0 +1,49 @@ +defmodule OfpPacketOutTest do + use ExUnit.Case + doctest Openflow + + @packet << + 0xf2, 0x0b, 0xa4, 0xd0, 0x3f, 0x70, 0xf2, 0x0b, + 0xa4, 0x7d, 0xf8, 0xea, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x54, 0xf8, 0x1a, 0x00, 0x00, 0xff, 0x01, + 0xaf, 0x8b, 0x0a, 0x00, 0x00, 0x01, 0x0a, 0x00, + 0x00, 0x02, 0x08, 0x00, 0x02, 0x08, 0xf7, 0x60, + 0x00, 0x00, 0x31, 0xd6, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xab, 0x8d, 0x2d, 0x31, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, + 0x2e, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + >> + + describe "Openflow.read/1" do + test "with OFP_PACKET_OUT packet" do + {:ok, pktout, ""} = + "test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet" + |> File.read! + |> Openflow.read + + assert pktout.version == 4 + assert pktout.xid == 0 + assert pktout.buffer_id == :no_buffer + assert pktout.in_port == :controller + assert pktout.actions == [%Openflow.Action.Output{max_len: :no_buffer, port_number: :all}] + assert pktout.data == @packet + end + end + + describe "Openflow.to_binary/1" do + pktout = %Openflow.PacketOut{ + actions: [Openflow.Action.Output.new(port_number: :all)], + data: @packet + } + + expect = + "test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet" + |> File.read! + + assert Openflow.to_binary(pktout) == expect + end +end diff --git a/test/ofp_port_mod_test.exs b/test/ofp_port_mod_test.exs new file mode 100644 index 0000000..e9725d3 --- /dev/null +++ b/test/ofp_port_mod_test.exs @@ -0,0 +1,34 @@ +defmodule OfpPortModTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_PORT_MOD packet(1)" do + binary = File.read!("test/packet_data/4-22-ofp_port_mod.packet") + {:ok, port_mod, ""} = Openflow.read(binary) + + assert port_mod.version == 4 + assert port_mod.xid == 0 + assert port_mod.number == 1 + assert port_mod.hw_addr == "001100001111" + assert port_mod.config == [] + assert port_mod.mask == [] + assert port_mod.advertise == [:fiber] + assert Openflow.to_binary(port_mod) == binary + end + end + + test "with OFP_PORT_MOD packet(2)" do + binary = File.read!("test/packet_data/libofproto-OFP13-port_mod.packet") + {:ok, port_mod, ""} = Openflow.read(binary) + + assert port_mod.version == 4 + assert port_mod.xid == 0 + assert port_mod.number == 1 + assert port_mod.hw_addr == "aabbcc998877" + assert port_mod.config == [:port_down] + assert port_mod.mask == [:port_down] + assert port_mod.advertise == [:"100mb_fd", :copper, :autoneg] + assert Openflow.to_binary(port_mod) == binary + end +end diff --git a/test/ofp_port_status_test.exs b/test/ofp_port_status_test.exs new file mode 100644 index 0000000..fa6b5ea --- /dev/null +++ b/test/ofp_port_status_test.exs @@ -0,0 +1,48 @@ +defmodule OfpPortStatusTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_PORT_STATUS packet" do + {:ok, port_status, ""} = + "test/packet_data/libofproto-OFP13-port_status.packet" + |> File.read! + |> Openflow.read + + assert port_status.version == 4 + assert port_status.xid == 0 + assert port_status.reason == :modify + assert port_status.port.number == 1 + assert port_status.port.hw_addr == "ffffffffffff" + assert port_status.port.name == "eth0" + assert port_status.port.config == [] + assert port_status.port.state == [:live] + assert port_status.port.current_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.advertised_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.peer_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.current_speed == 50_000 + assert port_status.port.max_speed == 100_000 + end + + test "with OFP_PORT_STATUS packet(with kanji port name)" do + {:ok, port_status, ""} = + "test/packet_data/4-39-ofp_port_status.packet" + |> File.read! + |> Openflow.read + + assert port_status.version == 4 + assert port_status.xid == 0 + assert port_status.reason == :add + assert port_status.port.number == 7 + assert port_status.port.hw_addr == "f20ba4d03f70" + assert port_status.port.name == "私のポート" + assert port_status.port.config == [] + assert port_status.port.state == [:live] + assert port_status.port.current_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.advertised_features == [:copper, :autoneg] + assert port_status.port.peer_features == [:"100mb_fd", :copper, :autoneg] + assert port_status.port.current_speed == 5_000 + assert port_status.port.max_speed == 5_000 + end + end +end diff --git a/test/ofp_set_config_test.exs b/test/ofp_set_config_test.exs new file mode 100644 index 0000000..7266c6f --- /dev/null +++ b/test/ofp_set_config_test.exs @@ -0,0 +1,32 @@ +defmodule OfpSetConfigTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_SET_CONFIG packet" do + {:ok, %Openflow.SetConfig{} = config, ""} = + "test/packet_data/ofp_set_config.raw" + |> File.read! + |> Openflow.read + assert config.version == 4 + assert config.xid == 0 + assert config.flags == [] + assert config.miss_send_len == 128 + end + end + + describe "Openflow.to_binary/1" do + test "with %Openflow.SetConfig{}" do + config = %Openflow.SetConfig{ + version: 4, + xid: 0, + flags: [], + miss_send_len: 128 + } + expect = + "test/packet_data/ofp_set_config.raw" + |> File.read! + assert Openflow.to_binary(config) == expect + end + end +end diff --git a/test/ofp_table_mod_test.exs b/test/ofp_table_mod_test.exs new file mode 100644 index 0000000..198e6fc --- /dev/null +++ b/test/ofp_table_mod_test.exs @@ -0,0 +1,28 @@ +defmodule OfpTableModTest do + use ExUnit.Case + doctest Openflow + + describe "Openflow.read/1" do + test "with OFP_TABLE_MOD packet(1)" do + binary = File.read!("test/packet_data/libofproto-OFP13-table_mod.packet") + {:ok, table_mod, ""} = Openflow.read(binary) + + assert table_mod.version == 4 + assert table_mod.xid == 0 + assert table_mod.table_id == :all + assert table_mod.config == 0 + assert Openflow.to_binary(table_mod) == binary + end + end + + test "with OFP_TABLE_MOD packet(2)" do + binary = File.read!("test/packet_data/4-23-ofp_table_mod.packet") + {:ok, table_mod, ""} = Openflow.read(binary) + + assert table_mod.version == 4 + assert table_mod.xid == 0 + assert table_mod.table_id == :all + assert table_mod.config == 0 + assert Openflow.to_binary(table_mod) == binary + end +end diff --git a/test/packet_data/.#nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw b/test/packet_data/.#nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw new file mode 120000 index 0000000..07c9d43 --- /dev/null +++ b/test/packet_data/.#nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw @@ -0,0 +1 @@ +shun159@shun159.19402:1510242034 \ No newline at end of file diff --git a/test/packet_data/4-0-ofp_desc_reply.packet b/test/packet_data/4-0-ofp_desc_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..edfaa3758f10a77b000c9e7394ff9c3ad112dc62 GIT binary patch literal 1072 wcmZP(W-(wu0=a2LNW789%qSmuen$3j@ksZ7YEfol4swW~vQr9B`6G=70Ki)Yt^fc4 literal 0 HcmV?d00001 diff --git a/test/packet_data/4-1-ofp_packet_out.packet b/test/packet_data/4-1-ofp_packet_out.packet new file mode 100644 index 0000000000000000000000000000000000000000..cf6689f86fb304edd844d9c8d95e9883fead74b8 GIT binary patch literal 138 zcmZSKW$0pHU|{$U0e=|;Ks+!O`2YX^pa1{CY=%$VOD@*(s~gVca90L9oL#Q*>R literal 0 HcmV?d00001 diff --git a/test/packet_data/4-10-ofp_hello.packet b/test/packet_data/4-10-ofp_hello.packet new file mode 100644 index 0000000000000000000000000000000000000000..644b4130550011887e285a6dda2f4c17308528cd GIT binary patch literal 16 UcmZQ!U=Uyc0Y(N62L?7i00LExB z4GdgN9Bfc^EDRD*mcal2|9?T*3=9Db3=AxnZRG0Y4H&B7qUa1}sK7f0Mh3A42C$ph z8W=d(K5;Lp{qc%{fq{{M1LQ?UkpEat%(g0z>456NMuC)q#FH3c{$pl101{x3WDsaz z;9+890`nLcxEVOWEFLsk8p@Yq;1DulU|UQ4lrIYAGc$;R>0|~51_1^J1~yn6{r}G(!1|c0BDJC*J|(dv5yWR; wP*#BP8CVz~@eFo92OA?3GYcyy>?Iil{{LrS_xE#S;9_88U|>dxhrclM0c(;Q%be$iUaYz{d88dr9q&R}2gc%nTdA;*tyk4GcU?j7(r2 z0|Pe$2bjf!MoUBaQVbkICJYP=A`Bce$`}|Jgc&$G+!+`cgrM?*3>=Lh0|cOaeg+OM z5MKhy7l-iw|7Q?jU|>jPU}R!uVP#`y5CzLKGl+rdWCjMXJlp^OAk7R63@i*9U>4ZD b9BdG63=9mC3*NfdK*m7nTG0 literal 0 HcmV?d00001 diff --git a/test/packet_data/4-21-ofp_group_mod.packet b/test/packet_data/4-21-ofp_group_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..395547776d43f31a07b52444cd139720adebd3a6 GIT binary patch literal 48 jcmZSKXE0zu07eD{21W)3I0nfGFfcGM{r?YQGB5xDAV30m literal 0 HcmV?d00001 diff --git a/test/packet_data/4-22-ofp_port_mod.packet b/test/packet_data/4-22-ofp_port_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..be7044e1f80dc7b475fc6cece3bd926ea2a78b9c GIT binary patch literal 40 bcmZP(V9;QI07ft+$iN^d2xdVE0Wb{!4VwUg literal 0 HcmV?d00001 diff --git a/test/packet_data/4-23-ofp_table_mod.packet b/test/packet_data/4-23-ofp_table_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..02b8b020a66f057711ab43306382e29e5f329ec1 GIT binary patch literal 16 UcmZP(WDsCrU|{$UA{ZDL016iYB>(^b literal 0 HcmV?d00001 diff --git a/test/packet_data/4-24-ofp_desc_request.packet b/test/packet_data/4-24-ofp_desc_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..b6de05776874572a060194cbc5404213d03122d7 GIT binary patch literal 16 NcmZP(Vh~_J0ssOo04D$d literal 0 HcmV?d00001 diff --git a/test/packet_data/4-25-ofp_aggregate_stats_request.packet b/test/packet_data/4-25-ofp_aggregate_stats_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..645ddacf94b93e7e411ceee6ce82c69bc80412ce GIT binary patch literal 56 hcmZP(Vz6KU0VWW^!0?}ef#E+CKt#|;Mg|s;2mm|k2~7Y1 literal 0 HcmV?d00001 diff --git a/test/packet_data/4-26-ofp_aggregate_stats_reply.packet b/test/packet_data/4-26-ofp_aggregate_stats_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..3d5cd801ade907698d245fcac015f94fb2d295cf GIT binary patch literal 40 ccmZP(X3$^&0VWUu$LtUmlN|#C0~?4901S!%j{pDw literal 0 HcmV?d00001 diff --git a/test/packet_data/4-27-ofp_table_stats_request.packet b/test/packet_data/4-27-ofp_table_stats_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..6fec06a4c03915b3cd8fa0c7d3ea06ec64493594 GIT binary patch literal 16 ScmZP(Vh~^e0cH@vzyJUOLjWoO literal 0 HcmV?d00001 diff --git a/test/packet_data/4-28-ofp_table_stats_reply.packet b/test/packet_data/4-28-ofp_table_stats_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..1da72ab077e5ce5abd54b4c6db2bebc56bc45c7d GIT binary patch literal 64 ecmZP(W^iBt0cH>Z$1G45oMwaw!1)j^0}B8b;sA62 literal 0 HcmV?d00001 diff --git a/test/packet_data/4-29-ofp_port_stats_request.packet b/test/packet_data/4-29-ofp_port_stats_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..d11164010d84790474390b8fbe155d984b1fa9e5 GIT binary patch literal 24 YcmZP(Vvt|}0TvL!!0`Y7|NkHs03I*|F8}}l literal 0 HcmV?d00001 diff --git a/test/packet_data/4-3-ofp_flow_mod.packet b/test/packet_data/4-3-ofp_flow_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..8952a5be41beaf5648dc799070804204003a6b70 GIT binary patch literal 80 zcmZSKV+dft4XPO!{=)%S86$&O0|NsK0|NtF0|N)!C+;P+KVC5~FfcN3FfuSO001Ot B5DfqT literal 0 HcmV?d00001 diff --git a/test/packet_data/4-30-ofp_port_stats_reply.packet b/test/packet_data/4-30-ofp_port_stats_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..8b8c182cad8121491c141bc96eb10c984c56bae4 GIT binary patch literal 240 ucmZP(X86DW0xTc`jM>2?DuIv%i!%m*De4exNXEeIfzmL0U^J3SGMNB!GXfX@ literal 0 HcmV?d00001 diff --git a/test/packet_data/4-31-ofp_group_features_request.packet b/test/packet_data/4-31-ofp_group_features_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..2175bb527e38f8ec5555978298d61a94df63a67a GIT binary patch literal 16 ScmZP(Vh~^e0S*wszyJUOW&ke$ literal 0 HcmV?d00001 diff --git a/test/packet_data/4-32-ofp_group_features_reply.packet b/test/packet_data/4-32-ofp_group_features_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..6f7715cd20012f67a2038b459a0a69e16f9f8770 GIT binary patch literal 56 icmZP(X0TuY0S*uW#{3Kn46KX{3=GJa`TqrEJXsd literal 0 HcmV?d00001 diff --git a/test/packet_data/4-35-ofp_queue_get_config_request.packet b/test/packet_data/4-35-ofp_queue_get_config_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..13072c85a5f005d3808e118b47bc6ad41932d2f6 GIT binary patch literal 16 VcmZP(V-R3qU|{(F|Nnmw4FDWa1SbFh literal 0 HcmV?d00001 diff --git a/test/packet_data/4-36-ofp_queue_get_config_reply.packet b/test/packet_data/4-36-ofp_queue_get_config_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..898f0b3fa0afc2c899d27b17a39b274beac47a54 GIT binary patch literal 211 zcmZP(XSmG3z`*eT|Ns9Wnt_2KnSp`9m%#zbV`LBjv$(({0|OI;&)fpx|Njr+GcYhe zhlnslfb}~H04Inr1Iz%Z2ABgFCqU%?|7Q?_ Ju%HGo0RS3N6bk?V literal 0 HcmV?d00001 diff --git a/test/packet_data/4-37-ofp_queue_stats_request.packet b/test/packet_data/4-37-ofp_queue_stats_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..4baf6d701183d2744d3df53d6a0a8d0b23937e01 GIT binary patch literal 24 UcmZP(Vvt|}0ag&f!0;am04OyGEC2ui literal 0 HcmV?d00001 diff --git a/test/packet_data/4-38-ofp_queue_stats_reply.packet b/test/packet_data/4-38-ofp_queue_stats_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..ad6d19718ed0bfcfd76de6f1ba110c7f21735734 GIT binary patch literal 136 lcmZP(X6Rr50ag$J#_S9X42)nFeu9libs+PY@EgUz003Oj0KEVJ literal 0 HcmV?d00001 diff --git a/test/packet_data/4-39-ofp_port_status.packet b/test/packet_data/4-39-ofp_port_status.packet new file mode 100644 index 0000000000000000000000000000000000000000..63280c7d8c26b88dcda493bb5f560a9f4fc3d957 GIT binary patch literal 80 zcmZSKVF+MA1?(X1C+;N|>&>^W4YHdmcA;fTTc}g@HkXgMmSV0ZMZ) JFbH>mFaUc053T?J literal 0 HcmV?d00001 diff --git a/test/packet_data/4-4-ofp_packet_in.packet b/test/packet_data/4-4-ofp_packet_in.packet new file mode 100644 index 0000000000000000000000000000000000000000..ebd12b51faf33856292db1bb7c7cfa7ac49b8344 GIT binary patch literal 148 zcmZSKVwl1J0Za^9jEoG7Ow3>|BSSy~0|N^K0|Q$F0~ZqqTLS|d+kY@-2cnr97!2U@Mr>fy7#JA9TK_}zaj-Ekaxk#5Ffc;+Al(pk ITnr4%0OK(uDF6Tf literal 0 HcmV?d00001 diff --git a/test/packet_data/4-40-ofp_flow_removed.packet b/test/packet_data/4-40-ofp_flow_removed.packet new file mode 100644 index 0000000000000000000000000000000000000000..677c2c4ae0c3b3a435bcbafbc8689cca3f208775 GIT binary patch literal 64 zcmZSKW^iCY0{{PmIm}EinN~0`gV`X05kxRBFoZEMGVnDpu(5sOUQ+wx6$1kR(B}x- literal 0 HcmV?d00001 diff --git a/test/packet_data/4-41-ofp_error_msg_experimenter.packet b/test/packet_data/4-41-ofp_error_msg_experimenter.packet new file mode 100644 index 0000000000000000000000000000000000000000..6d866b9a9f23d296782399d97342e8290d94a876 GIT binary patch literal 27 icmZQ!WRPZHU|{(F|5XA5zmt7dW_EUJo8>&09FPD21W)326hGp24)5Z27V9?00_eXVgLXD literal 0 HcmV?d00001 diff --git a/test/packet_data/4-44-ofp_set_async.packet b/test/packet_data/4-44-ofp_set_async.packet new file mode 100644 index 0000000000000000000000000000000000000000..de0aa1a433fcb8f5d7085d41ca6d9dc4c77fac5b GIT binary patch literal 32 gcmZRPVNhUz09FPD21W)326hGp24)5Z27V9?00`m$V*mgE literal 0 HcmV?d00001 diff --git a/test/packet_data/4-45-ofp_meter_mod.packet b/test/packet_data/4-45-ofp_meter_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..1ffbcf7e880ca077ec527c15cf910d2d9ec207e6 GIT binary patch literal 64 vcmZRPWpH4C06qo=h7<-y1_1^J<`)bM3|wGgCIp|6fq~)we*~X_f%!QAn#2ar literal 0 HcmV?d00001 diff --git a/test/packet_data/4-46-ofp_flow_mod.packet b/test/packet_data/4-46-ofp_flow_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..496f1da089f8f0717eb0537438fd94f4142cc703 GIT binary patch literal 96 zcmZSKV@P1Y0vI7;)eH>(;Q%be$iUaYz{d88dr9q&R}2gcYz!O>3=E76%nTA>c?Jdn Kkm&#a5FP+Ytr5Ne literal 0 HcmV?d00001 diff --git a/test/packet_data/4-47-ofp_meter_config_request.packet b/test/packet_data/4-47-ofp_meter_config_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..21a11e1048af78f5bccd2fce2143d03e476258a4 GIT binary patch literal 24 YcmZP(Vvt|}0WJ{1!0`Y7|NkHs03M12H2?qr literal 0 HcmV?d00001 diff --git a/test/packet_data/4-48-ofp_meter_config_reply.packet b/test/packet_data/4-48-ofp_meter_config_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..3f44326946aab32f4a412c02957b8c8ac44b8763 GIT binary patch literal 40 ncmZP(X3$^&0WJ{1z`!8Ez{kMAkix*oAi%)D{DOgj0W1Rm9rOX( literal 0 HcmV?d00001 diff --git a/test/packet_data/4-49-ofp_meter_stats_request.packet b/test/packet_data/4-49-ofp_meter_stats_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..3b321aff0bad23851f240a1230d273b5c17f74a4 GIT binary patch literal 24 YcmZP(Vvt|}0ZtIX!0`Y7|NkHs03Le;Gynhq literal 0 HcmV?d00001 diff --git a/test/packet_data/4-5-ofp_features_request.packet b/test/packet_data/4-5-ofp_features_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..7bb2775ad7f1e80de10831b9cbc1502d1580c204 GIT binary patch literal 8 PcmZQ!W#C|7U|;|M0CNBm literal 0 HcmV?d00001 diff --git a/test/packet_data/4-50-ofp_meter_stats_reply.packet b/test/packet_data/4-50-ofp_meter_stats_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..2317fc55c654c17a9f7c09a1c909a6f8c5a9c6ff GIT binary patch literal 72 ccmZP(X7FGD0ZtGB#wiRIU>Y~U9*iLe078QSUjP6A literal 0 HcmV?d00001 diff --git a/test/packet_data/4-51-ofp_meter_features_request.packet b/test/packet_data/4-51-ofp_meter_features_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..c4007bfb7426d8b0251574d991ef1cc6f10ab386 GIT binary patch literal 16 ScmZP(Vh~^e0d5e%zyJUOdjK*3 literal 0 HcmV?d00001 diff --git a/test/packet_data/4-52-ofp_meter_features_reply.packet b/test/packet_data/4-52-ofp_meter_features_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..fd9e3808d39334079f17294763537970a8085219 GIT binary patch literal 32 icmZP(W>8=N0d5e%z`)4Bz|g?Jz{bG9!2h3tfdK##7XjD+ literal 0 HcmV?d00001 diff --git a/test/packet_data/4-53-ofp_port_desc_request.packet b/test/packet_data/4-53-ofp_port_desc_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..0d859f1d72f70c3c77664bc034ead5d73ed72bc2 GIT binary patch literal 16 ScmZP(Vh~^e0bUTnzyJUOi2yVJ literal 0 HcmV?d00001 diff --git a/test/packet_data/4-54-ofp_port_desc_reply.packet b/test/packet_data/4-54-ofp_port_desc_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..e30e9ae1cf9a51628437c561cab0fe2d9aec8d6e GIT binary patch literal 144 zcmZP(W|+VL0=yssjM+itC+;N|>Z$ugd`^Aq$(Ix7#KhVk;wmO03-!AfPq1VfsuiQ zfr){Ifti5?3|Sdi7}ywCKx!D67?~KEm|0la*f}`4xOsT__yq)oghfQf#3dx9q-A8~ z>V7PoLyYq+&w(KynTHA`~w1m zfX!XqN1qGMv?;u8{+l2cOC(lau%vU76t@(T)!ic3n%$}1|Xs%vWN>KhuHnp;}i z+B-VCx_f&2`X@}BGo;uNw0XRJbd){ z$~I}^!dxzZ{L6X{Pp|K-+$mxU|?WjaA5$EVhk({q6{ny{0uA% z0t_q+;tVVd5)3R1+zc!XJPa%hf($GSLJTYn!VD}7A`C1HQVc8%(hMvNlF;yEqmnrs z3^N)S7#JEDSQr`@*cciZI2aljxELB3co-TO_!t@(1Q;3^gcuqaL>L+v#26YFBp4bP zq!=0)WEdJ4X?1j2Idi zOc)v%%orLNEEpOXtQZ;?Y#16C>=+st92goHoERDyTo@V{+!z`dJQx}nycig`2C2Jv z2C2LF2B*9J|6|}`U|>jvrhbP1{}=?Id{D8$`2Qb+2$avnzyL0bKr{nO1V|daJTinC z1EOP zlRqPRhG?`r87)snRC&SwO08d@(1u*g!2$EWu*P1O?^E&ylW+`Jkh4kQ1g0gyP@00ssb z21W)J1||j;24)5pFl1$5VPIom0jXhNVq{`uVrF4wW9Q)H;^yJy;};MV5*85^6PJ*b zl9rK`lUGnwQdUt_Q`gYc($>+{(>E|QGBzQN=`{lOV7y6%FfBn%P%M_DlRE4E3c@ms;;T6t8Zv* zYHn$5Ywzgn>h9_7>z^=j(&Q;qr%j(RbJpxRbLY)puyE1hB}NRWEt>3V5 z)8;K(w{73CbJy-Yd-v@>aPZLKBS()NKXLNZ=`&}~oxgDL(&Z~xuU)@!^VaPo;%Tz5np>)8{W=zkUDl^Vjb`fB%6)fq{XA!G!@tiZQS-h%&G+ z@H4P52r#fPh%>M-NHDN4a5Jzl@G!722r{rR2r;lQ2s5xSh%m4)NHMT5NHef7NJ7Jt zjY{TlFwAIRU|?uqU}0!rU}I=t;9zKA;9_WC;9+QB;A3cD5MXFv5MpRx5MgLw5MyXy zkYH$FkYZ?HkYQ+GkYi|IP+(|aP-19cP+@3bP-AFd&|qj_&|+v{&|zp`&|_#|Fkon4 zFk)z6Fkxt5Fk@(7uwZCluwrOnuwiImuw!UoaA0U)aAIg+aA9a*aARm-@L*_Q@M2)# z8l>*x8Kmyw8=UU?|Br!(fq@|vn)(_3|6>q<@ z2DhC2a)pq@q?}X*g9-yfm@#k~tvLD*l7Q$WSRRcQN1(tOEssXaBU+T-gS0$i2>=%_ zXyu9#C?}v`3d$9R5#7oeEl)6qG0bM)VlWXn8VPo{XsSgcV#~pq3}5U@>HZg7W0=h#t}%El)fXy=PL!Nmq@d146` bLnbIFPkxT%<#40x;YQcP(PaJLAT3V-y202j literal 0 HcmV?d00001 diff --git a/test/packet_data/4-57-ofp_group_stats_request.packet b/test/packet_data/4-57-ofp_group_stats_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..f59329e00aaff41ba8f83c3d92fc6269fb45e516 GIT binary patch literal 24 YcmZP(Vvt|}0X7i9!0`Y7|34rW03JUCE&u=k literal 0 HcmV?d00001 diff --git a/test/packet_data/4-58-ofp_group_stats_reply.packet b/test/packet_data/4-58-ofp_group_stats_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..fe5a549a1b8b7ae87d4e3f00fe3f9db5cfffd403 GIT binary patch literal 72 ycmZP(X7FGD0X7i9z`$SurWqL+7?{8;2vH583@jNK7&ujY)gD0j46h(GrzQYpvjiak literal 0 HcmV?d00001 diff --git a/test/packet_data/4-59-ofp_packet_in.packet b/test/packet_data/4-59-ofp_packet_in.packet new file mode 100644 index 0000000000000000000000000000000000000000..f0879a371a750afbdf63825afbf92486b2c33009 GIT binary patch literal 378 zcmZSKVyt3dU|{$FA{ZD>FfcMPgPDws@eK?NEUawo91RRiER0OdEDa1S93Xj+U;_ge z69-!Z0~_0aFlb=lVEe?qr1r9~783(w1A`6= z7Xyga1JTS430wn0Z7gSi~s-t literal 0 HcmV?d00001 diff --git a/test/packet_data/4-60-ofp_flow_mod.packet b/test/packet_data/4-60-ofp_flow_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..0ca9835144dfa93de0ddfcc1ef693a61f7086b23 GIT binary patch literal 400 zcmZSKW1PT%1u#Oysu>vm!vR=`kuko3fq{jUjh&-`fr*8YiJ7H=frWzs#9(D(Z(!hJ z;$UlFU}J-5Yhd7D`^3GZ_Q$IR1|BBn=M4-3jGPS&LX6A}3?hul4GdyXBP5`-6qEdf z1_l`>w*?Iha!k1!8WR=^!z{rgBoK>1A_)*Y6F8769Z!dgANN9 z1BliG(aa4D25>8l*uaivXkaiA_}2hof8bv7|DT7((gp@IR58}=cN!QhSQt2*6&o0= z7*8}X*f5@KV6cO$;!4Q-|F6E}c>{w3*j%Pr4Gc~YmUaV!3kw5hdujuN8zXB2g9jsH t1A`ay>a*_~7<@Q5p`d}mkBRZi|Nji^{(f!@Tnvm1jCNpuGcYe+>}! z2ks^R|9NOEZD24%6=U6gr-8wOg@MCav4O#g@k9fI4dclM20OSau7te*|LRMgH!wJW z&1IU^z~BU7X*V#qurP48r#3LSF|sx=crY?HFnBSqKKs6b!H0tr3K|&vm>9qO|Ife< V3TrL~Mg~SZu)i4?9UwHwi2$lBTz&um literal 0 HcmV?d00001 diff --git a/test/packet_data/4-7-ofp_set_config.packet b/test/packet_data/4-7-ofp_set_config.packet new file mode 100644 index 0000000000000000000000000000000000000000..43602b3e0a4971a15b5a7341a7f03b9ad33cf2ee GIT binary patch literal 12 OcmZSKWZ+?dfCc~oivXGc literal 0 HcmV?d00001 diff --git a/test/packet_data/4-8-ofp_get_config_request.packet b/test/packet_data/4-8-ofp_get_config_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..78ad50199398471bcbd90f66ede4d34bf8364531 GIT binary patch literal 8 PcmZQ!XW(F9U|;|M0D%A$ literal 0 HcmV?d00001 diff --git a/test/packet_data/4-9-ofp_get_config_reply.packet b/test/packet_data/4-9-ofp_get_config_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..b23f7bf374bf6247002bd240151ace3fa7797131 GIT binary patch literal 12 OcmZSKVBleZfCc~ofB=~Q literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-echo_reply.packet b/test/packet_data/libofproto-OFP13-echo_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..e54d3802b56c8cb01625cb8879c414accea2e2bb GIT binary patch literal 8 PcmZQ!X5e68U|;|M0A&CW literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-echo_request.packet b/test/packet_data/libofproto-OFP13-echo_request.packet new file mode 100644 index 0000000000000000000000000000000000000000..a13c95dd86833256d5c516f2d1c439a7178dc687 GIT binary patch literal 8 PcmZQ!V&Gt4U|;|M0A2tO literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-error_msg.packet b/test/packet_data/libofproto-OFP13-error_msg.packet new file mode 100644 index 0000000000000000000000000000000000000000..33b43ca19d14ab0a4f139cc5c4159cd92780b038 GIT binary patch literal 20 WcmZQ!WDsEh0Tu=}7Cr_J5EB3b*8n~M literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-features_reply.packet b/test/packet_data/libofproto-OFP13-features_reply.packet new file mode 100644 index 0000000000000000000000000000000000000000..1e5dee82ca65b95e8215c982a7c48946238ff111 GIT binary patch literal 32 YcmZQ!V^Cl~07eD|hX4N|e18xH03Uqn+a literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-flow_mod.packet b/test/packet_data/libofproto-OFP13-flow_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..8c86982f4bbc9b0ef9bb040375c41381bb642480 GIT binary patch literal 256 zcmZSKV`KmUA(OC*S$pn%_zwk4AVF-Hk-@Ejfq{jAVfEPt1~#@;yU)z*C~sikV&Y(E zVBldAe%`_#5&%F=-p@0P>hz&C`xHT{^urM&JKHI>+#x;H literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-flow_removed.packet b/test/packet_data/libofproto-OFP13-flow_removed.packet new file mode 100644 index 0000000000000000000000000000000000000000..c98a8f2b48332d939be3b567a53c35712d862ace GIT binary patch literal 120 zcmZSKW~g9bU|*(s~gVca90L9oL#Q*>R literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-packet_in.packet b/test/packet_data/libofproto-OFP13-packet_in.packet new file mode 100644 index 0000000000000000000000000000000000000000..817b51c0b3bea093abd46672e670428083461869 GIT binary patch literal 70 zcmZSKVsK-CfD_Cw7*Zfi1_lO328{*=1{MZ})n^+Rd^jNDhXWWG?N|=1U}9!qbO6yT K3=A3h>8Sul`U`#l literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-port_mod.packet b/test/packet_data/libofproto-OFP13-port_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..105a27eea8bf7464641c365541cab3c1fecfad26 GIT binary patch literal 40 icmZP(V9;QI07ekCYWJC$9pxYv1BlPaz@Wha;sF3$k_0&b literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-port_status.packet b/test/packet_data/libofproto-OFP13-port_status.packet new file mode 100644 index 0000000000000000000000000000000000000000..7a2c14c449946296a0a2a627f723ecb7b1cb3ddc GIT binary patch literal 80 ucmZSKVF+MgU|?VZ5fID>qW*&c14C*_h5?9=hFKUGG&oT4;Q$84wgmvSU<%Cu literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-set_config.packet b/test/packet_data/libofproto-OFP13-set_config.packet new file mode 100644 index 0000000000000000000000000000000000000000..43602b3e0a4971a15b5a7341a7f03b9ad33cf2ee GIT binary patch literal 12 OcmZSKWZ+?dfCc~oivXGc literal 0 HcmV?d00001 diff --git a/test/packet_data/libofproto-OFP13-table_mod.packet b/test/packet_data/libofproto-OFP13-table_mod.packet new file mode 100644 index 0000000000000000000000000000000000000000..02b8b020a66f057711ab43306382e29e5f329ec1 GIT binary patch literal 16 UcmZP(WDsCrU|{$UA{ZDL016iYB>(^b literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_bundle.raw b/test/packet_data/nx_bundle.raw new file mode 100644 index 0000000000000000000000000000000000000000..5599f3721f2283b098a84801cc6e67f505078b36 GIT binary patch literal 40 gcmezWpFx9xL0N%;hk=m+0+<+>APg|c!oUHd0bh&)hX4Qo literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_bundle_load.raw b/test/packet_data/nx_bundle_load.raw new file mode 100644 index 0000000000000000000000000000000000000000..8fc3404780755363316f7602ede6f919df649c03 GIT binary patch literal 40 lcmezWpFx9xL0N%;mw}N10+<+>7~~lk8Cbwv76uLw4FF;N0;~W4 literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_clone.raw b/test/packet_data/nx_clone.raw new file mode 100644 index 0000000000000000000000000000000000000000..47e62ddfdbbd8cf24594ea6b21c6add95615075a GIT binary patch literal 32 fcmezWpFx3vL0N%8iva={88{eN!AuSYE(QhwP3Z!x literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_controller.raw b/test/packet_data/nx_controller.raw new file mode 100644 index 0000000000000000000000000000000000000000..a66c40744bd62dfcb783685821846b2d71fd4f7d GIT binary patch literal 16 XcmezWpFx0uL0N%8gyoW$9uorqC*uQx literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_controller2.raw b/test/packet_data/nx_controller2.raw new file mode 100644 index 0000000000000000000000000000000000000000..0921fa8a9e5bcdcd70839d674d66dcb42d99e3fb GIT binary patch literal 64 zcmezWpTU8FL0N%8l>r7gSS~RzFfcN3i0LsfFfcK&GJ#~688{i4m|0jsY!GH)U;$A8 D*iHlf literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(alg=ftp).raw b/test/packet_data/nx_ct(alg=ftp).raw new file mode 100644 index 0000000000000000000000000000000000000000..3549456376ace287a0f35872a879ae67c5852269 GIT binary patch literal 24 XcmezWpFx6wL0N%8nE?v^gJ@9zJ(B~G literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(alg=tftp).raw b/test/packet_data/nx_ct(alg=tftp).raw new file mode 100644 index 0000000000000000000000000000000000000000..c6b5a9080513d8f08c74c9182299880517bfa5c6 GIT binary patch literal 24 XcmezWpFx6wL0N%8nE?v^gJ@R(J;MXR literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit).raw b/test/packet_data/nx_ct(commit).raw new file mode 100644 index 0000000000000000000000000000000000000000..e9e8956d6bb18f66d9f753304b9b769bbc221d81 GIT binary patch literal 24 YcmezWpFx6wL0N%8nSqf34E}>j06mKXeEj06nDxe*gdg literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw b/test/packet_data/nx_ct(commit,exec(load:0->NXM_NX_CT_LABEL[64..127],load:0x1d->NXM_NX_CT_LABEL[0..63])).raw new file mode 100644 index 0000000000000000000000000000000000000000..02667c5309d699858847f6790fa63086634a01c0 GIT binary patch literal 72 wcmezWpTUEHL0N%8nSqf34E{sN{|pjfadrWF2F4o#U_k~3hW~JJ24rzr0L?=R>Hq)$ literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw b/test/packet_data/nx_ct(commit,exec(load:0xf009->NXM_NX_CT_MARK[])).raw new file mode 100644 index 0000000000000000000000000000000000000000..a780a645fba79c042a8487cda7ab6089d0d38cd6 GIT binary patch literal 48 pcmezWpTU5EL0N%8nSqf34E{sN{|pjfadrlI2F7bFU_pitoB-EK2j~C* literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])).raw b/test/packet_data/nx_ct(commit,force,exec(load:0xf009->NXM_NX_CT_MARK[])).raw new file mode 100644 index 0000000000000000000000000000000000000000..46a06441cc3b6f8a9a4856189525719f1b3b0fcd GIT binary patch literal 48 pcmezWpTU5EL0N%8nSq%B4E{sN{|pjfadrlI2F7bFU_pitoB-F92kHO- literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(dst)).raw b/test/packet_data/nx_ct(commit,nat(dst)).raw new file mode 100644 index 0000000000000000000000000000000000000000..160d2375cf221611a62437e2aeb7c2f4ff4437a4 GIT binary patch literal 40 kcmezWpFx9xL0N%8nSqf34E{sN{|o|PaTNvz1||ju0Giwe1ONa4 literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)).raw b/test/packet_data/nx_ct(commit,nat(dst=10.0.0.128-10.0.0.254,hash)).raw new file mode 100644 index 0000000000000000000000000000000000000000..2dc561df77991255950ccefe8b3cb24efd37f3e5 GIT binary patch literal 48 scmezWpTU5EL0N%8nSqf34E{sN{|pjfaTNvz1}+9>E(V4ME(V5w0MT^^u>b%7 literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(src)).raw b/test/packet_data/nx_ct(commit,nat(src)).raw new file mode 100644 index 0000000000000000000000000000000000000000..028206b1ae5c5ed0057173b9674908528d3e43d3 GIT binary patch literal 40 icmezWpFx9xL0N%8nSqf34E{sN{|o|PaTNvzkPHBt*#-ju literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(src=10.0.0.240,random)).raw b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240,random)).raw new file mode 100644 index 0000000000000000000000000000000000000000..f201bb08bbf6c1a9c16dfd3c9dfc4f3f23ec5606 GIT binary patch literal 48 qcmezWpTU5EL0N%8nSqf34E{sN{|pjfaTNvz20;c!E(V4VAPE4{x(6Nr literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)).raw b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240-10.0.0.254:32768-65535,persistent)).raw new file mode 100644 index 0000000000000000000000000000000000000000..9176b4afe8fb4b7c39484a42bbcadf48b14a4ca2 GIT binary patch literal 56 ycmezWpTUBGL0N%8nSqf34E{sN{|pLXaTNvz237`RE(V4VTnr5V8W{eA6#xJ!5(?S? literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(src=10.0.0.240:32768-65535,random)).raw b/test/packet_data/nx_ct(commit,nat(src=10.0.0.240:32768-65535,random)).raw new file mode 100644 index 0000000000000000000000000000000000000000..ab1b13aa0a9b42bcf24b18074cc5b3c92aa596d7 GIT binary patch literal 48 tcmezWpTU5EL0N%8nSqf34E{sN{|pjfaTNvz20;cxE(V4V4GjPP0|41)3CsWh literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)).raw b/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random)).raw new file mode 100644 index 0000000000000000000000000000000000000000..fd1eb9c4871e152252b0d61e4e1153334fd21b13 GIT binary patch literal 80 zcmezWpCN#OL0N%8nSqf34E{sN{|pvjaTNvz20;d!e+^(^1|}ZO|NlA|7*Y8PyBYoq HfE54$jC2uC literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))fd.raw b/test/packet_data/nx_ct(commit,nat(src=[fe80::20c:29ff:fe88:1]-[fe80::20c:29ff:fe88:a18b]:255-4096,random))fd.raw new file mode 100644 index 0000000000000000000000000000000000000000..fd1eb9c4871e152252b0d61e4e1153334fd21b13 GIT binary patch literal 80 zcmezWpCN#OL0N%8nSqf34E{sN{|pvjaTNvz20;d!e+^(^1|}ZO|NlA|7*Y8PyBYoq HfE54$jC2uC literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)).raw b/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:1-fe80::20c:29ff:fe88:a18b,random)).raw new file mode 100644 index 0000000000000000000000000000000000000000..7f1089840a278092e384ad30062a35f2b395798a GIT binary patch literal 72 zcmezWpTUEHL0N%8nSqf34E{sN{|p9TaTNvz20;d%e+^(^1|}ZO|NlA|7*Y8Py8-!3 B5C8xG literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)).raw b/test/packet_data/nx_ct(commit,nat(src=fe80::20c:29ff:fe88:a18b,random)).raw new file mode 100644 index 0000000000000000000000000000000000000000..52c9c6cdea1276bad573fb1590f2e1da9ebc68f9 GIT binary patch literal 56 ycmezWpTUBGL0N%8nSqf34E{sN{|pLXaTNvz20;dve+^(^1|}ZO|NlA`b^`z=z6+fI literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(nat).raw b/test/packet_data/nx_ct(nat).raw new file mode 100644 index 0000000000000000000000000000000000000000..86595c327e97066f32c79ff1abc0cb338f613c00 GIT binary patch literal 40 gcmezWpFx9xL0N%8nE?v^gX#bO83e%MDqt=H0Ghc50RR91 literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(table=10).raw b/test/packet_data/nx_ct(table=10).raw new file mode 100644 index 0000000000000000000000000000000000000000..72c42ec89eca89cafd5539bc5f67ce0839d37dba GIT binary patch literal 24 WcmezWpFx6wL0N%8nE?v8z%&3izygN= literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(zone=10).raw b/test/packet_data/nx_ct(zone=10).raw new file mode 100644 index 0000000000000000000000000000000000000000..5f43a0c489fb5fe83a42ed569d92e2aa6294d07b GIT binary patch literal 24 XcmezWpFx6wL0N%8nE?X0{)1@%J;Vcs literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct(zone=NXM_NX_REG0[0..15]).raw b/test/packet_data/nx_ct(zone=NXM_NX_REG0[0..15]).raw new file mode 100644 index 0000000000000000000000000000000000000000..d91f148608ad4da353cf075da9a460064911b0aa GIT binary patch literal 24 dcmezWpFx6wL0N%8nSp_Uk%5JQ|33o=002G~1CRg! literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct.raw b/test/packet_data/nx_ct.raw new file mode 100644 index 0000000000000000000000000000000000000000..96358a5cad15c91bf69e442022a26db1368932dd GIT binary patch literal 24 WcmezWpFx6wL0N%8nE?v^gJ}Rgegk{} literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_ct_clear.raw b/test/packet_data/nx_ct_clear.raw new file mode 100644 index 0000000000000000000000000000000000000000..8cfac67010409a3f312ebe9ba0774db84a2189fb GIT binary patch literal 16 TcmezWpFx0uL0N%8n*j^}BWwbF literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_dec_ttl.raw b/test/packet_data/nx_dec_ttl.raw new file mode 100644 index 0000000000000000000000000000000000000000..6b77e1bbd848b0c3bca0f0ee7719afb7c148469a GIT binary patch literal 16 TcmezWpFx0uL0N%8hye@$BE14+ literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_dec_ttl_cnt_ids.raw b/test/packet_data/nx_dec_ttl_cnt_ids.raw new file mode 100644 index 0000000000000000000000000000000000000000..5338bdf01eb64bc4f78caf8d70b26c9196aa6c46 GIT binary patch literal 32 lcmezWpFx3vL0N%8l!29jfq|ic!N8Isis>&4h{M3Z003q81i}CS literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_exit.raw b/test/packet_data/nx_exit.raw new file mode 100644 index 0000000000000000000000000000000000000000..f6accc15ebe9c254e2b32ce6d3b2b7628d718284 GIT binary patch literal 16 TcmezWpFx0uL0N%8kO2$;BDMl! literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_fin_timeout.raw b/test/packet_data/nx_fin_timeout.raw new file mode 100644 index 0000000000000000000000000000000000000000..a4cf557a10f9f0e25c5a9e959a2f6ce55d294520 GIT binary patch literal 16 XcmezWpFx0uL0N%8n1PEygn5_X7#J8CIKU(e0}lfO2NMGW Wlr&&qU||FCnAjK?1OymB>KOq0D+mJs literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_multipath.raw b/test/packet_data/nx_multipath.raw new file mode 100644 index 0000000000000000000000000000000000000000..2dcc91747a58e7648e8a29b85d11133b45a79eca GIT binary patch literal 32 dcmezWpFx3vL0N%;i-Cc`hyfYMGcYo+002&H0>S_Q literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_note.raw b/test/packet_data/nx_note.raw new file mode 100644 index 0000000000000000000000000000000000000000..19be9f79e1191f8b5b36be3d590fa9c9467190ee GIT binary patch literal 16 XcmezWpFx0uL0N%;L-6ISwdtP$Et&=O literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_output_reg.raw b/test/packet_data/nx_output_reg.raw new file mode 100644 index 0000000000000000000000000000000000000000..0dd869edad2208dd000c1ca84111b0033ffd0aff GIT binary patch literal 24 ccmezWpFx6wL0N%;pV5_pk%{I1e+DoB07)AJv;Y7A literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_output_trunc.raw b/test/packet_data/nx_output_trunc.raw new file mode 100644 index 0000000000000000000000000000000000000000..29631eb3a8a435e67d38743cce5f59758d656b68 GIT binary patch literal 16 XcmezWpFx0uL0N%8oq>^ofguF|Be??J literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_packet_in2.raw b/test/packet_data/nx_packet_in2.raw new file mode 100644 index 0000000000000000000000000000000000000000..d293c64a199c39afef176d75de674eef8a0de718 GIT binary patch literal 152 zcmZQ%VVJ=H2FeNy3=DD%3=AesV9?MA1`G@V91N}u3>siXY(}iX!NI{HAt50Qp$uVQ zF$lrPz`?-4;K0BHq8UXPm>F2vK{6~10wC(&on14^LW~3$SQ%IuK>}rO@B4Pqp literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_reg_load.raw b/test/packet_data/nx_reg_load.raw new file mode 100644 index 0000000000000000000000000000000000000000..c5f0b4e9956a2b2e5bd871ed8dcf15137e7d37cf GIT binary patch literal 24 ccmezWpFx6wL0N%;oq?Z$frE(w3_frI066Xga{vGU literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_reg_move.raw b/test/packet_data/nx_reg_move.raw new file mode 100644 index 0000000000000000000000000000000000000000..863a2042d5dc51bfe0fa34971bd445276b2ba19f GIT binary patch literal 24 acmezWpFx6wL0N%;jX{6`0+<*WIG6x7y#jmy literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_resubmit.raw b/test/packet_data/nx_resubmit.raw new file mode 100644 index 0000000000000000000000000000000000000000..503688437d0d9511d57df2f2c37133b4de77584c GIT binary patch literal 16 XcmezWpFx0uL0N%;k%5(gfq?-4B3}Yn literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_resubmit_table.raw b/test/packet_data/nx_resubmit_table.raw new file mode 100644 index 0000000000000000000000000000000000000000..a115cfde120e165dd6b1de012f3e3978431bd343 GIT binary patch literal 16 XcmezWpFx0uL0N%;kAaJofq?-4BIg2c literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_resubmit_table_ct.raw b/test/packet_data/nx_resubmit_table_ct.raw new file mode 100644 index 0000000000000000000000000000000000000000..ff974d5e67c71771b6891ab546b16bf7e8d11ecb GIT binary patch literal 16 XcmezWpFx0uL0N%8hk=Wgfq?-4Be?>N literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_sample.raw b/test/packet_data/nx_sample.raw new file mode 100644 index 0000000000000000000000000000000000000000..44c243768606aee9bbcf0a951fcd089bbe3ce244 GIT binary patch literal 24 fcmezWpFx6wL0N%8*1(d1A$kD=LpwVI!=^j{O8f8U|?ooP*z}IU=U$|fNB5-xB&J5 literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_set_packet_in_format.raw b/test/packet_data/nx_set_packet_in_format.raw new file mode 100644 index 0000000000000000000000000000000000000000..ae11499d67c89af872223d559e5ef583961208e7 GIT binary patch literal 20 bcmZQ!VGvsP= literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_set_tunnel.raw b/test/packet_data/nx_set_tunnel.raw new file mode 100644 index 0000000000000000000000000000000000000000..baa9a5cf722363f37af05f5ecaf0df37eecf81b2 GIT binary patch literal 16 XcmezWpFx0uL0N%;iGe}LB&-4eBvk`w literal 0 HcmV?d00001 diff --git a/test/packet_data/nx_set_tunnel64.raw b/test/packet_data/nx_set_tunnel64.raw new file mode 100644 index 0000000000000000000000000000000000000000..01068744f8ce21595c91841d55052746289d86ee GIT binary patch literal 24 ccmezWpFx6wL0N%;lK~8ls9E@W9<@yX07OOweEn+a literal 0 HcmV?d00001 diff --git a/test/packet_data/ofp_features_request.raw b/test/packet_data/ofp_features_request.raw new file mode 100644 index 0000000000000000000000000000000000000000..7bb2775ad7f1e80de10831b9cbc1502d1580c204 GIT binary patch literal 8 PcmZQ!W#C|7U|;|M0CNBm literal 0 HcmV?d00001 diff --git a/test/packet_data/ofp_get_config_reply.raw b/test/packet_data/ofp_get_config_reply.raw new file mode 100644 index 0000000000000000000000000000000000000000..b23f7bf374bf6247002bd240151ace3fa7797131 GIT binary patch literal 12 OcmZSKVBleZfCc~ofB=~Q literal 0 HcmV?d00001 diff --git a/test/packet_data/ofp_get_config_request.raw b/test/packet_data/ofp_get_config_request.raw new file mode 100644 index 0000000000000000000000000000000000000000..78ad50199398471bcbd90f66ede4d34bf8364531 GIT binary patch literal 8 PcmZQ!XW(F9U|;|M0D%A$ literal 0 HcmV?d00001 diff --git a/test/packet_data/ofp_hello.raw b/test/packet_data/ofp_hello.raw new file mode 100644 index 0000000000000000000000000000000000000000..644b4130550011887e285a6dda2f4c17308528cd GIT binary patch literal 16 UcmZQ!U=Uyc0Y(N62L?7i00L_#5&%F=-p@4}IB-+3L<$@`Y2m>R7TLS|F3j@RIvkeSvY^!#k znb}d^z`(`C!O+0K!zBE?fkA@hzzQbD1_mDvunva90St_GATeeJMh6hh!T>Ts0wnwY TKZ5`RgR%mH5(5*%>a*_wuTd^X literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_controller.packet b/test/packet_data/ovs-ofctl-of13-action_controller.packet new file mode 100644 index 0000000000000000000000000000000000000000..8fba90408f0fc6083fe3a8199f16c57ce03603e0 GIT binary patch literal 80 tcmZSKV+dft4O0Fi0kASg1{N^Q!XN>n|NmzYU|>*IU=U$pU|?ir002_`4@&?5 literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_controller2.packet b/test/packet_data/ovs-ofctl-of13-action_controller2.packet new file mode 100644 index 0000000000000000000000000000000000000000..7e6b046f1ca77c25cb1f2cbffb255c15a549974e GIT binary patch literal 128 zcmZSKV`yN&4O0Fi0kASg1{N^Q!r%d-|Nm!jU|>*IU{D3|z?cK9fRTZNfs28Gfr){Y U6(r2ez{$wO%mQYE_$&}}0BZseFaQ7m literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_ct.packet b/test/packet_data/ovs-ofctl-of13-action_ct.packet new file mode 100644 index 0000000000000000000000000000000000000000..5eb733f969fbabe7d5d88ec279422b3d5fd286d0 GIT binary patch literal 104 zcmZSKW5{5@0+_*K4GjO009c5TL9Bs+i;07Qf$=g2ScHK=fq{X61xzyh|IZ-7z@V(a Rpv=I)z{tSDD9r*^1OWR95nccQ literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_ct_exec.packet b/test/packet_data/ovs-ofctl-of13-action_ct_exec.packet new file mode 100644 index 0000000000000000000000000000000000000000..3f0b3431295f706a18cb371ac1f72a0b1ed38b96 GIT binary patch literal 120 zcmZSKW2j)j0+_*K4GjO009c5TL9Bs+i;07Qf$=g20|SE+6tgfGfaL%GXV73^P*z}2 aW?%&I85sUUC`kqZ2F7bF45`kFAOQeJq7+&H literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_ct_nat.packet b/test/packet_data/ovs-ofctl-of13-action_ct_nat.packet new file mode 100644 index 0000000000000000000000000000000000000000..e210a38c2e03328dcaaae464f76b84b6e04d510e GIT binary patch literal 128 zcmZSKV`yN&0+_*K4GjO009c5TfvbUmi-`lwVPIfjVQ>I3|Nm#OU|>*IU{GdY1o0Ud h{)4Ii{}~j(;wlUb42%rMT#P&nT#UT`85o(t3IGR07IOdq literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_ct_nat_v6.packet b/test/packet_data/ovs-ofctl-of13-action_ct_nat_v6.packet new file mode 100644 index 0000000000000000000000000000000000000000..94b2aaa98917b8f4ffd49bdcdffa197ba7741713 GIT binary patch literal 144 zcmZSKW0=5z1u%of8W{c~0k9Aw16KnB7gO6^Fpq(Og&_dM`v0H7gMmR=fkByp5yWR; f_z$N3|7S1&i>oj&FfcLjC@{k0!P1PV{Qv&}%DET% literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_dec_ttl_cnt_ids.packet b/test/packet_data/ovs-ofctl-of13-action_dec_ttl_cnt_ids.packet new file mode 100644 index 0000000000000000000000000000000000000000..c4308ccb890634382ae6159630f5c81a3761e5aa GIT binary patch literal 104 zcmZSKW5{5@4O0Fi0kASg2L1*HE+!6!1_lvEHUl N0}}%?0}GVL008$?5Xb-k literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_fintimeout.packet b/test/packet_data/ovs-ofctl-of13-action_fintimeout.packet new file mode 100644 index 0000000000000000000000000000000000000000..78c3eab4248121bf88006b7cd6ae7cc6c80746cf GIT binary patch literal 88 zcmZSKV~AkD4O0Fi0kASg2L1*HE+!6!1_lvEHU%500u@okQg%qqXUR$VE~!X0G1VEfT)sW5P<6B1d0Fu&yc{t zpsc_kz`($$!+_+-WRNri0}seH2C$tB3=9Sg3@mIQ2_`lM1`QsVIEMoRqXNi$Fe#t_ QaXCmG10w@R3IhWJ0Mz>?jsO4v literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_note.packet b/test/packet_data/ovs-ofctl-of13-action_note.packet new file mode 100644 index 0000000000000000000000000000000000000000..9a1491c91665aa88129925c8b5ab61a6aa39e79c GIT binary patch literal 80 tcmZSKV+dft4O0Fi0kASg1{N^Q!XN>n|NmzYU|>*IVBlb3Wn*Vx002_r4@&?5 literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_output_trunc.packet b/test/packet_data/ovs-ofctl-of13-action_output_trunc.packet new file mode 100644 index 0000000000000000000000000000000000000000..491cf4c3adcd8aeedc4ed849ad64dc7a038a8f91 GIT binary patch literal 80 tcmZSKV+dft4O0Fi0kASg1{N^Q!XN>n|NmzYU|>*IU{IHzz`(%50036+5C;GN literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_resubmit.packet b/test/packet_data/ovs-ofctl-of13-action_resubmit.packet new file mode 100644 index 0000000000000000000000000000000000000000..690b978b0ea52cecb39ef793b5f56c8aa3e38d1e GIT binary patch literal 144 zcmZSKW0=5z1u%of8W{c~0k9AwgIfaw0}BJg>az_DY;3D`pPAWF-oU`c#KF+Oz{4c` zyn#W2<-iIi#s&r-4zLb}!vPG8b|5ik21W-E&B6dOK>{TE|38BO1B0>x10Tz!WCjKX E0HxX>(*OVf literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_sample.packet b/test/packet_data/ovs-ofctl-of13-action_sample.packet new file mode 100644 index 0000000000000000000000000000000000000000..c5167ba2179d01ecae17c3bd4979257b3894c559 GIT binary patch literal 88 zcmZSKV~AkD4O0Fi0kASg1{N^Q!k_@6|Nm!@U|>*IV31{CW?*1oWME)mVqjok1_06A B4{HDb literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_sample2.packet b/test/packet_data/ovs-ofctl-of13-action_sample2.packet new file mode 100644 index 0000000000000000000000000000000000000000..444f094b6dd0410357a2a84ffd2227cad3840237 GIT binary patch literal 96 zcmZSKV@P1Y4O0Fi0kASg1{N^Q!k_`7|Nm!DU|>*IU{GUVW?*1oWME)mVqjokmY)EU HWncgRSMm@$ literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_stack_pop.packet b/test/packet_data/ovs-ofctl-of13-action_stack_pop.packet new file mode 100644 index 0000000000000000000000000000000000000000..d6f99e2c4e0a72d1a66c06d4c8202cf3902b4e24 GIT binary patch literal 88 zcmZSKV~AkD4O0Fi0kASg1{N^Q!k_@6|Nm!@U|>*IV31*8WME`rVPFM`GB5xD(iabK literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-action_stack_push.packet b/test/packet_data/ovs-ofctl-of13-action_stack_push.packet new file mode 100644 index 0000000000000000000000000000000000000000..e5d38a320421315685524817663df0443e7a9629 GIT binary patch literal 88 zcmZSKV~AkD4O0Fi0kASg1{N^Q!k_@6|Nm!@U|>*IV31~DWME`rVPFM`GB5xD(g+W4 literal 0 HcmV?d00001 diff --git a/test/packet_data/ovs-ofctl-of13-match_conj.packet b/test/packet_data/ovs-ofctl-of13-match_conj.packet new file mode 100644 index 0000000000000000000000000000000000000000..1f9c57cd62515387dd7b78c21d6958213753a1dd GIT binary patch literal 96 zcmZSKV@P0NU|1i;vZk%1$Hfq?-4Zm%AY literal 0 HcmV?d00001 diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/test/tres_test.exs b/test/tres_test.exs new file mode 100644 index 0000000..24ae030 --- /dev/null +++ b/test/tres_test.exs @@ -0,0 +1,4 @@ +defmodule TresTest do + use ExUnit.Case + doctest Tres +end