From fc02a678de3d47f0288e29408fdc01c489f952f8 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 13 Nov 2017 22:52:53 +0900 Subject: [PATCH 01/50] 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 From e52fe31b79951eaec285effbf15d7a9e92d7064a Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 20 Nov 2017 13:38:24 +0900 Subject: [PATCH 02/50] Implement Openflow Protocol and Callback system --- config/config.exs | 9 +- lib/openflow.ex | 14 ++ lib/openflow/.#nx_packet_in2.ex | 1 - lib/openflow/actions/nx_bundle.ex | 15 +- lib/openflow/actions/nx_bundle_load.ex | 25 ++-- lib/openflow/actions/nx_conjunction.ex | 7 +- lib/openflow/actions/nx_conntrack.ex | 26 +--- lib/openflow/actions/nx_controller.ex | 7 +- lib/openflow/actions/nx_controller2.ex | 15 +- lib/openflow/actions/nx_fin_timeout.ex | 5 +- lib/openflow/actions/nx_flow_spec_load.ex | 18 +-- lib/openflow/actions/nx_flow_spec_match.ex | 18 +-- lib/openflow/actions/nx_flow_spec_output.ex | 12 +- lib/openflow/actions/nx_learn.ex | 29 ++-- lib/openflow/actions/nx_learn2.ex | 36 ++--- lib/openflow/actions/set_field.ex | 2 +- lib/openflow/enums.ex | 23 ++- lib/openflow/match.ex | 4 +- lib/openflow/match/field.ex | 22 +++ lib/openflow/multipart/aggregate/reply.ex | 1 + lib/openflow/multipart/desc/reply.ex | 1 + lib/openflow/multipart/flow/reply.ex | 11 ++ lib/openflow/multipart/flow/request.ex | 4 +- lib/openflow/multipart/group/reply.ex | 11 ++ lib/openflow/multipart/group_desc/reply.ex | 10 ++ .../multipart/group_features/reply.ex | 1 + lib/openflow/multipart/meter/reply.ex | 11 ++ lib/openflow/multipart/port_desc/reply.ex | 33 ++++ lib/openflow/multipart/port_desc/request.ex | 24 +++ lib/openflow/multipart/port_stats/reply.ex | 11 ++ lib/openflow/multipart/queue/reply.ex | 11 ++ lib/openflow/multipart/table/reply.ex | 11 ++ lib/openflow/nx_packet_in2.ex | 141 +++++++++++++++++- lib/tres/application.ex | 3 +- lib/tres/controller.ex | 14 ++ lib/tres/example_handler.ex | 108 ++++++++++++++ lib/tres/message_handler.ex | 52 ------- lib/tres/message_handler_sup.ex | 13 +- lib/tres/message_helper.ex | 88 +++++++++++ lib/tres/messages.ex | 128 ++++++++++++++++ lib/tres/secure_channel.ex | 86 +++++++---- lib/tres/secure_channel_state.ex | 8 +- lib/tres/switch_registry.ex | 6 + lib/tres/utils.ex | 39 ++++- mix.exs | 1 + src/tres_xact_kv.erl | 61 ++++++++ test/flay_test.exs | 5 + test/packet_data/nx_packet_in2_arp.raw | Bin 0 -> 200 bytes 48 files changed, 937 insertions(+), 244 deletions(-) delete mode 120000 lib/openflow/.#nx_packet_in2.ex create mode 100644 lib/openflow/multipart/port_desc/reply.ex create mode 100644 lib/openflow/multipart/port_desc/request.ex create mode 100644 lib/tres/controller.ex create mode 100644 lib/tres/example_handler.ex delete mode 100644 lib/tres/message_handler.ex create mode 100644 lib/tres/message_helper.ex create mode 100644 lib/tres/messages.ex create mode 100644 src/tres_xact_kv.erl create mode 100644 test/flay_test.exs create mode 100644 test/packet_data/nx_packet_in2_arp.raw diff --git a/config/config.exs b/config/config.exs index 93b9ad9..4113ff7 100644 --- a/config/config.exs +++ b/config/config.exs @@ -3,11 +3,12 @@ use Mix.Config config :tres, - callback_module: Tres.ExampleHandler, + protocol: :tcp, + port: 6633, max_connections: 10, - num_acceptors: 10, - protocol: :tcp, - port: 6653 + num_acceptors: 10, + callback_module: Tres.ExampleHandler, + callback_args: [] config :logger, level: :info, diff --git a/lib/openflow.ex b/lib/openflow.ex index a5ca2bc..a0e5f0d 100644 --- a/lib/openflow.ex +++ b/lib/openflow.ex @@ -26,6 +26,10 @@ defmodule Openflow do end end + def to_binary(messages) when is_list(messages) do + binaries = for message <- messages, do: to_binary(message) + Enum.join(binaries, "") + 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) -> @@ -36,6 +40,16 @@ defmodule Openflow do end end + def append_body(nil, message), do: message + def append_body(message, continue) do + mod = message.__struct__ + if function_exported?(mod, :append_body, 2) do + mod.append_body(message, continue) + else + message + end + end + # private functions defp do_read({:error, reason}, _) do diff --git a/lib/openflow/.#nx_packet_in2.ex b/lib/openflow/.#nx_packet_in2.ex deleted file mode 120000 index b4344b8..0000000 --- a/lib/openflow/.#nx_packet_in2.ex +++ /dev/null @@ -1 +0,0 @@ -shun159@shun159.5674:1510580208 \ No newline at end of file diff --git a/lib/openflow/actions/nx_bundle.ex b/lib/openflow/actions/nx_bundle.ex index 6a65bc4..8fcd5f0 100644 --- a/lib/openflow/actions/nx_bundle.ex +++ b/lib/openflow/actions/nx_bundle.ex @@ -14,15 +14,12 @@ defmodule Openflow.Action.NxBundle do 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} + slaves = options[:slaves] || [] + %NxBundle{algorithm: options[:algorithm] || :active_backup, + hash_field: options[:hash_field] || :eth_src, + basis: options[:basis] || 0, + n_slaves: length(slaves), + slaves: slaves} end def to_binary(%NxBundle{algorithm: alg, diff --git a/lib/openflow/actions/nx_bundle_load.ex b/lib/openflow/actions/nx_bundle_load.ex index a76d9f4..b9c4ef1 100644 --- a/lib/openflow/actions/nx_bundle_load.ex +++ b/lib/openflow/actions/nx_bundle_load.ex @@ -19,22 +19,17 @@ defmodule Openflow.Action.NxBundleLoad do 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) + dst_field = 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} + slaves = options[:slaves] || [] + %NxBundleLoad{algorithm: options[:algorithm] || :active_backup, + hash_field: options[:hash_field] || :eth_src, + basis: options[:basis] || 0, + n_slaves: length(slaves), + slaves: slaves, + offset: options[:offset] || 0, + n_bits: options[:n_bits] || default_n_bits, + dst_field: options[:dst_field]} end def to_binary(%NxBundleLoad{algorithm: alg, diff --git a/lib/openflow/actions/nx_conjunction.ex b/lib/openflow/actions/nx_conjunction.ex index 3b90b4d..b9818d6 100644 --- a/lib/openflow/actions/nx_conjunction.ex +++ b/lib/openflow/actions/nx_conjunction.ex @@ -11,10 +11,9 @@ defmodule Openflow.Action.NxConjunction do 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} + %NxConjunction{clause: options[:clause] || 0, + n_clauses: options[:n_clauses] || 0, + id: options[:id] || 0} end def to_binary(%NxConjunction{clause: clause, n_clauses: n_clauses, id: id}) do diff --git a/lib/openflow/actions/nx_conntrack.ex b/lib/openflow/actions/nx_conntrack.ex index 76b033f..4c4dafc 100644 --- a/lib/openflow/actions/nx_conntrack.ex +++ b/lib/openflow/actions/nx_conntrack.ex @@ -18,24 +18,14 @@ defmodule Openflow.Action.NxConntrack do 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 - } + %NxConntrack{flags: options[:flags] || [], + zone_src: options[:zone_src], + zone_imm: options[:zone_imm] || 0, + zone_offset: options[:zone_offset], + zone_n_bits: options[:zone_n_bits], + recirc_table: options[:recirc_table] || 255, + alg: options[:alg] || 0, + exec: options[:exec] || []} end def to_binary(%NxConntrack{ diff --git a/lib/openflow/actions/nx_controller.ex b/lib/openflow/actions/nx_controller.ex index 8128f40..4faf769 100644 --- a/lib/openflow/actions/nx_controller.ex +++ b/lib/openflow/actions/nx_controller.ex @@ -11,10 +11,9 @@ defmodule Openflow.Action.NxController do 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} + %NxController{max_len: options[:max_len] || :no_buffer, + id: options[:id] || 0, + reason: options[:reason] || :action} end def to_binary(%NxController{max_len: max_len, id: controller_id, reason: reason}) do diff --git a/lib/openflow/actions/nx_controller2.ex b/lib/openflow/actions/nx_controller2.ex index 6e1637c..5b932e7 100644 --- a/lib/openflow/actions/nx_controller2.ex +++ b/lib/openflow/actions/nx_controller2.ex @@ -21,16 +21,11 @@ defmodule Openflow.Action.NxController2 do 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} + %NxController2{max_len: options[:max_len] || :no_buffer, + id: options[:id] || 0, + reason: options[:reason] || :action, + userdata: options[:userdata], + pause: options[:pause] || false} end def to_binary(%NxController2{} = ctl) do diff --git a/lib/openflow/actions/nx_fin_timeout.ex b/lib/openflow/actions/nx_fin_timeout.ex index 00a14d4..a94a4dd 100644 --- a/lib/openflow/actions/nx_fin_timeout.ex +++ b/lib/openflow/actions/nx_fin_timeout.ex @@ -10,9 +10,8 @@ defmodule Openflow.Action.NxFinTimeout do 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} + %NxFinTimeout{idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0} end def to_binary(%NxFinTimeout{idle_timeout: fin_idle, hard_timeout: fin_hard}) do diff --git a/lib/openflow/actions/nx_flow_spec_load.ex b/lib/openflow/actions/nx_flow_spec_load.ex index 120a90d..5b35f20 100644 --- a/lib/openflow/actions/nx_flow_spec_load.ex +++ b/lib/openflow/actions/nx_flow_spec_load.ex @@ -14,17 +14,13 @@ defmodule Openflow.Action.NxFlowSpecLoad do 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} + dst = options[:dst] + n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst) + %NxFlowSpecLoad{src: options[:src], + dst: dst, + n_bits: n_bits, + src_offset: options[:src_offset] || 0, + dst_offset: options[:dst_offset] || 0} end def to_binary(%NxFlowSpecLoad{} = fsm) do diff --git a/lib/openflow/actions/nx_flow_spec_match.ex b/lib/openflow/actions/nx_flow_spec_match.ex index 8312fb4..c8e2e1c 100644 --- a/lib/openflow/actions/nx_flow_spec_match.ex +++ b/lib/openflow/actions/nx_flow_spec_match.ex @@ -14,17 +14,13 @@ defmodule Openflow.Action.NxFlowSpecMatch do 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} + dst = options[:dst] + n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst) + %NxFlowSpecMatch{src: options[:src], + dst: dst, + n_bits: n_bits, + src_offset: options[:src_offset] || 0, + dst_offset: options[:dst_offset] || 0} end def to_binary(%NxFlowSpecMatch{} = fsm) do diff --git a/lib/openflow/actions/nx_flow_spec_output.ex b/lib/openflow/actions/nx_flow_spec_output.ex index ae0b18f..75ad422 100644 --- a/lib/openflow/actions/nx_flow_spec_output.ex +++ b/lib/openflow/actions/nx_flow_spec_output.ex @@ -11,13 +11,11 @@ defmodule Openflow.Action.NxFlowSpecOutput do 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} + src = options[:src] + n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(src) + %NxFlowSpecOutput{n_bits: n_bits, + src: src, + src_offset: options[:src_offset] || 0} end def to_binary(%NxFlowSpecOutput{n_bits: n_bits, diff --git a/lib/openflow/actions/nx_learn.ex b/lib/openflow/actions/nx_learn.ex index df6932f..b57f380 100644 --- a/lib/openflow/actions/nx_learn.ex +++ b/lib/openflow/actions/nx_learn.ex @@ -17,24 +17,15 @@ defmodule Openflow.Action.NxLearn do 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} + %NxLearn{idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0, + priority: options[:priority] || 0, + cookie: options[:cookie] || 0, + flags: options[:flags] || [], + table_id: options[:table_id] || 0xff, + fin_idle_timeout: options[:fin_idle_timeout] || 0, + fin_hard_timeout: options[:fin_hard_timeout] || 0, + flow_specs: options[:flow_specs] || []} end def to_binary(%NxLearn{idle_timeout: idle, @@ -52,7 +43,7 @@ defmodule Openflow.Action.NxLearn do 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) + 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)>> diff --git a/lib/openflow/actions/nx_learn2.ex b/lib/openflow/actions/nx_learn2.ex index 4a47ef5..fac6e77 100644 --- a/lib/openflow/actions/nx_learn2.ex +++ b/lib/openflow/actions/nx_learn2.ex @@ -20,30 +20,18 @@ defmodule Openflow.Action.NxLearn2 do 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} + %NxLearn2{idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0, + priority: options[:priority] || 0, + cookie: options[:cookie] || 0, + flags: options[:flags] || [], + table_id: options[:table_id] || 0xff, + fin_idle_timeout: options[:fin_idle_timeout] || 0, + fin_hard_timeout: options[:fin_hard_timeout] || 0, + limit: options[:limit] || 0, + result_dst_offset: options[:result_dst_offset] || 0, + result_dst: options[:result_dst], + flow_specs: options[:flow_specs] || []} end def to_binary(%NxLearn2{idle_timeout: idle, diff --git a/lib/openflow/actions/set_field.ex b/lib/openflow/actions/set_field.ex index ed962ab..a1b0c1d 100644 --- a/lib/openflow/actions/set_field.ex +++ b/lib/openflow/actions/set_field.ex @@ -25,7 +25,7 @@ defmodule Openflow.Action.SetField do 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 + <<_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) diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex index 32f36d4..ab41448 100644 --- a/lib/openflow/enums.ex +++ b/lib/openflow/enums.ex @@ -316,8 +316,9 @@ defmodule Openflow.Enums do ], experimenter_oxm_vendors: [ - nicira_ext_match: 0x00002320, - onf_ext_match: 0x4f4e4600 + nicira_ext_match: 0x00002320, + hp_ext_match: 0x00002428, + onf_ext_match: 0x4f4e4600 ], match_type: [ @@ -618,6 +619,24 @@ defmodule Openflow.Enums do nsh_c4: 9 ], + hp_ext_match: [ + hp_udp_src_port_range: 0, + hp_udp_dst_port_range: 1, + hp_tcp_src_port_range: 2, + hp_tcp_dst_port_range: 3, + hp_tcp_flags: 4, + hp_custom_1: 5, + hp_custom_2: 6, + hp_custom_3: 7, + hp_custom_4: 8 + ], + + hp_custom_match_type: [ + l2_start: 1, + l3_start: 2, + l4_start: 3 + ], + onf_ext_match: [ onf_tcp_flags: 42, onf_actset_output: 43, diff --git a/lib/openflow/match.ex b/lib/openflow/match.ex index baac231..bd40634 100644 --- a/lib/openflow/match.ex +++ b/lib/openflow/match.ex @@ -18,7 +18,7 @@ defmodule Openflow.Match do def read(binary) do <<1::16, no_pad_len::16, binary1::binary>> = binary - padding_length = @match_size - rem(no_pad_len, 8) + padding_length = Openflow.Utils.pad_length(no_pad_len, 8) match_field_len = no_pad_len - @header_size <> = binary1 {decode_fields(match_fields, []), rest} @@ -58,7 +58,7 @@ defmodule Openflow.Match do 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>>), + def header_size(<<0xffff::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _exp_int::32, _::bytes>>), do: 8 # private functions diff --git a/lib/openflow/match/field.ex b/lib/openflow/match/field.ex index 4c45221..8a6e411 100644 --- a/lib/openflow/match/field.ex +++ b/lib/openflow/match/field.ex @@ -275,6 +275,17 @@ defmodule Openflow.Match.Field do def vendor_of(:nsh_c3), do: :nicira_ext_match def vendor_of(:nsh_c4), do: :nicira_ext_match + # HP Ext Match + def vendor_of(:hp_udp_src_port_range), do: :hp_ext_match + def vendor_of(:hp_udp_dst_port_range), do: :hp_ext_match + def vendor_of(:hp_tcp_src_port_range), do: :hp_ext_match + def vendor_of(:hp_tcp_dst_port_range), do: :hp_ext_match + def vendor_of(:hp_tcp_flags), do: :hp_ext_match + def vendor_of(:hp_custom_1), do: :hp_ext_match + def vendor_of(:hp_custom_2), do: :hp_ext_match + def vendor_of(:hp_custom_3), do: :hp_ext_match + def vendor_of(:hp_custom_4), do: :hp_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 @@ -518,6 +529,17 @@ defmodule Openflow.Match.Field do def format_of(:nsh_c3), do: {:be32, :decimal} def format_of(:nsh_c4), do: {:be32, :decimal} + # HP Ext Match + def format_of(:hp_udp_src_port_range), do: {:be32, :decimal} + def format_of(:hp_udp_dst_port_range), do: {:be32, :decimal} + def format_of(:hp_tcp_src_port_range), do: {:be32, :decimal} + def format_of(:hp_tcp_dst_port_range), do: {:be32, :decimal} + def format_of(:hp_tcp_flags), do: {:be16, :tcp_flags} + def format_of(:hp_custom_1), do: {:dynamic, :bytes} + def format_of(:hp_custom_2), do: {:dynamic, :bytes} + def format_of(:hp_custom_3), do: {:dynamic, :bytes} + def format_of(:hp_custom_4), do: {:dynamic, :bytes} + # ONF Ext Match def format_of(:onf_tcp_flags), do: {:be16, :tcp_flags} def format_of(:onf_actset_output), do: {:be32, :openflow13_port} diff --git a/lib/openflow/multipart/aggregate/reply.ex b/lib/openflow/multipart/aggregate/reply.ex index bcc208f..7bb7beb 100644 --- a/lib/openflow/multipart/aggregate/reply.ex +++ b/lib/openflow/multipart/aggregate/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Aggregate.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], packet_count: 0, byte_count: 0, diff --git a/lib/openflow/multipart/desc/reply.ex b/lib/openflow/multipart/desc/reply.ex index db3c818..7794428 100644 --- a/lib/openflow/multipart/desc/reply.ex +++ b/lib/openflow/multipart/desc/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Desc.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], mfr_desc: "", hw_desc: "", diff --git a/lib/openflow/multipart/flow/reply.ex b/lib/openflow/multipart/flow/reply.ex index 5d41dbc..41941f0 100644 --- a/lib/openflow/multipart/flow/reply.ex +++ b/lib/openflow/multipart/flow/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Flow.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], flows: [] ) @@ -19,6 +20,16 @@ defmodule Openflow.Multipart.Flow.Reply do flows = Openflow.Multipart.FlowStats.read(flows_bin) %Reply{flows: flows} end + + def append_body(%Reply{flows: flows} = message, %Reply{flags: [:more], flows: continue}) do + %{message|flows: [continue|flows]} + end + def append_body(%Reply{flows: flows} = message, %Reply{flags: [], flows: continue}) do + new_flows = [continue|flows] + |> Enum.reverse + |> List.flatten + %{message|flows: new_flows} + end end defmodule Openflow.Multipart.FlowStats do diff --git a/lib/openflow/multipart/flow/request.ex b/lib/openflow/multipart/flow/request.ex index 9485c63..53e8146 100644 --- a/lib/openflow/multipart/flow/request.ex +++ b/lib/openflow/multipart/flow/request.ex @@ -16,13 +16,13 @@ defmodule Openflow.Multipart.Flow.Request do def ofp_type, do: 18 - def new(options) do + 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, []) + match = Keyword.get(options, :match, Openflow.Match.new) %Request{table_id: table_id, out_port: out_port, out_group: out_group, diff --git a/lib/openflow/multipart/group/reply.ex b/lib/openflow/multipart/group/reply.ex index 8d96406..fc434a0 100644 --- a/lib/openflow/multipart/group/reply.ex +++ b/lib/openflow/multipart/group/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Group.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], groups: [] ) @@ -19,6 +20,16 @@ defmodule Openflow.Multipart.Group.Reply do groups = Openflow.Multipart.Group.read(groups_bin) %Reply{groups: groups} end + + def append_body(%Reply{groups: groups} = message, %Reply{flags: [:more], groups: continue}) do + %{message|groups: [continue|groups]} + end + def append_body(%Reply{groups: groups} = message, %Reply{flags: [], groups: continue}) do + new_groups = [continue|groups] + |> Enum.reverse + |> List.flatten + %{message|groups: new_groups} + end end defmodule Openflow.Multipart.Group do diff --git a/lib/openflow/multipart/group_desc/reply.ex b/lib/openflow/multipart/group_desc/reply.ex index 49bd412..fea5684 100644 --- a/lib/openflow/multipart/group_desc/reply.ex +++ b/lib/openflow/multipart/group_desc/reply.ex @@ -19,6 +19,16 @@ defmodule Openflow.Multipart.GroupDesc.Reply do groups = Openflow.Multipart.GroupDescStats.read(groups_bin) %Reply{groups: groups} end + + def append_body(%Reply{groups: groups} = message, %Reply{flags: [:more], groups: continue}) do + %{message|groups: [continue|groups]} + end + def append_body(%Reply{groups: groups} = message, %Reply{flags: [], groups: continue}) do + new_groups = [continue|groups] + |> Enum.reverse + |> List.flatten + %{message|groups: new_groups} + end end defmodule Openflow.Multipart.GroupDescStats do diff --git a/lib/openflow/multipart/group_features/reply.ex b/lib/openflow/multipart/group_features/reply.ex index 498d9ea..495ad83 100644 --- a/lib/openflow/multipart/group_features/reply.ex +++ b/lib/openflow/multipart/group_features/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.GroupFeatures.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], types: 0, capabilities: [], diff --git a/lib/openflow/multipart/meter/reply.ex b/lib/openflow/multipart/meter/reply.ex index bafd74f..2aede6e 100644 --- a/lib/openflow/multipart/meter/reply.ex +++ b/lib/openflow/multipart/meter/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Meter.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], meters: [] ) @@ -15,6 +16,16 @@ defmodule Openflow.Multipart.Meter.Reply do meters = Openflow.Multipart.Meter.read(meters_bin) %Reply{meters: meters} end + + def append_body(%Reply{meters: meters} = message, %Reply{flags: [:more], meters: continue}) do + %{message|meters: [continue|meters]} + end + def append_body(%Reply{meters: meters} = message, %Reply{flags: [], meters: continue}) do + new_meters = [continue|meters] + |> Enum.reverse + |> List.flatten + %{message|meters: new_meters} + end end defmodule Openflow.Multipart.Meter do diff --git a/lib/openflow/multipart/port_desc/reply.ex b/lib/openflow/multipart/port_desc/reply.ex new file mode 100644 index 0000000..73e0573 --- /dev/null +++ b/lib/openflow/multipart/port_desc/reply.ex @@ -0,0 +1,33 @@ +defmodule Openflow.Multipart.PortDesc.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: nil, + flags: [], + ports: [] + ) + + alias __MODULE__ + + def ofp_type, do: 18 + + def new(ports \\ []) do + %Reply{ports: ports} + end + + def read(<>) do + ports = for (<>), do: Openflow.Port.read(port_bin) + %Reply{ports: Enum.reverse(ports)} + end + + def append_body(%Reply{ports: ports} = message, %Reply{flags: [:more], ports: continue}) do + %{message|ports: [continue|ports]} + end + def append_body(%Reply{ports: ports} = message, %Reply{flags: [], ports: continue}) do + new_ports = [continue|ports] + |> Enum.reverse + |> List.flatten + %{message|ports: new_ports} + end +end diff --git a/lib/openflow/multipart/port_desc/request.ex b/lib/openflow/multipart/port_desc/request.ex new file mode 100644 index 0000000..be0abde --- /dev/null +++ b/lib/openflow/multipart/port_desc/request.ex @@ -0,0 +1,24 @@ +defmodule Openflow.Multipart.PortDesc.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/port_stats/reply.ex b/lib/openflow/multipart/port_stats/reply.ex index e51d0a3..e57ed32 100644 --- a/lib/openflow/multipart/port_stats/reply.ex +++ b/lib/openflow/multipart/port_stats/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.PortStats.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], ports: [] ) @@ -19,6 +20,16 @@ defmodule Openflow.Multipart.PortStats.Reply do ports = Openflow.Multipart.PortStats.read(ports_bin) %Reply{ports: ports} end + + def append_body(%Reply{ports: ports} = message, %Reply{flags: [:more], ports: continue}) do + %{message|ports: [continue|ports]} + end + def append_body(%Reply{ports: ports} = message, %Reply{flags: [], ports: continue}) do + new_ports = [continue|ports] + |> Enum.reverse + |> List.flatten + %{message|ports: new_ports} + end end defmodule Openflow.Multipart.PortStats do diff --git a/lib/openflow/multipart/queue/reply.ex b/lib/openflow/multipart/queue/reply.ex index 53d3284..38afbbc 100644 --- a/lib/openflow/multipart/queue/reply.ex +++ b/lib/openflow/multipart/queue/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Queue.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], queues: [] ) @@ -19,6 +20,16 @@ defmodule Openflow.Multipart.Queue.Reply do queues = Openflow.Multipart.Queue.read(queues_bin) %Reply{queues: queues} end + + def append_body(%Reply{queues: queues} = message, %Reply{flags: [:more], queues: continue}) do + %{message|queues: [continue|queues]} + end + def append_body(%Reply{queues: queues} = message, %Reply{flags: [], queues: continue}) do + new_queues = [continue|queues] + |> Enum.reverse + |> List.flatten + %{message|queues: new_queues} + end end defmodule Openflow.Multipart.Queue do diff --git a/lib/openflow/multipart/table/reply.ex b/lib/openflow/multipart/table/reply.ex index 496450b..457de7c 100644 --- a/lib/openflow/multipart/table/reply.ex +++ b/lib/openflow/multipart/table/reply.ex @@ -3,6 +3,7 @@ defmodule Openflow.Multipart.Table.Reply do version: 4, xid: 0, datapath_id: nil, # virtual field + aux_id: nil, flags: [], tables: [] ) @@ -15,6 +16,16 @@ defmodule Openflow.Multipart.Table.Reply do tables = Openflow.Multipart.TableStats.read(tables_bin) %Reply{tables: tables} end + + def append_body(%Reply{tables: tables} = message, %Reply{flags: [:more], tables: continue}) do + %{message|tables: [continue|tables]} + end + def append_body(%Reply{tables: tables} = message, %Reply{flags: [], tables: continue}) do + new_tables = [continue|tables] + |> Enum.reverse + |> List.flatten + %{message|tables: new_tables} + end end defmodule Openflow.Multipart.TableStats do diff --git a/lib/openflow/nx_packet_in2.ex b/lib/openflow/nx_packet_in2.ex index 1ba67e0..dfa8b34 100644 --- a/lib/openflow/nx_packet_in2.ex +++ b/lib/openflow/nx_packet_in2.ex @@ -13,14 +13,13 @@ defmodule Openflow.NxPacketIn2 do reason: nil, metadata: nil, userdata: nil, - continuation: nil, # continuation properties: - continuation_bridge: nil, - continuation_stack: nil, - continuation_conntracked: nil, + continuation_bridge: "", + continuation_stack: [], + continuation_conntracked: false, continuation_table_id: nil, continuation_cookie: nil, - continuation_actions: nil, + continuation_actions: [], continuation_action_set: nil ) @@ -29,6 +28,138 @@ defmodule Openflow.NxPacketIn2 do @experimenter 0x00002320 @nx_type 30 + @packet 0 + @full_len 1 + @buffer_id 2 + @table_id 3 + @cookie 4 + @reason 5 + @metadata 6 + @userdata 7 + @continuation 8 + + @nxcpt_bridge 0x8000 + @nxcpt_stack 0x8001 + @nxcpt_mirrors 0x8002 + @nxcpt_conntracked 0x8003 + @nxcpt_table_id 0x8004 + @nxcpt_cookie 0x8005 + @nxcpt_actions 0x8006 + @nxcpt_action_set 0x8007 + + @prop_header_length 4 + def ofp_type, do: 4 + def read(<<@experimenter::32, @nx_type::32, props_bin::bytes>>) do + %NxPacketIn2{} + |> decode_props(props_bin) + end + + ## private functions + + defp decode_props(pktin, ""), do: pktin + defp decode_props(pktin, <<@packet::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + packet_length = length - @prop_header_length + <> = tail + decode_props(%{pktin|packet: packet}, rest) + end + defp decode_props(pktin, <<@full_len::16, _length::16, full_len::32, rest::bytes>>) do + decode_props(%{pktin|full_len: full_len}, rest) + end + defp decode_props(pktin, <<@buffer_id::16, _length::16, buffer_id::32, rest::bytes>>) do + decode_props(%{pktin|buffer_id: buffer_id}, rest) + end + defp decode_props(pktin, <<@table_id::16, _length::16, table_id::8, _::24, rest::bytes>>) do + decode_props(%{pktin|table_id: table_id}, rest) + end + defp decode_props(pktin, <<@cookie::16, _length::16, _::32, cookie::64, rest::bytes>>) do + decode_props(%{pktin|cookie: cookie}, rest) + end + defp decode_props(pktin, <<@reason::16, _length::16, reason_int::8, _::24, rest::bytes>>) do + reason = Openflow.Enums.to_atom(reason_int, :packet_in_reason) + decode_props(%{pktin|reason: reason}, rest) + end + defp decode_props(pktin, <<@metadata::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + match_field_length = length - @prop_header_length + <> = tail + match_len = 4 + byte_size(match_fields_bin) + padding = Openflow.Utils.pad_length(match_len, 8) + match_bin = (<<1::16, match_len::16, match_fields_bin::bytes, 0::size(padding)-unit(8)>>) + {fields, _rest} = Openflow.Match.read(match_bin) + decode_props(%{pktin|metadata: fields}, rest) + end + defp decode_props(pktin, <<@userdata::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + userdata_length = length - @prop_header_length + <> = tail + decode_props(%{pktin|userdata: userdata}, rest) + end + defp decode_props(pktin, <<@continuation::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length - 4 + <<_pad::32, data::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + pktin + |> decode_continuations(data) + |> decode_props(rest) + end + defp decode_props(pktin, <<_::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <<_data::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + decode_props(pktin, rest) + end + + defp decode_continuations(pktin, ""), do: pktin + defp decode_continuations(pktin, <<@nxcpt_bridge::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <> = tail + decode_continuations(%{pktin|continuation_bridge: bridge}, rest) + end + defp decode_continuations(pktin, <<@nxcpt_stack::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = (length - @prop_header_length) * 8 + <> = tail + decode_continuations(%{pktin|continuation_stack: pktin.continuation_stack ++ [stack]}, rest) + end + defp decode_continuations(pktin, <<@nxcpt_mirrors::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <> = tail + decode_continuations(%{pktin|continuation_mirrors: mirrors}, rest) + end + defp decode_continuations(pktin, <<@nxcpt_conntracked::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <<_::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + decode_continuations(%{pktin|continuation_conntracked: true}, rest) + end + defp decode_continuations(pktin, <<@nxcpt_table_id::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + <> = tail + decode_continuations(%{pktin|continuation_table_id: table_id}, rest) + end + defp decode_continuations(pktin, <<@nxcpt_cookie::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + <> = tail + decode_continuations(%{pktin|continuation_cookie: cookie}, rest) + end + defp decode_continuations(pktin, <<@nxcpt_actions::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length - 4 + <<_pad::32, actions::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + decode_continuations(%{pktin|continuation_actions: Openflow.Action.read(actions)}, rest) + end + defp decode_continuations(pktin, <<@nxcpt_action_set::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + data_length = length - @prop_header_length + <> = tail + decode_continuations(%{pktin|continuation_action_set: action_set}, rest) + end + defp decode_continuations(pktin, _) do + decode_continuations(pktin, "") + end end diff --git a/lib/tres/application.ex b/lib/tres/application.ex index 5dc3b16..3186d9d 100644 --- a/lib/tres/application.ex +++ b/lib/tres/application.ex @@ -8,8 +8,9 @@ defmodule Tres.Application do def start(_type, _args) do import Supervisor.Spec + {cb_mod, _cb_args} = Tres.Utils.get_callback_module children = [worker(Registry, [[keys: :unique, name: SwitchRegistry]], id: SwitchRegistry), - supervisor(Tres.MessageHandlerSup, [], id: MessageHandlerSup)] + supervisor(Tres.MessageHandlerSup, [cb_mod], id: MessageHandlerSup)] opts = [strategy: :one_for_one, name: Tres.Supervisor] {:ok, _connection_pid} = Tres.Utils.start_openflow_listener Supervisor.start_link(children, opts) diff --git a/lib/tres/controller.ex b/lib/tres/controller.ex new file mode 100644 index 0000000..c9e0a38 --- /dev/null +++ b/lib/tres/controller.ex @@ -0,0 +1,14 @@ +defmodule Tres.Controller do + def controller_helpers do + quote do + import Tres.SwitchRegistry, only: [send_message: 2] + + use Tres.Messages + use Tres.MessageHelper + end + end + + defmacro __using__(_) do + controller_helpers() + end +end diff --git a/lib/tres/example_handler.ex b/lib/tres/example_handler.ex new file mode 100644 index 0000000..deaf722 --- /dev/null +++ b/lib/tres/example_handler.ex @@ -0,0 +1,108 @@ +defmodule Tres.ExampleHandler do + use GenServer + use Tres.Controller + + import Logger + + defmodule State do + defstruct [ + datapath_id: nil, + aux_id: nil, + conn_ref: nil + ] + end + + def start_link(datapath, args) do + GenServer.start_link(__MODULE__, [datapath, args]) + end + + def init([{datapath_id, aux_id}, _args]) do + info("[#{__MODULE__}] Switch Ready: " + <> "datapath_id: #{datapath_id} " + <> "aux_id: #{aux_id} " + <> "in #{inspect(self())}") + _ = send_flows_for_test(datapath_id) + _ = send_flow_stats_request(datapath_id) + _ = send_desc_stats_request(datapath_id) + _ = send_port_desc_stats_request(datapath_id) + conn_ref = SwitchRegistry.monitor(datapath_id) + state = %State{datapath_id: datapath_id, aux_id: aux_id, conn_ref: conn_ref} + {:ok, state} + end + + def handle_info(%Flow.Reply{datapath_id: datapath_id} = desc, state) do + handle_flow_stats_reply(desc, datapath_id) + {:noreply, state} + end + def handle_info(%PortDesc.Reply{datapath_id: datapath_id} = desc, state) do + handle_port_desc_stats_reply(desc, datapath_id) + {:noreply, state} + end + def handle_info(%Desc.Reply{datapath_id: datapath_id} = desc, state) do + handle_desc_stats_reply(desc, datapath_id) + {:noreply, state} + end + + # To prevent process leakage, following section is required. + def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do + :ok = warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id}") + {:stop, :normal, state} + end + + # `Catch all` function is required. + def handle_info(info, state) do + :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}: #{state.datapath_id}") + {:noreply, state} + end + + # private functions + + defp send_flows_for_test(datapath_id) do + for count <- Range.new(1, 1024) do + send_flow_mod_add(datapath_id, match: Match.new(metadata: count)) + end + end + + defp send_flow_stats_request(datapath_id) do + Flow.Request.new + |> send_message(datapath_id) + end + + defp send_desc_stats_request(datapath_id) do + Desc.Request.new + |> send_message(datapath_id) + end + + defp send_port_desc_stats_request(datapath_id) do + PortDesc.Request.new + |> send_message(datapath_id) + end + + defp handle_flow_stats_reply(desc, datapath_id) do + info("[#{__MODULE__}] Switch #{length(desc.flows)} installed on #{datapath_id}") + end + + defp handle_desc_stats_reply(desc, datapath_id) do + info( + "[#{__MODULE__}] Switch Desc: " + <> "mfr = #{desc.mfr_desc} " + <> "hw = #{desc.hw_desc} " + <> "sw = #{desc.sw_desc} " + <> "for #{datapath_id}" + ) + end + + defp handle_port_desc_stats_reply(port_desc, datapath_id) do + for port <- port_desc.ports do + info( + "[#{__MODULE__}] Switch has port: " + <> "number = #{port.number} " + <> "hw_addr = #{port.hw_addr} " + <> "name = #{port.name} " + <> "config = #{inspect(port.config)} " + <> "current_speed = #{port.current_speed} " + <> "on #{datapath_id}" + ) + end + end +end diff --git a/lib/tres/message_handler.ex b/lib/tres/message_handler.ex deleted file mode 100644 index 7f01ed8..0000000 --- a/lib/tres/message_handler.ex +++ /dev/null @@ -1,52 +0,0 @@ -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 index ca6d393..9c250f6 100644 --- a/lib/tres/message_handler_sup.ex +++ b/lib/tres/message_handler_sup.ex @@ -1,16 +1,17 @@ defmodule Tres.MessageHandlerSup do use Supervisor - def start_link do - Supervisor.start_link(__MODULE__, [], name: __MODULE__) + def start_link(cb_mod) do + Supervisor.start_link(__MODULE__, [cb_mod], name: __MODULE__) end - def init(_) do - children = [worker(Tres.MessageHandler, [], restart: :temporary)] + def init([cb_mod]) do + children = [worker(cb_mod, [], restart: :temporary, shutdown: 5000)] supervise(children, strategy: :simple_one_for_one) end - def start_child({ip_addr, port}) do - Supervisor.start_child(__MODULE__, [{ip_addr, port}, self()]) + def start_child({dpid, aux_id}) do + {_cb_mod, cb_args} = Tres.Utils.get_callback_module + Supervisor.start_child(__MODULE__, [{dpid, aux_id}, cb_args]) end end diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex new file mode 100644 index 0000000..8080d5b --- /dev/null +++ b/lib/tres/message_helper.ex @@ -0,0 +1,88 @@ +defmodule Tres.MessageHelper do + defmacro __using__(_) do + quote location: :keep do + defp send_flow_mod_add(datapath_id, options) do + flow_mod = %Openflow.FlowMod{ + cookie: options[:cookie] || 0, + priority: options[:priority] || 0, + table_id: options[:table_id] || 0, + command: :add, + idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0, + buffer_id: :no_buffer, + out_port: :any, + out_group: :any, + flags: options[:flags] || [], + match: options[:match] || Openflow.Match.new, + instructions: options[:instructions] || [], + } + send_message(flow_mod, datapath_id) + end + + defp send_flow_mod_modify(datapath_id, options) do + command = Tres.Utils.flow_command(options, :modify) + flow_mod = %Openflow.FlowMod{ + cookie: options[:cookie] || 0, + table_id: options[:table_id] || 0, + command: command, + idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0, + out_port: :any, + out_group: :any, + match: options[:match] || Openflow.Match.new, + instructions: options[:instructions] || [], + } + send_message(flow_mod, datapath_id) + end + + defp send_flow_mod_delete(datapath_id, options) do + command = Tres.Utils.flow_command(options, :delete) + flow_mod = %Openflow.FlowMod{ + cookie: options[:cookie] || 0, + cookie_mask: options[:cookie_mask] || 0, + table_id: options[:table_id] || :all, + command: command, + out_port: options[:out_port] || :any, + out_group: options[:out_group] || :any, + match: options[:match] || Openflow.Match.new + } + send_message(flow_mod, datapath_id) + end + + defp send_packet_out(datapath_id, options) do + packet_out = %Openflow.PacketOut{ + buffer_id: options[:buffer_id] || :no_buffer, + in_port: options[:in_port] || :controller, + actions: options[:actions] || [], + data: options[:data] || "" + } + send_message(packet_out, datapath_id) + end + + defp send_group_mod_add(datapath_id, options) do + group_mod = Openflow.GroupMod.new( + command: :add, + type: options[:type] || :all, + group_id: options[:group_id] || 0, + buckets: options[:buckets] || [] + ) + send_message(group_mod, datapath_id) + end + + defp send_group_mod_delete(datapath_id, group_id) do + group_mod = Openflow.GroupMod.new(command: :delete, group_id: group_id) + send_message(group_mod, datapath_id) + end + + defp send_group_mod_modify(datapath_id, options) do + group_mod = Openflow.GroupMod.new( + command: :modify, + type: options[:type] || :all, + group_id: options[:group_id] || 0, + buckets: options[:buckets] || [] + ) + send_message(group_mod, datapath_id) + end + end + end +end diff --git a/lib/tres/messages.ex b/lib/tres/messages.ex new file mode 100644 index 0000000..04810a2 --- /dev/null +++ b/lib/tres/messages.ex @@ -0,0 +1,128 @@ +defmodule Tres.Messages do + defmacro __using__(_) do + quote do + alias Openflow.ErrorMsg + alias Openflow.Echo + alias Openflow.Features + alias Openflow.GetConfig + alias Openflow.SetConfig + alias Openflow.PacketIn + alias Openflow.FlowRemoved + alias Openflow.PortStatus + alias Openflow.PacketOut + alias Openflow.FlowMod + alias Openflow.GroupMod + alias Openflow.PortMod + alias Openflow.TableMod + alias Openflow.Multipart + alias Openflow.Barrier + alias Openflow.Role + alias Openflow.GetAsync + alias Openflow.SetAsync + alias Openflow.MeterMod + + alias Openflow.Match + alias Openflow.Port + + alias Openflow.NxSetPacketInFormat + alias Openflow.NxSetControllerId + alias Openflow.NxPacketIn2 + + alias Openflow.Multipart.Desc + alias Openflow.Multipart.Flow + alias Openflow.Multipart.Aggregate + alias Openflow.Multipart.Table + alias Openflow.Multipart.PortStats + alias Openflow.Multipart.Queue + alias Openflow.Multipart.Group + alias Openflow.Multipart.GroupDesc + alias Openflow.Multipart.GroupFeatures + alias Openflow.Multipart.Meter + alias Openflow.Multipart.MeterConfig + alias Openflow.Multipart.MeterFeatures + alias Openflow.Multipart.TableFeatures + alias Openflow.Multipart.PortDesc + + alias Openflow.Instruction.GotoTable + alias Openflow.Instruction.WriteMetadata + alias Openflow.Instruction.WriteActions + alias Openflow.Instruction.ApplyActions + alias Openflow.Instruction.ClearActions + alias Openflow.Instruction.Meter + + alias Openflow.Action.Output + alias Openflow.Action.CopyTtlOut + alias Openflow.Action.CopyTtlIn + alias Openflow.Action.SetMplsTtl + alias Openflow.Action.DecMplsTtl + alias Openflow.Action.PushVlan + alias Openflow.Action.PopVlan + alias Openflow.Action.PushMpls + alias Openflow.Action.PopMpls + alias Openflow.Action.SetQueue + alias Openflow.Action.Group + alias Openflow.Action.SetNwTtl + alias Openflow.Action.DecNwTtl + alias Openflow.Action.SetField + alias Openflow.Action.PushPbb + alias Openflow.Action.PopPbb + alias Openflow.Action.Encap + alias Openflow.Action.Decap + alias Openflow.Action.SetSequence + alias Openflow.Action.ValidateSequence + + alias Openflow.Action.NxResubmit + alias Openflow.Action.NxSetTunnel + alias Openflow.Action.NxSetQueue + alias Openflow.Action.NxPopQueue + alias Openflow.Action.NxRegMove + alias Openflow.Action.NxRegLoad + alias Openflow.Action.NxNote + alias Openflow.Action.NxSetTunnel64 + alias Openflow.Action.NxMultipath + alias Openflow.Action.NxBundle + alias Openflow.Action.NxBundleLoad + alias Openflow.Action.NxResubmitTable + alias Openflow.Action.NxOutputReg + alias Openflow.Action.NxLearn + alias Openflow.Action.NxExit + alias Openflow.Action.NxDecTtl + alias Openflow.Action.NxFinTimeout + alias Openflow.Action.NxController + alias Openflow.Action.NxDecTtlCntIds + alias Openflow.Action.NxWriteMetadata + alias Openflow.Action.NxPushMpls + alias Openflow.Action.NxPopMpls + alias Openflow.Action.NxSetMplsTtl + alias Openflow.Action.NxDecMplsTtl + alias Openflow.Action.NxStackPush + alias Openflow.Action.NxStackPop + alias Openflow.Action.NxSample + alias Openflow.Action.NxSetMplsLabel + alias Openflow.Action.NxSetMplsTc + alias Openflow.Action.NxOutputReg2 + alias Openflow.Action.NxRegLoad2 + alias Openflow.Action.NxConjunction + alias Openflow.Action.NxConntrack + alias Openflow.Action.NxNat + alias Openflow.Action.NxController2 + alias Openflow.Action.NxSample2 + alias Openflow.Action.NxOutputTrunc + alias Openflow.Action.NxGroup + alias Openflow.Action.NxSample3 + alias Openflow.Action.NxClone + alias Openflow.Action.NxCtClear + alias Openflow.Action.NxResubmitTableCt + alias Openflow.Action.NxLearn2 + alias Openflow.Action.NxEncap + alias Openflow.Action.NxDecap + alias Openflow.Action.NxDebugRecirc + + alias Openflow.Action.NxFlowSpecMatch + alias Openflow.Action.NxFlowSpecLoad + alias Openflow.Action.NxFlowSpecOutput + + alias Tres.SwitchRegistry + end + end +end diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 013e731..187b693 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -3,6 +3,7 @@ defmodule Tres.SecureChannel do import Logger + alias :tres_xact_kv, as: XACT_KV alias Tres.SecureChannelState alias Tres.SwitchRegistry alias Tres.MessageHandlerSup @@ -32,12 +33,11 @@ defmodule Tres.SecureChannel do 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, []) + state_data = init_secure_channel(ref, socket, transport) + debug("[#{__MODULE__}] TCP connected to Switch on" + <> " #{state_data.ip_addr}:#{state_data.port}" + <> " on #{inspect(self())}") + :gen_statem.enter_loop(__MODULE__, [debug: [:debug]], :INIT, state_data, []) end # TCP handler @@ -73,8 +73,9 @@ defmodule Tres.SecureChannel do handle_WATING(type, message, state_data) end - def terminate(reason, state, %SecureChannelState{datapath_id: datapath_id, aux_id: aux_id}) do + def terminate(reason, state, %SecureChannelState{datapath_id: datapath_id, aux_id: aux_id, xact_kv_ref: kv_ref}) do warn("[#{__MODULE__}] termiate: #{inspect(reason)} state = #{inspect(state)}") + true = XACT_KV.drop(kv_ref) :ok = SwitchRegistry.unregister({datapath_id, aux_id}) end @@ -83,7 +84,8 @@ defmodule Tres.SecureChannel do 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) + kv_ref = XACT_KV.create + SecureChannelState.new(ref: ref, socket: socket, transport: transport, xact_kv_ref: kv_ref) end defp init_process(ref) do @@ -93,8 +95,8 @@ defmodule Tres.SecureChannel do end defp init_handler(state_data) do - %SecureChannelState{ip_addr: ip_addr, port: port} = state_data - {:ok, pid} = MessageHandlerSup.start_child({ip_addr, port}) + %SecureChannelState{datapath_id: dpid, aux_id: aux_id} = state_data + {:ok, pid} = MessageHandlerSup.start_child({dpid, aux_id}) ref = Process.monitor(pid) %{state_data|handler_pid: pid, handler_ref: ref} end @@ -128,8 +130,7 @@ defmodule Tres.SecureChannel 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}") + debug("[#{__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 @@ -142,9 +143,10 @@ defmodule Tres.SecureChannel do end # CONNECTED state - defp handle_CONNECTED(:enter, :CONNECTING, _state_data) do + defp handle_CONNECTED(:enter, :CONNECTING, state_data) do + new_state_data = init_handler(state_data) start_periodic_idle_check() - :keep_state_and_data + {:keep_state, new_state_data} end defp handle_CONNECTED(:info, :idle_check, state_data) do start_periodic_idle_check() @@ -162,19 +164,48 @@ defmodule Tres.SecureChannel 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 + defp handle_CONNECTED(:internal, {:openflow, %Openflow.Barrier.Reply{xid: xid}}, state_data) do + for {:xact_entry, _xid, message, _orig} <- XACT_KV.get(state_data.xact_kv_ref, xid) do + unless is_nil(message) do + send(state_data.handler_pid, message) + XACT_KV.delete(state_data.xact_kv_ref, message.xid) + end + end + :keep_state_and_data + end + defp handle_CONNECTED(:internal, {:openflow, message}, state_data) do + %SecureChannelState{datapath_id: dpid, aux_id: aux_id} = state_data + new_message = %{message|datapath_id: dpid, aux_id: aux_id} + state_data.xact_kv_ref + |> XACT_KV.is_exists(message.xid) + |> handle_message(new_message, state_data) :keep_state_and_data end defp handle_CONNECTED(:cast, {:send_message, message}, state_data) do - message - |> send_message(state_data) + xid = SecureChannelState.increment_transaction_id(state_data.xid) + messages = [ + %{message|xid: xid}, + %{Openflow.Barrier.Request.new|xid: xid} + ] + XACT_KV.insert(state_data.xact_kv_ref, xid, message) + send_message(messages, state_data) :keep_state_and_data end + defp handle_message(_in_xact = true, message, state_data) do + [{:xact_entry, _xid, prev_message, _orig}|_] = XACT_KV.get(state_data.xact_kv_ref, message.xid) + new_message = Openflow.append_body(prev_message, message) + XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message) + end + defp handle_message(_in_xact = false, message, %SecureChannelState{handler_pid: handler_pid}) do + send(handler_pid, message) + end + # WATING state defp handle_WATING(:enter, :CONNECTING, state_data) do warn("[#{__MODULE__}] Possible HANG Detected on datapath_id: #{state_data.datapath_id} !") + %SecureChannelState{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data + send(handler_pid, {:switch_hang, {dpid, aux_id}}) start_periodic_idle_check() :keep_state_and_data end @@ -186,8 +217,9 @@ defmodule Tres.SecureChannel do 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 + defp handle_WATING(:internal, {:openflow, message}, state_data) do + %SecureChannelState{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data + send(handler_pid, %{message|datapath_id: dpid, aux_id: aux_id}) {:next_state, :CONNECTING, state_data} end defp handle_WATING(type, message, state_data) @@ -249,12 +281,10 @@ defmodule Tres.SecureChannel do {: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 monitor_connection(datapath_id, aux_id) when aux_id > 0, + do: SwitchRegistry.monitor(datapath_id) + defp monitor_connection(_datapath_id, _aux_id), + do: nil defp send_hello(state_data) do @supported_version @@ -320,7 +350,7 @@ defmodule Tres.SecureChannel do end defp send_message(message, %SecureChannelState{socket: socket, transport: transport}) do - debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + #debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") Tres.Utils.send_message(message, socket, transport) end @@ -371,7 +401,7 @@ defmodule Tres.SecureChannel do {: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}") + warn("[#{__MODULE__}] connection terminated: Trapped by #{reason}") {:stop, :normal, %{state_data|socket: nil}} end defp close_connection(:tcp_closed, state_data) do diff --git a/lib/tres/secure_channel_state.ex b/lib/tres/secure_channel_state.ex index f2c163d..e3f8873 100644 --- a/lib/tres/secure_channel_state.ex +++ b/lib/tres/secure_channel_state.ex @@ -16,10 +16,12 @@ defmodule Tres.SecureChannelState do ping_xid: 0, ping_timer_ref: nil, ping_fail_count: 0, - last_received: 0 + last_received: 0, + xact_kv_ref: nil ) alias __MODULE__ + alias :tres_xact_kv, as: XACT_KV def new(options) do ref = Keyword.get(options, :ref) @@ -27,13 +29,15 @@ defmodule Tres.SecureChannelState do transport = Keyword.get(options, :transport) {:ok, {ip_addr, port}} = :inet.peername(socket) {:ok, xid_agent} = Agent.start_link(fn -> 0 end) + kv_ref = XACT_KV.create %SecureChannelState{ ref: ref, socket: socket, transport: transport, ip_addr: :inet.ntoa(ip_addr), port: port, - xid: xid_agent + xid: xid_agent, + xact_kv_ref: kv_ref } end diff --git a/lib/tres/switch_registry.ex b/lib/tres/switch_registry.ex index f7b8a19..1145ee3 100644 --- a/lib/tres/switch_registry.ex +++ b/lib/tres/switch_registry.ex @@ -24,6 +24,12 @@ defmodule Tres.SwitchRegistry do send_message(message, {dpid, 0}) end + def monitor(datapath_id) do + datapath_id + |> lookup_pid + |> Process.monitor + end + # private function defp dispatch(entries, message) do diff --git a/lib/tres/utils.ex b/lib/tres/utils.ex index 358e44f..b99f3a1 100644 --- a/lib/tres/utils.ex +++ b/lib/tres/utils.ex @@ -6,10 +6,16 @@ defmodule Tres.Utils do @default_num_acceptors 10 @default_openflow_port 6633 + def get_callback_module do + cb_mod = get_config(:callback_module, Tres.ExampleHandler) + cb_args = get_config(:callback_args, []) + {cb_mod, cb_args} + end + 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) + max_connections = get_config(:max_connections, @default_max_connections) + num_acceptors = get_config(:num_acceptors, @default_num_acceptors) + port = get_config(: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 @@ -23,4 +29,31 @@ defmodule Tres.Utils do error("[#{__MODULE__}] Unencodable error: #{inspect(message)}") end end + + def is_multipart?(message) do + message.__struct__ + |> Module.split + |> Enum.at(1) + |> String.match?(~r/Multipart/) + end + + def flow_command(:delete, options) do + if Keyword.get(options, :strict, false) do + :delete_strict + else + :delete + end + end + def flow_command(:modify, options) do + if Keyword.get(options, :strict, false) do + :modify_strict + else + :modify + end + end + + # private functions + defp get_config(item, default) do + Application.get_env(:tres, item, default) + end end diff --git a/mix.exs b/mix.exs index 7a68614..8aa92e7 100644 --- a/mix.exs +++ b/mix.exs @@ -6,6 +6,7 @@ defmodule Tres.Mixfile do version: "0.1.0", elixir: "~> 1.5", start_permanent: Mix.env == :prod, + compilers: [:erlang] ++ Mix.compilers, deps: deps()] end diff --git a/src/tres_xact_kv.erl b/src/tres_xact_kv.erl new file mode 100644 index 0000000..6e41e90 --- /dev/null +++ b/src/tres_xact_kv.erl @@ -0,0 +1,61 @@ +-module(tres_xact_kv). + +-include_lib("stdlib/include/ms_transform.hrl"). + +-export([create/0, drop/1]). +-export([insert/3, update/3, get/2, delete/2, is_exists/2]). + +-define(TABLE, xact_kv). +-define(ENTRY, xact_entry). +-define(TABLE_OPTS, [set, protected, {keypos, #?ENTRY.xid}]). + +-record(?ENTRY, {xid = 0, pending = nil, orig = nil}). + +-spec create() -> reference(). +create() -> + ets:new(?TABLE, ?TABLE_OPTS). + +-spec drop(reference()) -> true. +drop(Tid) -> + ets:delete(Tid). + +-spec insert(reference(), integer(), map()) -> true. +insert(Tid, Xid, Orig) -> + ets:insert(Tid, #?ENTRY{xid = Xid, orig = Orig}). + +-spec update(reference(), integer(), map()) -> integer(). +update(Tid, Xid, #{'__struct__' := 'Elixir.Openflow.ErrorMsg'} = Error) -> + ets:select_replace(Tid, ms_for_handle_error(Tid, Xid, Error)); +update(Tid, Xid, Msg) -> + ets:select_replace(Tid, ms_for_update(Xid, Msg)). + +-spec get(reference(), integer()) -> [term()]. +get(Tid, Xid) -> + ets:select(Tid, ms_for_get(Xid)). + +-spec delete(reference(), integer()) -> integer(). +delete(Tid, Xid) -> + ets:select_delete(Tid, ms_for_exists(Xid)). + +-spec is_exists(reference(), integer()) -> boolean(). +is_exists(Tid, Xid) -> + case ets:select(Tid, ms_for_exists(Xid)) of + [_|_] -> true; + [] -> false + end. + +%% Private functions + +ms_for_exists(Xid) -> + ets:fun2ms(fun(#?ENTRY{xid = TXid}) when TXid < Xid -> true end). + +ms_for_get(Xid) -> + ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E end). + +ms_for_update(Xid, Msg) -> + ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Msg} end). + +ms_for_handle_error(Tid, Xid, Error) -> + [Orig|_] = get(Tid, Xid), + Error1 = maps:merge(Error, #{data => Orig}), + ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Error1} end). diff --git a/test/flay_test.exs b/test/flay_test.exs new file mode 100644 index 0000000..22285b8 --- /dev/null +++ b/test/flay_test.exs @@ -0,0 +1,5 @@ +defmodule FlayTest do + use ExUnit.Case + + +end diff --git a/test/packet_data/nx_packet_in2_arp.raw b/test/packet_data/nx_packet_in2_arp.raw new file mode 100644 index 0000000000000000000000000000000000000000..cbf807f138b32844b52bfbba565d06f35259df8f GIT binary patch literal 200 zcmZQ!VK~762FeNy3=DD%3=9tc!GJ+*+g7_{9Bd4X91LtM42%%|ffZL7!O9s9thm|$ zVZq2oMy?D7W(HO;pM^mHOv4CP23D{}HU^#s1_qY@|Ns95OK~uGfanGW29ffVN=-rM s19LWR@|y7JpT}>oI4i^)u#^7(|KGsIAOR9*U| Date: Mon, 20 Nov 2017 16:57:37 +0900 Subject: [PATCH 03/50] Work on test case for secure channel --- config/config.exs | 2 +- lib/openflow/actions/push_vlan.ex | 2 +- lib/tres/message_helper.ex | 4 +- lib/tres/secure_channel.ex | 16 +- mix.exs | 6 +- mix.lock | 1 + src/tres_xact_kv.erl | 4 +- test/flay.ex | 106 ++++++++ test/flay_test.exs | 436 +++++++++++++++++++++++++++++- test/pf.ex | 40 +++ test/test_helper.exs | 2 +- 11 files changed, 603 insertions(+), 16 deletions(-) create mode 100644 test/flay.ex create mode 100644 test/pf.ex diff --git a/config/config.exs b/config/config.exs index 4113ff7..e8565e6 100644 --- a/config/config.exs +++ b/config/config.exs @@ -4,7 +4,7 @@ use Mix.Config config :tres, protocol: :tcp, - port: 6633, + port: 6653, max_connections: 10, num_acceptors: 10, callback_module: Tres.ExampleHandler, diff --git a/lib/openflow/actions/push_vlan.ex b/lib/openflow/actions/push_vlan.ex index 056f033..8c8ca39 100644 --- a/lib/openflow/actions/push_vlan.ex +++ b/lib/openflow/actions/push_vlan.ex @@ -5,7 +5,7 @@ defmodule Openflow.Action.PushVlan do def ofpat, do: 17 - def new(ethertype) do + def new(ethertype \\ 0x8100) do %PushVlan{ethertype: ethertype} end diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index 8080d5b..8cfe40e 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -20,7 +20,7 @@ defmodule Tres.MessageHelper do end defp send_flow_mod_modify(datapath_id, options) do - command = Tres.Utils.flow_command(options, :modify) + command = Tres.Utils.flow_command(:modify, options) flow_mod = %Openflow.FlowMod{ cookie: options[:cookie] || 0, table_id: options[:table_id] || 0, @@ -36,7 +36,7 @@ defmodule Tres.MessageHelper do end defp send_flow_mod_delete(datapath_id, options) do - command = Tres.Utils.flow_command(options, :delete) + command = Tres.Utils.flow_command(:delete, options) flow_mod = %Openflow.FlowMod{ cookie: options[:cookie] || 0, cookie_mask: options[:cookie_mask] || 0, diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 187b693..48fd016 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -18,7 +18,7 @@ defmodule Tres.SecureChannel do @hello_handshake_timeout 1000 @features_handshake_timeout 1000 @ping_timeout 5000 - @transaction_timeout 5000 + # @transaction_timeout 5000 @ping_interval 5000 @ping_fail_max_count 10 @@ -193,12 +193,16 @@ defmodule Tres.SecureChannel do end defp handle_message(_in_xact = true, message, state_data) do - [{:xact_entry, _xid, prev_message, _orig}|_] = XACT_KV.get(state_data.xact_kv_ref, message.xid) - new_message = Openflow.append_body(prev_message, message) - XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message) + case XACT_KV.get(state_data.xact_kv_ref, message.xid) do + [{:xact_entry, _xid, prev_message, _orig}|_] -> + new_message = Openflow.append_body(prev_message, message) + XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message) + _ -> + XACT_KV.delete(state_data.xact_kv_ref, message.xid) + end end - defp handle_message(_in_xact = false, message, %SecureChannelState{handler_pid: handler_pid}) do - send(handler_pid, message) + defp handle_message(_in_xact = false, message, state_data) do + send(state_data.handler_pid, message) end # WATING state diff --git a/mix.exs b/mix.exs index 8aa92e7..c6707a5 100644 --- a/mix.exs +++ b/mix.exs @@ -7,7 +7,8 @@ defmodule Tres.Mixfile do elixir: "~> 1.5", start_permanent: Mix.env == :prod, compilers: [:erlang] ++ Mix.compilers, - deps: deps()] + deps: deps(), + aliases: [test: "test --no-start"]] end # Run "mix help compile.app" to learn about applications. @@ -20,6 +21,7 @@ defmodule Tres.Mixfile do defp deps do [{:ranch, "~> 1.4.0"}, {:binpp, github: "jtendo/binpp", branch: "master"}, - {:pkt, github: "msantos/pkt", ref: "3afb196"}] + {:pkt, github: "msantos/pkt", ref: "3afb196", only: :test, override: true}, + {:epcap, github: "msantos/epcap", branch: "master", only: :test}] end end diff --git a/mix.lock b/mix.lock index e371c67..c50c3ed 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,5 @@ %{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, + "epcap": {:git, "https://github.com/msantos/epcap.git", "9566f0420a4dcf1292c1a1afd9339c35dbdfd041", [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"}, diff --git a/src/tres_xact_kv.erl b/src/tres_xact_kv.erl index 6e41e90..e7a99a1 100644 --- a/src/tres_xact_kv.erl +++ b/src/tres_xact_kv.erl @@ -47,7 +47,7 @@ is_exists(Tid, Xid) -> %% Private functions ms_for_exists(Xid) -> - ets:fun2ms(fun(#?ENTRY{xid = TXid}) when TXid < Xid -> true end). + ets:fun2ms(fun(#?ENTRY{xid = TXid}) when TXid == Xid -> true end). ms_for_get(Xid) -> ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E end). @@ -56,6 +56,6 @@ ms_for_update(Xid, Msg) -> ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Msg} end). ms_for_handle_error(Tid, Xid, Error) -> - [Orig|_] = get(Tid, Xid), + [#?ENTRY{orig = Orig}|_] = get(Tid, Xid), Error1 = maps:merge(Error, #{data => Orig}), ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Error1} end). diff --git a/test/flay.ex b/test/flay.ex new file mode 100644 index 0000000..d218886 --- /dev/null +++ b/test/flay.ex @@ -0,0 +1,106 @@ +defmodule Flay do + use GenServer + use Tres.Controller + + import Logger + + defmodule State do + defstruct [ + datapath_id: nil, + tester_pid: nil, + conn_ref: nil, + reply_to: nil + ] + end + + def start_link(datapath, args) do + GenServer.start_link(__MODULE__, [datapath, args], name: __MODULE__) + end + + def init(args) do + state = init_controller(args) + init_bridge(state.datapath_id) + {:ok, state} + end + + def handle_call(:port_desc_stats, from, state) do + send_message(PortDesc.Request.new, state.datapath_id) + {:noreply, %{state|reply_to: from}} + end + def handle_call(:desc_stats, from, state) do + send_message(Desc.Request.new, state.datapath_id) + {:noreply, %{state|reply_to: from}} + end + + def handle_cast({:flow_install, flow_opts, tester_pid}, state) do + send_flow_mod_add(state.datapath_id, flow_opts) + flow_opts_to_ofp_print(flow_opts) + {:noreply, %{state|tester_pid: tester_pid}} + end + + def handle_info(%ErrorMsg{} = error, state) do + send(state.tester_pid, error) + {:noreply, state} + end + def handle_info(%PortDesc.Reply{} = desc, state) do + GenServer.reply(state.reply_to, desc) + {:noreply, %{state|reply_to: nil}} + end + def handle_info(%Desc.Reply{} = desc, state) do + GenServer.reply(state.reply_to, desc) + {:noreply, %{state|reply_to: nil}} + end + # `Catch all` function is required. + def handle_info(info, state) do + :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}") + {:noreply, state} + end + + # private functions + + defp flow_opts_to_ofp_print(flow_opts) do + flow_opts + |> FlowMod.new + |> Openflow.to_binary + |> binary_to_space_delimited_hex + |> ofp_print_cmd + |> IO.inspect + end + + defp ofp_print_cmd(print_args) do + IO.inspect("\n") + {result, _code} = System.cmd("ovs-ofctl", ["ofp-print", "#{print_args}"]) + result + end + + defp binary_to_space_delimited_hex(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 + + defp init_controller([datapath_id, tester_pid]) do + conn_ref = SwitchRegistry.monitor(datapath_id) + %State{ + datapath_id: datapath_id, + tester_pid: tester_pid, + conn_ref: conn_ref + } + end + + defp init_bridge(datapath_id) do + send_flow_mod_delete(datapath_id, table_id: :all) + end +end diff --git a/test/flay_test.exs b/test/flay_test.exs index 22285b8..8597de1 100644 --- a/test/flay_test.exs +++ b/test/flay_test.exs @@ -1,5 +1,439 @@ defmodule FlayTest do use ExUnit.Case + use Bitwise - + @vlan_trunk_port 1 + @access_port 2 + @vxlan_port 3 + @bootnet_vid 0x1000 ||| 5 + @user_vid 0x1000 ||| 123 + @mcast {"010000000000", "010000000000"} + @mac "010203040506" + @sdl_vmac "000000000001" + @trusted_macs [ + "0800274d3297", + "0800274d3298", + "0800274d3299" + ] + + import Record + # Extract Erlang record for klarna/brod + for {name, schema} <- extract_all(from_lib: "pkt/include/pkt.hrl") do + defrecord(name, schema) + end + + setup do + Code.load_file("test/flay.ex") + Code.load_file("test/pf.ex") + Application.put_env(:tres, :protocol, :tcp, persistent: true) + Application.put_env(:tres, :port, 6633, persistent: true) + Application.put_env(:tres, :mac_connections, 1, persistent: true) + Application.put_env(:tres, :mac_acceptors, 1, persistent: true) + Application.put_env(:tres, :callback_module, Flay, persistent: true) + Application.put_env(:tres, :callback_args, self(), persistent: true) + Application.start(:binpp) + Application.start(:pkt) + Application.start(:epcap) + Application.start(:ranch) + Application.start(:tres) + wait_for_connected() + end + + describe "switch:merged_handler" do + test "OFPMP_DESC" do + %Openflow.Multipart.Desc.Reply{} = GenServer.call(Flay, :desc_stats, 5000) + end + + test "Flow install" do + options = [cookie: 0x8000000000000000] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new( + in_port: @vlan_trunk_port, + eth_dst: "ffffffffffff", + vlan_vid: {0x0000, 0x1fff}, + eth_type: 0x88cc + ) + ins = Openflow.Instruction.ApplyActions.new(Openflow.Action.Output.new(:controller)) + options = [ + cookie: 0x4000000000000000, + table_id: 0, + priority: 200, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + # LLDP Packet to vlan trunk port mac_address + src = "00000000000a" + src_mac = <<(String.to_integer(src, 16))::48>> + dst = "ffffffffffff" + dst_mac = <<(String.to_integer(dst, 16))::48>> + lldp = lldp(pdus: [ + {:chassis_id, :mac_address, src_mac}, + {:port_id, :mac_address, src_mac}, + {:ttl, 120} + ]) + ether = ether(shost: src_mac, dhost: dst_mac, type: 0x88cc) + _packet = <<(:pkt.ether(ether))::bytes, (:pkt.lldp(lldp))::bytes>> + + %Openflow.Multipart.PortDesc.Reply{ports: ports} = GenServer.call(Flay, :port_desc_stats, 5000) + for port <- ports do + match = Openflow.Match.new(in_port: port.number, eth_src: port.hw_addr) + options = [ + cookie: 0x4000000000000000, + table_id: 0, + priority: 201, + match: match, + instructions: [] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + # ARP Packet to vlan_trunk port mac address + arp = arp(arp_sha: src_mac, arp_sha: dst_mac) + ether = ether(shost: src_mac, dhost: dst_mac, type: 0x0806) + _packet = <<(:pkt.ether(ether))::bytes, (:pkt.arp(arp))::bytes>> + end + refute_receive %Openflow.ErrorMsg{}, 1000 + end + end + + describe "switch:uplink_escalation_flow" do + test "Flow install" do + match = Openflow.Match.new(eth_type: 0x0806) + action = Openflow.Action.Output.new(:controller) + ins = Openflow.Instruction.ApplyActions.new(action) + options = [ + cookie: 0x2000000000000000, + table_id: 0, + priority: 10, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) + action = Openflow.Action.Output.new(:controller) + ins = Openflow.Instruction.ApplyActions.new(action) + options = [ + cookie: 0x2000000000000000, + table_id: 0, + priority: 10, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(in_port: @vlan_trunk_port) + options = [ + cookie: 0x2000000000000000, + table_id: 0, + priority: 11, + match: match, + instructions: [] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + refute_receive %Openflow.ErrorMsg{}, 1000 + end + end + + describe "associate:register_bootstrap_rule" do + test "Flow install" do + cookie = :crypto.strong_rand_bytes(8) |> :binary.decode_unsigned(:big) + match = Openflow.Match.new(in_port: @access_port, eth_src: @mac) + actions = [ + Openflow.Action.PushVlan.new, + Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}), + Openflow.Action.Output.new(@vlan_trunk_port) + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [table_id: 0, + priority: 50, + cookie: cookie, + idle_timeout: 300, + hard_timeout: 300, + flags: [:send_flow_rem], + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + # trusted mac + match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @bootnet_vid, eth_dst: @mcast) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(@access_port) + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [table_id: 0, + priority: 50, + cookie: cookie, + idle_timeout: 300, + hard_timeout: 300, + flags: [:send_flow_rem], + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @bootnet_vid, eth_dst: @mac) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(@access_port) + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [table_id: 0, + priority: 50, + cookie: cookie, + idle_timeout: 300, + hard_timeout: 300, + flags: [:send_flow_rem], + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(in_port: @vlan_trunk_port, eth_src: @mac) + options = + [table_id: 0, + priority: 29, + cookie: cookie, + idle_timeout: 300, + hard_timeout: 300, + flags: [:send_flow_rem], + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + # Trunk Port, w/Trusted MAC -> Pop VLAN -> Access Port + for trusted <- @trusted_macs do + match = Openflow.Match.new( + in_port: @vlan_trunk_port, + eth_src: trusted, + eth_dst: @mac, + vlan_vid: @bootnet_vid + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(@access_port) + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [table_id: 0, + priority: 50, + cookie: cookie, + idle_timeout: 300, + hard_timeout: 300, + flags: [:send_flow_rem], + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(eth_src: trusted) + options = + [table_id: 0, + priority: 29, + cookie: cookie, + idle_timeout: 300, + hard_timeout: 300, + flags: [:send_flow_rem], + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + end + refute_receive %Openflow.ErrorMsg{}, 1000 + end + end + + describe "associate:register_usernet_rule" do + test "Flow install" do + # BOOTNET_USE_USER_NETWORK + # + # Reply from SDL to the authorized client + cookie = :crypto.strong_rand_bytes(8) |> :binary.decode_unsigned(:big) + match = Openflow.Match.new(in_port: @vxlan_port, eth_dst: @mac) + action = Openflow.Action.Output.new(@access_port) + ins = Openflow.Instruction.ApplyActions.new(action) + options = [ + cookie: cookie, + table_id: 0, + priority: 40, + idle_timeout: 32_768, + hard_timeout: 32_768, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + # HTTP request to the `CaptivePortal` via the SDL controller + match = Openflow.Match.new( + eth_type: 0x0800, + vlan_vid: {0x1000, 0x1000}, + ip_proto: 6, + tcp_dst: 443, + ipv4_dst: {192,168,5,4} + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), + Openflow.Action.Output.new(@vxlan_port) + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = [ + cookie: 0x2000000000000001, + table_id: 1, + priority: 30, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new( + eth_type: 0x0800, + vlan_vid: {0x1000, 0x1000}, + ip_proto: 6, + tcp_dst: 80, + ipv4_dst: {192,168,5,5} + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), + Openflow.Action.Output.new(@vxlan_port) + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = [ + cookie: 0x2000000000000001, + table_id: 1, + priority: 30, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + # Access Port, w/sMAC -> Push VLAN -> Table1 + # Table1, w/dMAC, w/VLAN -> Pop VLAN -> Access Port + match = Openflow.Match.new(in_port: @access_port, eth_src: @mac) + actions = [ + Openflow.Action.PushVlan.new, + Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}) + ] + insts = [ + Openflow.Instruction.ApplyActions.new(actions), + Openflow.Instruction.GotoTable.new(1) + ] + options = [ + cookie: cookie, + table_id: 0, + priority: 20, + idle_timeout: 32_768, + hard_timeout: 32_768, + flags: [:send_flow_rem], + match: match, + instructions: insts + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(in_port: @vlan_trunk_port, eth_src: @mac) + options = [ + cookie: cookie, + table_id: 0, + priority: 19, + idle_timeout: 32_768, + hard_timeout: 32_768, + flags: [:send_flow_rem], + match: match, + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(eth_src: @mac) + actions = [Openflow.Action.Output.new(:controller)] + insts = [Openflow.Instruction.ApplyActions.new(actions)] + options = [ + cookie: cookie, + table_id: 0, + priority: 18, + idle_timeout: 32_768, + hard_timeout: 32_768, + flags: [:send_flow_rem], + match: match, + instructions: insts + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(vlan_vid: {0x1000, 0x1000}, eth_dst: @mac) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(@access_port) + ] + insts = [Openflow.Instruction.ApplyActions.new(actions)] + options = [ + cookie: cookie, + table_id: 1, + priority: 50, + idle_timeout: 32_768, + hard_timeout: 32_768, + flags: [:send_flow_rem], + match: match, + instructions: insts + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + # VLAN 毎に登録する共通ルール + # Table1, w/VLAN -+-> Trunk + # +-> Pop VLAN -> Access Ports + # Trunk Port, w/VLAN -> Table1 + match = Openflow.Match.new(vlan_vid: @user_vid, eth_dst: @mcast) + actions = [ + Openflow.Action.Output.new(@vlan_trunk_port), + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(@access_port) + ] + insts = [Openflow.Instruction.ApplyActions.new(actions)] + options = [ + cookie: 0x200000000000001, + table_id: 1, + priority: 60, + match: match, + instructions: insts + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(vlan_vid: @user_vid) + actions = [ + Openflow.Action.Output.new(@vlan_trunk_port), + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(@access_port) + ] + insts = [Openflow.Instruction.ApplyActions.new(actions)] + options = [ + cookie: 0x200000000000001, + table_id: 1, + priority: 20, + match: match, + instructions: insts + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @user_vid) + insts = [Openflow.Instruction.GotoTable.new(1)] + options = [ + cookie: 0x200000000000001, + table_id: 0, + priority: 50, + match: match, + instructions: insts + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + + refute_receive %Openflow.ErrorMsg{}, 1000 + end + end + + defp wait_for_connected do + case Process.whereis(Flay) do + nil -> + wait_for_connected() + pid when is_pid(pid) -> + :ok + end + end end diff --git a/test/pf.ex b/test/pf.ex new file mode 100644 index 0000000..5762578 --- /dev/null +++ b/test/pf.ex @@ -0,0 +1,40 @@ +defmodule Pf do + use GenServer + + defmodule State do + defstruct [ + ifname: nil, + pcap_ref: nil, + tester_pid: nil + ] + end + + def start_link(ifname, pid) do + ifname = String.to_charlist(ifname) + GenServer.start_link(__MODULE__, [ifname, pid]) + end + + def init([ifname, pid]) do + {:ok, pid} = :epcap.start_link(interface: ifname, chroot: 'priv/tmp', inject: true) + %State{pcap_ref: pid, ifname: ifname, tester_pid: pid} + end + + def handle_cast({:inject, packet}, state) do + :epcap.send(state.pcap_ref, packet) + {:noreply, state} + end + def handle_cast(:stop, state) do + {:stop, :normal, state} + end + def handle_cast(_req, state) do + {:noreply, state} + end + + def handle_info({:packet, _dlt, _time, _len, data}, state) do + send(state.tester_pid, data) + {:noreply, state} + end + def handle_info(_info, state) do + {:noreply, state} + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs index 869559e..8f72200 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1 +1 @@ -ExUnit.start() +ExUnit.start(trace: true) From 892fd0fbaeeeb75a1a05dfad8e3f634572babbcd Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 21 Nov 2017 11:29:26 +0900 Subject: [PATCH 04/50] Refactored testcases --- test/flay.ex | 7 + test/flay_test.exs | 439 --------------------------------------------- test/flog_test.exs | 172 ++++++++++++++++++ test/pf.ex | 29 ++- 4 files changed, 201 insertions(+), 446 deletions(-) delete mode 100644 test/flay_test.exs create mode 100644 test/flog_test.exs diff --git a/test/flay.ex b/test/flay.ex index d218886..88490b1 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -32,6 +32,9 @@ defmodule Flay do {:noreply, %{state|reply_to: from}} end + def handle_cast({:register_pid, tester_pid}, state) do + {:noreply, %{state|tester_pid: tester_pid}} + end def handle_cast({:flow_install, flow_opts, tester_pid}, state) do send_flow_mod_add(state.datapath_id, flow_opts) flow_opts_to_ofp_print(flow_opts) @@ -42,6 +45,10 @@ defmodule Flay do send(state.tester_pid, error) {:noreply, state} end + def handle_info(%PacketIn{} = pktin, state) do + send(state.tester_pid, pktin) + {:noreply, state} + end def handle_info(%PortDesc.Reply{} = desc, state) do GenServer.reply(state.reply_to, desc) {:noreply, %{state|reply_to: nil}} diff --git a/test/flay_test.exs b/test/flay_test.exs deleted file mode 100644 index 8597de1..0000000 --- a/test/flay_test.exs +++ /dev/null @@ -1,439 +0,0 @@ -defmodule FlayTest do - use ExUnit.Case - use Bitwise - - @vlan_trunk_port 1 - @access_port 2 - @vxlan_port 3 - @bootnet_vid 0x1000 ||| 5 - @user_vid 0x1000 ||| 123 - @mcast {"010000000000", "010000000000"} - @mac "010203040506" - @sdl_vmac "000000000001" - @trusted_macs [ - "0800274d3297", - "0800274d3298", - "0800274d3299" - ] - - import Record - # Extract Erlang record for klarna/brod - for {name, schema} <- extract_all(from_lib: "pkt/include/pkt.hrl") do - defrecord(name, schema) - end - - setup do - Code.load_file("test/flay.ex") - Code.load_file("test/pf.ex") - Application.put_env(:tres, :protocol, :tcp, persistent: true) - Application.put_env(:tres, :port, 6633, persistent: true) - Application.put_env(:tres, :mac_connections, 1, persistent: true) - Application.put_env(:tres, :mac_acceptors, 1, persistent: true) - Application.put_env(:tres, :callback_module, Flay, persistent: true) - Application.put_env(:tres, :callback_args, self(), persistent: true) - Application.start(:binpp) - Application.start(:pkt) - Application.start(:epcap) - Application.start(:ranch) - Application.start(:tres) - wait_for_connected() - end - - describe "switch:merged_handler" do - test "OFPMP_DESC" do - %Openflow.Multipart.Desc.Reply{} = GenServer.call(Flay, :desc_stats, 5000) - end - - test "Flow install" do - options = [cookie: 0x8000000000000000] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new( - in_port: @vlan_trunk_port, - eth_dst: "ffffffffffff", - vlan_vid: {0x0000, 0x1fff}, - eth_type: 0x88cc - ) - ins = Openflow.Instruction.ApplyActions.new(Openflow.Action.Output.new(:controller)) - options = [ - cookie: 0x4000000000000000, - table_id: 0, - priority: 200, - match: match, - instructions: [ins] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # LLDP Packet to vlan trunk port mac_address - src = "00000000000a" - src_mac = <<(String.to_integer(src, 16))::48>> - dst = "ffffffffffff" - dst_mac = <<(String.to_integer(dst, 16))::48>> - lldp = lldp(pdus: [ - {:chassis_id, :mac_address, src_mac}, - {:port_id, :mac_address, src_mac}, - {:ttl, 120} - ]) - ether = ether(shost: src_mac, dhost: dst_mac, type: 0x88cc) - _packet = <<(:pkt.ether(ether))::bytes, (:pkt.lldp(lldp))::bytes>> - - %Openflow.Multipart.PortDesc.Reply{ports: ports} = GenServer.call(Flay, :port_desc_stats, 5000) - for port <- ports do - match = Openflow.Match.new(in_port: port.number, eth_src: port.hw_addr) - options = [ - cookie: 0x4000000000000000, - table_id: 0, - priority: 201, - match: match, - instructions: [] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # ARP Packet to vlan_trunk port mac address - arp = arp(arp_sha: src_mac, arp_sha: dst_mac) - ether = ether(shost: src_mac, dhost: dst_mac, type: 0x0806) - _packet = <<(:pkt.ether(ether))::bytes, (:pkt.arp(arp))::bytes>> - end - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "switch:uplink_escalation_flow" do - test "Flow install" do - match = Openflow.Match.new(eth_type: 0x0806) - action = Openflow.Action.Output.new(:controller) - ins = Openflow.Instruction.ApplyActions.new(action) - options = [ - cookie: 0x2000000000000000, - table_id: 0, - priority: 10, - match: match, - instructions: ins - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) - action = Openflow.Action.Output.new(:controller) - ins = Openflow.Instruction.ApplyActions.new(action) - options = [ - cookie: 0x2000000000000000, - table_id: 0, - priority: 10, - match: match, - instructions: ins - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port) - options = [ - cookie: 0x2000000000000000, - table_id: 0, - priority: 11, - match: match, - instructions: [] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "associate:register_bootstrap_rule" do - test "Flow install" do - cookie = :crypto.strong_rand_bytes(8) |> :binary.decode_unsigned(:big) - match = Openflow.Match.new(in_port: @access_port, eth_src: @mac) - actions = [ - Openflow.Action.PushVlan.new, - Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}), - Openflow.Action.Output.new(@vlan_trunk_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [table_id: 0, - priority: 50, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match, - instructions: [ins]] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # trusted mac - match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @bootnet_vid, eth_dst: @mcast) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [table_id: 0, - priority: 50, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match, - instructions: [ins]] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @bootnet_vid, eth_dst: @mac) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [table_id: 0, - priority: 50, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match, - instructions: [ins]] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port, eth_src: @mac) - options = - [table_id: 0, - priority: 29, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # Trunk Port, w/Trusted MAC -> Pop VLAN -> Access Port - for trusted <- @trusted_macs do - match = Openflow.Match.new( - in_port: @vlan_trunk_port, - eth_src: trusted, - eth_dst: @mac, - vlan_vid: @bootnet_vid - ) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [table_id: 0, - priority: 50, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match, - instructions: ins] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(eth_src: trusted) - options = - [table_id: 0, - priority: 29, - cookie: cookie, - idle_timeout: 300, - hard_timeout: 300, - flags: [:send_flow_rem], - match: match] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - end - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "associate:register_usernet_rule" do - test "Flow install" do - # BOOTNET_USE_USER_NETWORK - # - # Reply from SDL to the authorized client - cookie = :crypto.strong_rand_bytes(8) |> :binary.decode_unsigned(:big) - match = Openflow.Match.new(in_port: @vxlan_port, eth_dst: @mac) - action = Openflow.Action.Output.new(@access_port) - ins = Openflow.Instruction.ApplyActions.new(action) - options = [ - cookie: cookie, - table_id: 0, - priority: 40, - idle_timeout: 32_768, - hard_timeout: 32_768, - match: match, - instructions: [ins] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # HTTP request to the `CaptivePortal` via the SDL controller - match = Openflow.Match.new( - eth_type: 0x0800, - vlan_vid: {0x1000, 0x1000}, - ip_proto: 6, - tcp_dst: 443, - ipv4_dst: {192,168,5,4} - ) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(@vxlan_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = [ - cookie: 0x2000000000000001, - table_id: 1, - priority: 30, - match: match, - instructions: [ins] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new( - eth_type: 0x0800, - vlan_vid: {0x1000, 0x1000}, - ip_proto: 6, - tcp_dst: 80, - ipv4_dst: {192,168,5,5} - ) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(@vxlan_port) - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = [ - cookie: 0x2000000000000001, - table_id: 1, - priority: 30, - match: match, - instructions: [ins] - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # Access Port, w/sMAC -> Push VLAN -> Table1 - # Table1, w/dMAC, w/VLAN -> Pop VLAN -> Access Port - match = Openflow.Match.new(in_port: @access_port, eth_src: @mac) - actions = [ - Openflow.Action.PushVlan.new, - Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}) - ] - insts = [ - Openflow.Instruction.ApplyActions.new(actions), - Openflow.Instruction.GotoTable.new(1) - ] - options = [ - cookie: cookie, - table_id: 0, - priority: 20, - idle_timeout: 32_768, - hard_timeout: 32_768, - flags: [:send_flow_rem], - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port, eth_src: @mac) - options = [ - cookie: cookie, - table_id: 0, - priority: 19, - idle_timeout: 32_768, - hard_timeout: 32_768, - flags: [:send_flow_rem], - match: match, - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(eth_src: @mac) - actions = [Openflow.Action.Output.new(:controller)] - insts = [Openflow.Instruction.ApplyActions.new(actions)] - options = [ - cookie: cookie, - table_id: 0, - priority: 18, - idle_timeout: 32_768, - hard_timeout: 32_768, - flags: [:send_flow_rem], - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(vlan_vid: {0x1000, 0x1000}, eth_dst: @mac) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - insts = [Openflow.Instruction.ApplyActions.new(actions)] - options = [ - cookie: cookie, - table_id: 1, - priority: 50, - idle_timeout: 32_768, - hard_timeout: 32_768, - flags: [:send_flow_rem], - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - # VLAN 毎に登録する共通ルール - # Table1, w/VLAN -+-> Trunk - # +-> Pop VLAN -> Access Ports - # Trunk Port, w/VLAN -> Table1 - match = Openflow.Match.new(vlan_vid: @user_vid, eth_dst: @mcast) - actions = [ - Openflow.Action.Output.new(@vlan_trunk_port), - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - insts = [Openflow.Instruction.ApplyActions.new(actions)] - options = [ - cookie: 0x200000000000001, - table_id: 1, - priority: 60, - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(vlan_vid: @user_vid) - actions = [ - Openflow.Action.Output.new(@vlan_trunk_port), - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(@access_port) - ] - insts = [Openflow.Instruction.ApplyActions.new(actions)] - options = [ - cookie: 0x200000000000001, - table_id: 1, - priority: 20, - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - match = Openflow.Match.new(in_port: @vlan_trunk_port, vlan_vid: @user_vid) - insts = [Openflow.Instruction.GotoTable.new(1)] - options = [ - cookie: 0x200000000000001, - table_id: 0, - priority: 50, - match: match, - instructions: insts - ] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - defp wait_for_connected do - case Process.whereis(Flay) do - nil -> - wait_for_connected() - pid when is_pid(pid) -> - :ok - end - end -end diff --git a/test/flog_test.exs b/test/flog_test.exs new file mode 100644 index 0000000..857d17f --- /dev/null +++ b/test/flog_test.exs @@ -0,0 +1,172 @@ +defmodule FlogTest do + use ExUnit.Case, async: true + use Bitwise + + @vlan_trunk_port "veth0" # FIXME: + @access_port "veth3" # FIXME: + @vxlan_port "veth4" # FIXME: + #@bootnet_vid 0x1000 ||| 5 + #@user_vid 0x1000 ||| 123 + @mcast {"010000000000", "010000000000"} + #@bcast "ffffffffffff" + #@mac "010203040506" + #@sdl_vmac "000000000001" + #@trusted_macs [ + # "0800274d3297", + # "0800274d3298", + # "0800274d3299" + #] + + import Record + # Extract Erlang record for msantos/pkt + for {name, schema} <- extract_all(from_lib: "pkt/include/pkt.hrl") do + defrecord(name, schema) + end + + Code.load_file("test/flay.ex") + Code.load_file("test/pf.ex") + + setup_all do + setup_applications() + wait_for_connected() + ports = get_ports_desc() + vlan_trunk = Enum.find(ports, fn(port) -> port.name == @vlan_trunk_port end) + vxlan_port = Enum.find(ports, fn(port) -> port.name == @vxlan_port end) + port = Enum.find(ports, fn(port) -> port.name == @access_port end) + options = [ + vlan_trunk: vlan_trunk, + vxlan_port: vxlan_port, + port: port + ] + {:ok, options} + end + + describe("switch:merged_handler:table=0,priority=0,cookie=0x8000000000000000,actions=drop") do + test "Install Flow", state do + options = [ + cookie: 0x8000000000000000, + table_id: 0, + priority: 0 + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:merged_handler:" <> + "table=0,priority=200,cookie=0x4000000000000000,in_port=%d,dl_dst=%s,dl_vlan=0x0000/0x1fff,dl_type=%s," <> + "actions=controller") do + test "Install Flow", state do + match = Openflow.Match.new( + in_port: state.vlan_trunk.number, + eth_dst: @bcast, + vlan_vid: {0x0000, 0x1fff}, + eth_type: 0x88cc + ) + action = Openflow.Action.Output.new(:controller) + ins = Openflow.Instruction.ApplyActions.new(action) + options = + [cookie: 0x4000000000000000, + table_id: 0, + priority: 200, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:merged_handler:" <> + "table=0,priority=201,cookie=0x4000000000000000,in_port=%d,dl_src=%s,actions=drop") do + test "Install Flow", state do + for port <- [state.vlan_trunk, state.port] do + match = Openflow.Match.new(in_port: port.number, eth_src: port.hw_addr) + options = + [cookie: 0x4000000000000000, + table_id: 0, + priority: 201, + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + end + + describe("switch:uplink_escalation_flow:" <> + "table=0,priority=10,cookie=0x2000000000000000,arp,actions=controller") do + test "Install Flow" do + match = Openflow.Match.new(eth_type: 0x0806) + action = Openflow.Action.Output.new(:controller) + ins = Openflow.Instruction.ApplyActions.new(action) + options = + [cookie: 0x2000000000000000, + table_id: 0, + priority: 10, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:uplink_escalation_flow:" <> + "table=0,priority=10,cookie=0x2000000000000000,udp,udp_dst=67,actions=controller") do + test "Install Flow" do + match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) + action = Openflow.Action.Output.new(:controller) + ins = Openflow.Instruction.ApplyActions.new(action) + options = + [cookie: 0x2000000000000000, + table_id: 0, + priority: 10, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:uplink_escalation_flow:" <> + "table=0,priority=11,cookie=0x2000000000000000,in_port={trunk_port},actions=drop") do + test "Install Flow", state do + match = Openflow.Match.new(in_port: state.vlan_trunk.number) + options = + [cookie: 0x2000000000000000, + table_id: 0, + priority: 11, + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + # private functions + + defp setup_applications do + Application.put_env(:tres, :protocol, :tcp, persistent: true) + Application.put_env(:tres, :port, 6633, persistent: true) + Application.put_env(:tres, :mac_connections, 1, persistent: true) + Application.put_env(:tres, :mac_acceptors, 1, persistent: true) + Application.put_env(:tres, :callback_module, Flay, persistent: true) + Application.put_env(:tres, :callback_args, self(), persistent: true) + Application.start(:binpp) + Application.start(:pkt) + Application.start(:epcap) + Application.start(:ranch) + Application.start(:tres) + end + + defp wait_for_connected do + case Process.whereis(Flay) do + nil -> + wait_for_connected() + pid when is_pid(pid) -> + :ok + end + end + + defp get_ports_desc do + port_desc = GenServer.call(Flay, :port_desc_stats, 5000) + port_desc.ports + end +end diff --git a/test/pf.ex b/test/pf.ex index 5762578..408e4fa 100644 --- a/test/pf.ex +++ b/test/pf.ex @@ -9,14 +9,28 @@ defmodule Pf do ] end - def start_link(ifname, pid) do - ifname = String.to_charlist(ifname) - GenServer.start_link(__MODULE__, [ifname, pid]) + def inject!(pid, packet) do + GenServer.cast(pid, {:inject, packet}) end - def init([ifname, pid]) do - {:ok, pid} = :epcap.start_link(interface: ifname, chroot: 'priv/tmp', inject: true) - %State{pcap_ref: pid, ifname: ifname, tester_pid: pid} + def start_link(ifname) do + ifname = String.to_charlist(ifname) + GenServer.start_link(__MODULE__, [ifname, self()]) + end + + def init([ifname, tester_pid]) do + {:ok, epcap_pid} = + :epcap.start_link( + interface: ifname, + promiscuous: true, + inject: true + ) + state = %State{ + pcap_ref: epcap_pid, + ifname: ifname, + tester_pid: tester_pid + } + {:ok, state} end def handle_cast({:inject, packet}, state) do @@ -31,7 +45,8 @@ defmodule Pf do end def handle_info({:packet, _dlt, _time, _len, data}, state) do - send(state.tester_pid, data) + pkt = :pkt.decapsulate(data) + send(state.tester_pid, {pkt, self()}) {:noreply, state} end def handle_info(_info, state) do From bbbe62bb25340ffef11228a606c261a39186532b Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 21 Nov 2017 13:37:30 +0900 Subject: [PATCH 05/50] Add test cases for After authenticated flow patterns --- test/flog_test.exs | 425 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 412 insertions(+), 13 deletions(-) diff --git a/test/flog_test.exs b/test/flog_test.exs index 857d17f..6696728 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -1,21 +1,24 @@ defmodule FlogTest do - use ExUnit.Case, async: true + use ExUnit.Case, async: false use Bitwise @vlan_trunk_port "veth0" # FIXME: @access_port "veth3" # FIXME: @vxlan_port "veth4" # FIXME: - #@bootnet_vid 0x1000 ||| 5 - #@user_vid 0x1000 ||| 123 + @bootnet_vid 0x1000 ||| 5 + @user_vid 0x1000 ||| 123 + @vlan_present {0x1000, 0x1000} @mcast {"010000000000", "010000000000"} - #@bcast "ffffffffffff" - #@mac "010203040506" - #@sdl_vmac "000000000001" - #@trusted_macs [ - # "0800274d3297", - # "0800274d3298", - # "0800274d3299" - #] + @sdl_vmac "000000000001" + @bcast "ffffffffffff" + @mac "010203040506" + @auth_ipv4_address {192,168,5,4} + @captive_ipv4_address {192,168,5,5} + @trusted_macs [ + "0800274d3297", + "0800274d3298", + "0800274d3299" + ] import Record # Extract Erlang record for msantos/pkt @@ -26,6 +29,7 @@ defmodule FlogTest do Code.load_file("test/flay.ex") Code.load_file("test/pf.ex") + # GIVEN setup_all do setup_applications() wait_for_connected() @@ -33,16 +37,20 @@ defmodule FlogTest do vlan_trunk = Enum.find(ports, fn(port) -> port.name == @vlan_trunk_port end) vxlan_port = Enum.find(ports, fn(port) -> port.name == @vxlan_port end) port = Enum.find(ports, fn(port) -> port.name == @access_port end) + cookie = 0x1000000000000001 + timeout = 32678 options = [ vlan_trunk: vlan_trunk, vxlan_port: vxlan_port, - port: port + port: port, + cookie: cookie, + timeout: timeout ] {:ok, options} end describe("switch:merged_handler:table=0,priority=0,cookie=0x8000000000000000,actions=drop") do - test "Install Flow", state do + test "Install Flow" do options = [ cookie: 0x8000000000000000, table_id: 0, @@ -140,6 +148,397 @@ defmodule FlogTest do end end + describe("associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "dl_src={mac},in_port={port_no}," <> + "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,output:{vlan_trunk_port}") do + test "Install Flow", state do + match = Openflow.Match.new( + in_port: state.port.number, + eth_src: @mac + ) + actions = [ + Openflow.Action.PushVlan.new, + Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}), + Openflow.Action.Output.new(state.vlan_trunk.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: state.cookie, + table_id: 0, + priority: 20, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "in_port={vlan_trunk_port},dl_vlan={vlan},dl_dst={mcast}," <> + "actions=strip_vlan,{outputs}") do + test "Install Flow", state do + match = Openflow.Match.new( + in_port: state.vlan_trunk.number, + vlan_vid: @bootnet_vid, + eth_dst: @mcast + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(state.port.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: state.cookie, + table_id: 0, + priority: 50, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "in_port={vlan_trunk_port},dl_vlan={vlan},dl_dst={mac}," <> + "actions=strip_vlan,output:{port_no}") do + test "Install Flow", state do + match = Openflow.Match.new( + in_port: state.vlan_trunk.number, + vlan_vid: @bootnet_vid, + eth_dst: @mac + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(state.port.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: state.cookie, + table_id: 0, + priority: 50, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "dl_src={mac},in_port={vlan_trunk_port}," <> + "actions=drop") do + test "Install Flow", state do + match = Openflow.Match.new( + in_port: state.vlan_trunk.number, + eth_src: @mac + ) + options = + [cookie: state.cookie, + table_id: 0, + priority: 19, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "in_port={vlan_trunk_port},dl_vlan={vlan},dl_src={trusted},dl_dst={mac}," <> + "actions=strip_vlan,output:{port_no}") do + test "Install Flow", state do + for trusted <- @trusted_macs do + match = Openflow.Match.new( + in_port: state.vlan_trunk.number, + vlan_vid: @bootnet_vid, + eth_src: trusted, + eth_dst: @mac + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(state.port.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: state.cookie, + table_id: 0, + priority: 50, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + end + + describe("associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "dl_src={trusted}," <> + "actions=drop") do + test "Install Flow", state do + for trusted <- @trusted_macs do + match = Openflow.Match.new(eth_src: trusted) + options = + [cookie: state.cookie, + table_id: 0, + priority: 29, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + end + + describe("associate:register_usernet_rule:" <> + "table=0, priority=40,cookie={cookie},hard_timeout={htimeout},in_port={vxlan_port},dl_dst={mac}," <> + "actions=output:{port_no}") do + test "Install Flow", state do + match = Openflow.Match.new( + in_port: state.vxlan_port.number, + eth_dst: @mac + ) + actions = [ + Openflow.Action.Output.new(state.port.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: state.cookie, + table_id: 0, + priority: 40, + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "vlan_vid=0x1000/0x1000,tcp,tcp_dst=443,nw_dst={auth_ipv4_address}," <> + "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vxlan_port}") do + test "Install Flow", state do + match = Openflow.Match.new( + vlan_vid: @vlan_present, + eth_type: 0x0800, + ip_proto: 6, + ipv4_dst: @auth_ipv4_address, + tcp_dst: 443 + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), + Openflow.Action.Output.new(state.port.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: 0x2000000000000001, + table_id: 1, + priority: 30, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "vlan_vid=0x1000/0x1000,tcp,tcp_dst=80,nw_dst={captive_ipv4_address}," <> + "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vxlan_port}") do + test "Install Flow", state do + match = Openflow.Match.new( + vlan_vid: @vlan_present, + eth_type: 0x0800, + ip_proto: 6, + ipv4_dst: @captive_ipv4_address, + tcp_dst: 80 + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), + Openflow.Action.Output.new(state.port.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: 0x2000000000000001, + table_id: 1, + priority: 30, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem, dl_src={mac},in_port={port_no}," <> + "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,goto_table:1") do + test "Install Flow", state do + match = Openflow.Match.new(in_port: state.port.number, eth_src: @mac) + actions = [ + Openflow.Action.PushVlan.new, + Openflow.Action.SetField.new({:vlan_vid, @user_vid}) + ] + ins = [ + Openflow.Instruction.ApplyActions.new(actions), + Openflow.Instruction.GotoTable.new(1) + ] + options = + [cookie: state.cookie, + table_id: 0, + priority: 20, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,dl_src={mac},in_port={vlan_trunk_port}," <> + "actions=drop") do + test "Install Flow", state do + match = Openflow.Match.new(in_port: state.vlan_trunk.number, eth_src: @mac) + options = + [cookie: state.cookie, + table_id: 0, + priority: 19, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,dl_src={mac}," <> + "actions=controller") do + test "Install Flow", state do + match = Openflow.Match.new(eth_src: @mac) + actions = [Openflow.Action.Output.new(:controller)] + ins = [Openflow.Instruction.ApplyActions.new(actions)] + options = + [cookie: state.cookie, + table_id: 0, + priority: 18, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,vlan_vid=0x1000/0x1000,dl_dst={mac}," <> + "actions=strip_vlan,output:{port_no}") do + test "Install Flow", state do + match = Openflow.Match.new(vlan_vid: @vlan_present, eth_src: @mac) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(state.port.number)] + ins = [Openflow.Instruction.ApplyActions.new(actions)] + options = + [cookie: state.cookie, + table_id: 1, + priority: 50, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "dl_vlan={vlan},dl_dst={mcast}," <> + "actions=output:{vlan_trunk_port},strip_vlan,{outputs}") do + test "Install Flow", state do + match = Openflow.Match.new(vlan_vid: @user_vid, eth_dst: @mcast) + actions = [ + Openflow.Action.Output.new(state.vlan_trunk.number), + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(state.port.number)] + ins = [Openflow.Instruction.ApplyActions.new(actions)] + options = + [cookie: 0x2000000000000001, + table_id: 1, + priority: 60, + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "dl_vlan={vlan}," <> + "actions=output:{vlan_trunk_port},strip_vlan,{outputs}") do + test "Install Flow", state do + match = Openflow.Match.new(vlan_vid: @user_vid) + actions = [ + Openflow.Action.Output.new(state.vlan_trunk.number), + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(state.port.number)] + ins = [Openflow.Instruction.ApplyActions.new(actions)] + options = + [cookie: 0x2000000000000001, + table_id: 1, + priority: 20, + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("associate:register_usernet_rule:" <> + "in_port={vlan_trunk_port},dl_vlan={vlan}," <> + "actions=goto_table:1") do + test "Install Flow", state do + match = Openflow.Match.new(in_port: state.vlan_trunk.number, vlan_vid: @user_vid) + ins = [Openflow.Instruction.GotoTable.new(1)] + options = + [cookie: 0x2000000000000001, + table_id: 0, + priority: 50, + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + # private functions defp setup_applications do From 4b4d6ce0356cd9133d969004a35d122fbc998e74 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 21 Nov 2017 13:57:24 +0900 Subject: [PATCH 06/50] Add test cases for NAT flow patterns --- test/flog_test.exs | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/flog_test.exs b/test/flog_test.exs index 6696728..6b2e526 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -14,6 +14,11 @@ defmodule FlogTest do @mac "010203040506" @auth_ipv4_address {192,168,5,4} @captive_ipv4_address {192,168,5,5} + @client_ip {192,168,5,10} + @client_farm_ip {192,168,255,1} + @gateway_ip {192,168,5,4} + @farm_gw_ip {192,168,255,254} + @farm_gw_mac "00000000000f" @trusted_macs [ "0800274d3297", "0800274d3298", @@ -539,6 +544,69 @@ defmodule FlogTest do end end + describe("switch:register_farm_nat_rule:" <> + "dl_src={src_mac},arp,arp_spa={src_ip},arp_tpa={dst_ip}," <> + "actions=set_field:{farm_vmac}->eth_dst,set_field:{src_fip}->arp_spa," <> + "set_field:{dst_fip}->arp_tpa,output:{vlan_trunk_port}") do + test "Install Flow", state do + match = Openflow.Match.new( + eth_src: @mac, + eth_type: 0x0806, + arp_spa: @client_ip, + arp_tpa: @gateway_ip + ) + actions = [ + Openflow.Action.SetField.new({:eth_dst, @farm_gw_mac}), + Openflow.Action.SetField.new({:arp_spa, @client_farm_ip}), + Openflow.Action.SetField.new({:arp_tpa, @farm_gw_ip}), + Openflow.Action.PushVlan.new, + Openflow.Action.SetField.new({:vlan_vid, @user_vid}), + Openflow.Action.Output.new(state.vlan_trunk.number), + ] + ins = [Openflow.Instruction.ApplyActions.new(actions)] + options = + [cookie: 0x3000000000000001, + table_id: 1, + priority: 30, + idle_timeout: 60, + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + + describe("switch:register_farm_nat_rule:" <> + "dl_src={src_mac},ip,nw_src={src_ip},nw_dst={dst_ip}," <> + "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,set_field:{farm_vmac}->eth_dst," <> + "set_field:{src_fip}->nw_src,output:{vlan_trunk_port}") do + test "Install Flow", state do + match = Openflow.Match.new( + eth_src: @mac, + eth_type: 0x0800, + ipv4_src: @client_ip, + ipv4_dst: {8,8,8,8} + ) + actions = [ + Openflow.Action.SetField.new({:eth_dst, @farm_gw_mac}), + Openflow.Action.PushVlan.new, + Openflow.Action.SetField.new({:vlan_vid, @user_vid}), + Openflow.Action.SetField.new({:ipv4_src, @client_farm_ip}), + Openflow.Action.Output.new(state.vlan_trunk.number), + ] + ins = [Openflow.Instruction.ApplyActions.new(actions)] + options = + [cookie: 0x3000000000000001, + table_id: 1, + priority: 30, + idle_timeout: 60, + match: match, + instructions: ins] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_received %Openflow.ErrorMsg{}, 1000 + end + end + # private functions defp setup_applications do From 67ad6900d8c72377aa8a61739e3197efdebd5c1d Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 21 Nov 2017 14:07:06 +0900 Subject: [PATCH 07/50] O TA ME SHI --- test/flay.ex | 14 +++++++++++++- test/flog_test.exs | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/test/flay.ex b/test/flay.ex index 88490b1..94b5e64 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -31,6 +31,10 @@ defmodule Flay do send_message(Desc.Request.new, state.datapath_id) {:noreply, %{state|reply_to: from}} end + def handle_call(:flow_stats, from, state) do + send_message(Flow.Request.new, state.datapath_id) + {:noreply, %{state|reply_to: from}} + end def handle_cast({:register_pid, tester_pid}, state) do {:noreply, %{state|tester_pid: tester_pid}} @@ -40,6 +44,10 @@ defmodule Flay do flow_opts_to_ofp_print(flow_opts) {:noreply, %{state|tester_pid: tester_pid}} end + def handle_cast(:flow_del, state) do + send_flow_mod_delete(state.datapath_id, match: Match.new) + {:noreply, state} + end def handle_info(%ErrorMsg{} = error, state) do send(state.tester_pid, error) @@ -57,9 +65,13 @@ defmodule Flay do GenServer.reply(state.reply_to, desc) {:noreply, %{state|reply_to: nil}} end + def handle_info(%Flow.Reply{} = desc, state) do + GenServer.reply(state.reply_to, desc) + {:noreply, %{state|reply_to: nil}} + end # `Catch all` function is required. def handle_info(info, state) do - :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}") + # :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}") {:noreply, state} end diff --git a/test/flog_test.exs b/test/flog_test.exs index 6b2e526..ab247b3 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -35,7 +35,7 @@ defmodule FlogTest do Code.load_file("test/pf.ex") # GIVEN - setup_all do + setup do setup_applications() wait_for_connected() ports = get_ports_desc() @@ -51,6 +51,11 @@ defmodule FlogTest do cookie: cookie, timeout: timeout ] + + on_exit fn -> + print_flows() + GenServer.cast(Flay, :flow_del) + end {:ok, options} end @@ -636,4 +641,11 @@ defmodule FlogTest do port_desc = GenServer.call(Flay, :port_desc_stats, 5000) port_desc.ports end + + defp print_flows do + flow_stats = GenServer.call(Flay, :flow_stats, 5000) + for flow <- flow_stats.flows do + IO.inspect(flow) + end + end end From 4c429d81d72361e2f4730288f4462f09efb23e06 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 21 Nov 2017 14:18:45 +0900 Subject: [PATCH 08/50] Change refute_received -> refute_receive --- test/flog_test.exs | 48 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/flog_test.exs b/test/flog_test.exs index ab247b3..096a226 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -67,7 +67,7 @@ defmodule FlogTest do priority: 0 ] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -90,7 +90,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -105,7 +105,7 @@ defmodule FlogTest do priority: 201, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end end @@ -123,7 +123,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -140,7 +140,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -154,7 +154,7 @@ defmodule FlogTest do priority: 11, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -183,7 +183,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -212,7 +212,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -241,7 +241,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -263,7 +263,7 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -294,7 +294,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end end @@ -315,7 +315,7 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end end @@ -341,7 +341,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -369,7 +369,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -397,7 +397,7 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -424,7 +424,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -442,7 +442,7 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -463,7 +463,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -486,7 +486,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -507,7 +507,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -528,7 +528,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -545,7 +545,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -577,7 +577,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -608,7 +608,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_received %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end From b592219bd33df1c658fe4221b911113b66b582d0 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 21 Nov 2017 15:04:45 +0900 Subject: [PATCH 09/50] Improved ofp-print --- test/flay.ex | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/flay.ex b/test/flay.ex index 94b5e64..e37c239 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -70,7 +70,7 @@ defmodule Flay do {:noreply, %{state|reply_to: nil}} end # `Catch all` function is required. - def handle_info(info, state) do + def handle_info(_info, state) do # :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}") {:noreply, state} end @@ -83,11 +83,10 @@ defmodule Flay do |> Openflow.to_binary |> binary_to_space_delimited_hex |> ofp_print_cmd - |> IO.inspect + |> Logger.info end defp ofp_print_cmd(print_args) do - IO.inspect("\n") {result, _code} = System.cmd("ovs-ofctl", ["ofp-print", "#{print_args}"]) result end From 2974723807ff2f84ec1005fc1d2df93791da8151 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Wed, 22 Nov 2017 01:09:07 +0900 Subject: [PATCH 10/50] Confession of guilt --- lib/openflow/enums.ex | 19 ++ lib/openflow/instructions/experimenter.ex | 2 +- lib/openflow/match.ex | 51 ++- lib/openflow/multipart/table_features/body.ex | 297 ++++++++++++++++++ .../multipart/table_features/reply.ex | 41 +++ .../multipart/table_features/request.ex | 41 +++ test/flay.ex | 64 +++- test/flog_test.exs | 5 +- 8 files changed, 507 insertions(+), 13 deletions(-) create mode 100644 lib/openflow/multipart/table_features/body.ex create mode 100644 lib/openflow/multipart/table_features/reply.ex create mode 100644 lib/openflow/multipart/table_features/request.ex diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex index ab41448..4d43189 100644 --- a/lib/openflow/enums.ex +++ b/lib/openflow/enums.ex @@ -1047,6 +1047,25 @@ defmodule Openflow.Enums do add: 0, delete: 1, clear: 2 + ], + + table_feature_prop_type: [ + instructions: 0, + instructions_miss: 1, + next_tables: 2, + next_tables_miss: 3, + write_actions: 4, + write_actions_miss: 5, + apply_actions: 6, + apply_actions_miss: 7, + match: 8, + wildcards: 10, + write_setfield: 12, + write_setfield_miss: 13, + apply_setfield: 14, + apply_setfield_miss: 15, + experimenter: 0xfffe, + experimenter_miss: 0xffff ] ] diff --git a/lib/openflow/instructions/experimenter.ex b/lib/openflow/instructions/experimenter.ex index 138b10b..c0c624d 100644 --- a/lib/openflow/instructions/experimenter.ex +++ b/lib/openflow/instructions/experimenter.ex @@ -3,7 +3,7 @@ defmodule Openflow.Instruction.Experimenter do alias __MODULE__ - def new(exp_id, data) do + def new(exp_id, data \\ "") do %Experimenter{exp_id: exp_id, data: data} end diff --git a/lib/openflow/match.ex b/lib/openflow/match.ex index bd40634..0bd54a7 100644 --- a/lib/openflow/match.ex +++ b/lib/openflow/match.ex @@ -33,27 +33,54 @@ defmodule Openflow.Match do end def codec_header(oxm_field) when is_atom(oxm_field) do + oxm_field = case has_mask(oxm_field) do + 1 -> + string = to_string(oxm_field) + "masked_" <> field = string + String.to_atom(field) + 0 -> + oxm_field + end 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] -> + has_mask = has_mask(oxm_field) + <> + experimenter when experimenter in [:nicira_ext_match, :onf_ext_match, :hp_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) - <> + has_mask = has_mask(oxm_field) + <> end end - def codec_header(<>) do + def codec_header(<>) do oxm_class = Openflow.Enums.to_atom(oxm_class_int, :oxm_class) - Openflow.Enums.to_atom(oxm_field_int, oxm_class) + case oxm_has_mask do + 0 -> Openflow.Enums.to_atom(oxm_field_int, oxm_class) + 1 -> + field_str = + oxm_field_int + |> Openflow.Enums.to_atom(oxm_class) + |> to_string + String.to_atom("masked_" <> field_str) + end end - def codec_header(<<0xffff::16, oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, experimenter_int::32>>) do + 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) + case oxm_has_mask do + 0 -> Openflow.Enums.to_atom(oxm_field_int, experimenter) + 1 -> + field_str = + oxm_field_int + |> Openflow.Enums.to_atom(experimenter) + |> to_string + String.to_atom("masked_" <> field_str) + end end def header_size(<<_oxm_class_int::16, _oxm_field_int::7, _oxm_has_mask::1, _oxm_length::8, _::bytes>>), @@ -151,4 +178,16 @@ defmodule Openflow.Match do match_class = Openflow.Match.Field.vendor_of(field_name) %{class: match_class, field: field_name, has_mask: false, value: value_bin} end + + defp has_mask(oxm_field) when is_atom(oxm_field) do + has_mask? = + oxm_field + |> to_string + |> String.match?(~r/^masked_/) + if has_mask? do + 1 + else + 0 + end + end end diff --git a/lib/openflow/multipart/table_features/body.ex b/lib/openflow/multipart/table_features/body.ex new file mode 100644 index 0000000..c4903fc --- /dev/null +++ b/lib/openflow/multipart/table_features/body.ex @@ -0,0 +1,297 @@ +defmodule Openflow.Multipart.TableFeatures.Body do + defstruct [ + table_id: 0, + name: "", + metadata_match: 0, + metadata_write: 0, + config: [], + max_entries: 0, + instructions: nil, + instructions_miss: nil, + next_tables: nil, + next_tables_miss: nil, + write_actions: nil, + write_actions_miss: nil, + apply_actions: nil, + apply_actions_miss: nil, + match: nil, + wildcards: nil, + write_setfield: nil, + write_setfield_miss: nil, + apply_setfield: nil, + apply_setfield_miss: nil + ] + + alias __MODULE__ + + @max_table_name_len 32 + @prop_header_length 4 + @table_features_length 64 + + @instructions 0 + @instructions_miss 1 + @next_tables 2 + @next_tables_miss 3 + @write_actions 4 + @write_actions_miss 5 + @apply_actions 6 + @apply_actions_miss 7 + @match 8 + @wildcards 10 + @write_setfield 12 + @write_setfield_miss 13 + @apply_setfield 14 + @apply_setfield_miss 15 + + @prop_keys [ + :instructions, + :instructions_miss, + :next_tables, + :next_tables_miss, + :write_actions, + :write_actions_miss, + :apply_actions, + :apply_actions_miss, + :match, + :wildcards, + :write_setfield, + :write_setfield_miss, + :apply_setfield, + :apply_setfield_miss + ] + + def new(options) do + %Body{ + table_id: options[:table_id] || 0, + name: options[:name] || "", + metadata_match: options[:metadata_match] || 0, + metadata_write: options[:metadata_write] || 0, + config: options[:config] || [], + max_entries: options[:max_entries] || 0, + instructions: options[:instructions], + instructions_miss: options[:instructions_miss], + next_tables: options[:next_tables], + next_tables_miss: options[:next_tables_miss], + write_actions: options[:write_actions], + write_actions_miss: options[:write_actions_miss], + apply_actions: options[:apply_actions], + apply_actions_miss: options[:apply_actions_miss], + match: options[:match], + wildcards: options[:wildcards], + write_setfield: options[:write_setfield], + write_setfield_miss: options[:write_setfield_miss], + apply_setfield: options[:apply_setfield], + apply_setfield_miss: options[:apply_setfield_miss] + } + end + + def read(binary) do + do_read([], binary) + end + + def to_binary(features) do + do_to_binary("", features) + end + + # private functions + + defp do_to_binary(acc, []), do: acc + defp do_to_binary(acc, [table|rest]) do + do_to_binary(<>, rest) + end + + defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do + <> = binary + do_read([decode(features_bin)|acc], rest) + end + + defp decode(<<_length::16, table_id::8, _::size(5)-unit(8), + name_bin::size(@max_table_name_len)-bytes, + metadata_match::64, metadata_write::64, + config_int::32, max_entries::32, props_bin::bytes>>) do + name = Openflow.Utils.decode_string(name_bin) + config = Openflow.Enums.int_to_flags(config_int, :table_config) + body = %Body{table_id: table_id, + name: name, + metadata_match: metadata_match, + metadata_write: metadata_write, + config: config, + max_entries: max_entries} + decode_props(body, props_bin) + end + + defp encode(table) do + filter_fn = fn(key) -> not is_nil(Map.get(table, key)) end + keys = Enum.filter(@prop_keys, filter_fn) + props_bin = encode_props("", table, keys) + length = @table_features_length + byte_size(props_bin) + %Body{table_id: table_id, + name: name, + metadata_match: metadata_match, + metadata_write: metadata_write, + config: config, + max_entries: max_entries} = table + config_int = Openflow.Enums.flags_to_int(config, :table_config) + name_bin = Openflow.Utils.encode_string(name, @max_table_name_len) + <> + end + + defp decode_props(body, ""), do: body + defp decode_props(body, <>) + when type_int == @instructions or type_int == @instructions_miss do + pad_length = Openflow.Utils.pad_length(length, 8) + value_length = length - @prop_header_length + <> = tail + instructions = decode_instructions([], instructions_bin) + type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) + body + |> struct(%{type => instructions}) + |> decode_props(rest) + end + defp decode_props(body, <>) + when type_int == @next_tables or type_int == @next_tables_miss do + pad_length = Openflow.Utils.pad_length(length, 8) + value_length = length - @prop_header_length + <> = tail + next_tables = for <>, do: table_id + type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) + body + |> struct(%{type => next_tables}) + |> decode_props(rest) + end + defp decode_props(body, <>) + when type_int == @write_actions or + type_int == @write_actions_miss or + type_int == @apply_actions or + type_int == @apply_actions_miss do + pad_length = Openflow.Utils.pad_length(length, 8) + value_length = length - @prop_header_length + <> = tail + actions = decode_actions([], actions_bin) + type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) + body + |> struct(%{type => actions}) + |> decode_props(rest) + end + defp decode_props(body, <>) + when type_int == @match or + type_int == @wildcards or + type_int == @write_setfield or + type_int == @write_setfield_miss or + type_int == @apply_setfield or + type_int == @apply_setfield_miss do + pad_length = Openflow.Utils.pad_length(length, 8) + value_length = length - @prop_header_length + <> = tail + matches = decode_matches([], matches_bin) + type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) + body + |> struct(%{type => matches}) + |> decode_props(rest) + end + defp decode_props(body, <<_type_int::16, length::16, tail::bytes>>) do + pad_length = Openflow.Utils.pad_length(length, 8) + value_length = length - @prop_header_length + <<_::size(value_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + decode_props(body, rest) + end + + defp encode_props(acc, _table, []), do: acc + defp encode_props(acc, table, [type|rest]) + when type == :instructions or type == :instructions_miss do + type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) + instructions_bin = encode_instructions("", Map.get(table, type)) + length = @prop_header_length + byte_size(instructions_bin) + pad_length = Openflow.Utils.pad_length(length, 8) + body = <> + encode_props(<>, table, rest) + end + defp encode_props(acc, table, [type|rest]) + when type == :next_tables or type == :next_tables_miss do + type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) + next_tables_bin = to_string(Map.get(table, type)) + length = @prop_header_length + byte_size(next_tables_bin) + pad_length = Openflow.Utils.pad_length(length, 8) + body = <> + encode_props(<>, table, rest) + end + defp encode_props(acc, table, [type|rest]) + when (type == :write_actions or + type == :write_actions_miss or + type == :apply_actions or + type == :apply_actions_miss) do + type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) + actions_bin = encode_actions("", Map.get(table, type)) + length = @prop_header_length + byte_size(actions_bin) + pad_length = Openflow.Utils.pad_length(length, 8) + body = <> + encode_props(<>, table, rest) + end + defp encode_props(acc, table, [type|rest]) + when (type == :match or + type == :wildcards or + type == :write_setfield or + type == :write_setfield_miss or + type == :apply_setfield or + type == :apply_setfield_miss) do + type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) + matches_bin = encode_matches("", Map.get(table, type)) + length = @prop_header_length + byte_size(matches_bin) + pad_length = Openflow.Utils.pad_length(length, 8) + body = <> + encode_props(<>, table, rest) + end + + defp decode_instructions(acc, ""), do: Enum.reverse(acc) + defp decode_instructions(acc, <<0xffff::16, _::16, exp_id::32, rest::bytes>>) do + decode_instructions([Openflow.Instruction.Experimenter.new(exp_id)|acc], rest) + end + defp decode_instructions(acc, <>) do + instruction = Openflow.Enums.to_atom(type_int, :instruction_type) + decode_instructions([instruction|acc], rest) + end + + defp encode_instructions(acc, []), do: acc + defp encode_instructions(acc, [%Openflow.Instruction.Experimenter{exp_id: exp_id}|rest]) do + encode_instructions(<>, rest) + end + defp encode_instructions(acc, [type|rest]) do + type_int = Openflow.Enums.to_int(type, :instruction_type) + encode_instructions(<>, rest) + end + + defp decode_actions(acc, ""), do: Enum.reverse(acc) + defp decode_actions(acc, <<0xffff::16, _::16, exp_id::32, rest::bytes>>) do + decode_actions([Openflow.Action.Experimenter.new(exp_id)|acc], rest) + end + defp decode_actions(acc, <>) do + action = Openflow.Enums.to_atom(type_int, :action_type) + decode_actions([action|acc], rest) + end + + defp encode_actions(acc, []), do: acc + defp encode_actions(acc, [%Openflow.Action.Experimenter{exp_id: exp_id}|rest]) do + encode_actions(<>, rest) + end + defp encode_actions(acc, [type|rest]) do + type_int = Openflow.Enums.to_int(type, :action_type) + encode_actions(<>, rest) + end + + defp decode_matches(acc, ""), do: Enum.reverse(acc) + defp decode_matches(acc, binary) do + length = Openflow.Match.header_size(binary) + <> = binary + field = Openflow.Match.codec_header(header_bin) + decode_matches([field|acc], rest) + end + + defp encode_matches(acc, []), do: acc + defp encode_matches(acc, [field|rest]) do + header_bin = Openflow.Match.codec_header(field) + encode_matches(<>, rest) + end +end diff --git a/lib/openflow/multipart/table_features/reply.ex b/lib/openflow/multipart/table_features/reply.ex new file mode 100644 index 0000000..0d3c834 --- /dev/null +++ b/lib/openflow/multipart/table_features/reply.ex @@ -0,0 +1,41 @@ +defmodule Openflow.Multipart.TableFeatures.Reply do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: nil, + flags: [], + tables: [] + ) + + alias __MODULE__ + alias Openflow.Multipart.TableFeatures.Body + + def ofp_type, do: 18 + + def new(tables \\ []) do + %Reply{tables: tables} + end + + def read(<>) do + tables = Body.read(tables_bin) + %Reply{tables: tables} + end + + def to_binary(msg) do + header_bin = Openflow.Multipart.Reply.header(msg) + %Reply{tables: tables} = msg + tables_bin = Openflow.Multipart.TableFeatures.Body.to_binary(tables) + <> + end + + def append_body(%Reply{tables: tables} = message, %Reply{flags: [:more], tables: continue}) do + %{message|tables: [continue|tables]} + end + def append_body(%Reply{tables: tables} = message, %Reply{flags: [], tables: continue}) do + new_tables = [continue|tables] + |> Enum.reverse + |> List.flatten + %{message|tables: new_tables} + end +end diff --git a/lib/openflow/multipart/table_features/request.ex b/lib/openflow/multipart/table_features/request.ex new file mode 100644 index 0000000..3259c5d --- /dev/null +++ b/lib/openflow/multipart/table_features/request.ex @@ -0,0 +1,41 @@ +defmodule Openflow.Multipart.TableFeatures.Request do + defstruct( + version: 4, + xid: 0, + datapath_id: nil, # virtual field + aux_id: nil, + flags: [], + tables: [] + ) + + alias __MODULE__ + alias Openflow.Multipart.TableFeatures.Body + + def ofp_type, do: 18 + + def new(tables \\ []) do + %Request{tables: tables} + end + + def read(<>) do + tables = Body.read(tables_bin) + %Request{tables: tables} + end + + def to_binary(msg) do + header_bin = Openflow.Multipart.Request.header(msg) + %Request{tables: tables} = msg + tables_bin = Openflow.Multipart.TableFeatures.Body.to_binary(tables) + <> + end + + def append_body(%Request{tables: tables} = message, %Request{flags: [:more], tables: continue}) do + %{message|tables: [continue|tables]} + end + def append_body(%Request{tables: tables} = message, %Request{flags: [], tables: continue}) do + new_tables = [continue|tables] + |> Enum.reverse + |> List.flatten + %{message|tables: new_tables} + end +end diff --git a/test/flay.ex b/test/flay.ex index e37c239..f166928 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -9,7 +9,8 @@ defmodule Flay do datapath_id: nil, tester_pid: nil, conn_ref: nil, - reply_to: nil + reply_to: nil, + default_profile: nil, ] end @@ -19,6 +20,8 @@ defmodule Flay do def init(args) do state = init_controller(args) + TableFeatures.Request.new + |> send_message(state.datapath_id) init_bridge(state.datapath_id) {:ok, state} end @@ -48,6 +51,10 @@ defmodule Flay do send_flow_mod_delete(state.datapath_id, match: Match.new) {:noreply, state} end + def handle_cast(:restore_flow_profile, state) do + send_message(state.default_profile, state.datapath_id) + {:noreply, state} + end def handle_info(%ErrorMsg{} = error, state) do send(state.tester_pid, error) @@ -57,6 +64,9 @@ defmodule Flay do send(state.tester_pid, pktin) {:noreply, state} end + def handle_info(%TableFeatures.Reply{} = table, state) do + {:noreply, %{state|default_profile: table}} + end def handle_info(%PortDesc.Reply{} = desc, state) do GenServer.reply(state.reply_to, desc) {:noreply, %{state|reply_to: nil}} @@ -70,8 +80,8 @@ defmodule Flay do {:noreply, %{state|reply_to: nil}} end # `Catch all` function is required. - def handle_info(_info, state) do - # :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}") + def handle_info(info, state) do + :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}") {:noreply, state} end @@ -119,6 +129,54 @@ defmodule Flay do end defp init_bridge(datapath_id) do + tables = [ + TableFeatures.Body.new( + table_id: 0, + max_entries: 2000, + instructions: [ + Openflow.Instruction.ApplyActions, + Openflow.Instruction.GotoTable + ], + next_tables: [1], + apply_actions: [ + Openflow.Action.Output, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.SetField + ], + match: [ + :in_port, :eth_src, :eth_dst, :eth_type, :vlan_vid, + :ip_proto, :ipv4_src, :ipv4_dst, :tcp_dst,:udp_dst + ], + apply_setfield: [ + :eth_src, :eth_dst, :vlan_vid + ] + ), + TableFeatures.Body.new( + table_id: 0, + max_entries: 2000, + instructions: [ + Openflow.Instruction.ApplyActions + ], + next_tables: [], + apply_actions: [ + Openflow.Action.Output, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.SetField + ], + match: [ + :in_port, :eth_src, :eth_dst, :eth_type, :vlan_vid, + :ip_proto, :ipv4_src, :ipv4_dst, :tcp_dst,:udp_dst + ], + apply_setfield: [ + :eth_src, :eth_dst, :vlan_vid, :ipv4_src, :ipv4_dst, + :arp_spa, :arp_tpa, :arp_tha + ] + ), + ] + TableFeatures.Request.new(tables) + |> send_message(datapath_id) send_flow_mod_delete(datapath_id, table_id: :all) end end diff --git a/test/flog_test.exs b/test/flog_test.exs index 096a226..656b184 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -35,7 +35,7 @@ defmodule FlogTest do Code.load_file("test/pf.ex") # GIVEN - setup do + setup_all do setup_applications() wait_for_connected() ports = get_ports_desc() @@ -53,8 +53,7 @@ defmodule FlogTest do ] on_exit fn -> - print_flows() - GenServer.cast(Flay, :flow_del) + GenServer.cast(Flay, :restore_flow_profile) end {:ok, options} end From 5762231816f510c243938d377f5d0ded35dc06db Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Wed, 22 Nov 2017 12:26:40 +0900 Subject: [PATCH 11/50] Fix to secure_channel send_message more safer --- lib/tres/secure_channel.ex | 25 +++++++++++++++-- lib/tres/secure_channel_state.ex | 3 ++- test/flay.ex | 46 +++----------------------------- 3 files changed, 29 insertions(+), 45 deletions(-) diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 48fd016..31d0852 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -4,6 +4,7 @@ defmodule Tres.SecureChannel do import Logger alias :tres_xact_kv, as: XACT_KV + alias :queue, as: Queue alias Tres.SecureChannelState alias Tres.SwitchRegistry alias Tres.MessageHandlerSup @@ -171,7 +172,14 @@ defmodule Tres.SecureChannel do XACT_KV.delete(state_data.xact_kv_ref, message.xid) end end - :keep_state_and_data + {next_actions, action_queue} = + case Queue.out(state_data.action_queue) do + {:empty, action_queue} -> + {[], action_queue} + {{:value, next_action}, action_queue} -> + {[{:next_event, :internal, next_action}], action_queue} + end + {:keep_state, %{state_data|action_queue: action_queue}, next_actions} end defp handle_CONNECTED(:internal, {:openflow, message}, state_data) do %SecureChannelState{datapath_id: dpid, aux_id: aux_id} = state_data @@ -181,7 +189,7 @@ defmodule Tres.SecureChannel do |> handle_message(new_message, state_data) :keep_state_and_data end - defp handle_CONNECTED(:cast, {:send_message, message}, state_data) do + defp handle_CONNECTED(:internal, {:send_message, message}, state_data) do xid = SecureChannelState.increment_transaction_id(state_data.xid) messages = [ %{message|xid: xid}, @@ -191,6 +199,19 @@ defmodule Tres.SecureChannel do send_message(messages, state_data) :keep_state_and_data end + defp handle_CONNECTED(:cast, {:send_message, message} = action, state_data) do + if Queue.is_empty(state_data.action_queue) do + xid = SecureChannelState.increment_transaction_id(state_data.xid) + messages = [ + %{message|xid: xid}, + %{Openflow.Barrier.Request.new|xid: xid} + ] + XACT_KV.insert(state_data.xact_kv_ref, xid, message) + send_message(messages, state_data) + end + action_queue = Queue.in(action, state_data.action_queue) + {:keep_state, %{state_data|action_queue: action_queue}} + end defp handle_message(_in_xact = true, message, state_data) do case XACT_KV.get(state_data.xact_kv_ref, message.xid) do diff --git a/lib/tres/secure_channel_state.ex b/lib/tres/secure_channel_state.ex index e3f8873..71f04ff 100644 --- a/lib/tres/secure_channel_state.ex +++ b/lib/tres/secure_channel_state.ex @@ -17,7 +17,8 @@ defmodule Tres.SecureChannelState do ping_timer_ref: nil, ping_fail_count: 0, last_received: 0, - xact_kv_ref: nil + xact_kv_ref: nil, + action_queue: :queue.new ) alias __MODULE__ diff --git a/test/flay.ex b/test/flay.ex index f166928..65ab055 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -132,48 +132,10 @@ defmodule Flay do tables = [ TableFeatures.Body.new( table_id: 0, - max_entries: 2000, - instructions: [ - Openflow.Instruction.ApplyActions, - Openflow.Instruction.GotoTable - ], - next_tables: [1], - apply_actions: [ - Openflow.Action.Output, - Openflow.Action.PushVlan, - Openflow.Action.PopVlan, - Openflow.Action.SetField - ], - match: [ - :in_port, :eth_src, :eth_dst, :eth_type, :vlan_vid, - :ip_proto, :ipv4_src, :ipv4_dst, :tcp_dst,:udp_dst - ], - apply_setfield: [ - :eth_src, :eth_dst, :vlan_vid - ] - ), - TableFeatures.Body.new( - table_id: 0, - max_entries: 2000, - instructions: [ - Openflow.Instruction.ApplyActions - ], - next_tables: [], - apply_actions: [ - Openflow.Action.Output, - Openflow.Action.PushVlan, - Openflow.Action.PopVlan, - Openflow.Action.SetField - ], - match: [ - :in_port, :eth_src, :eth_dst, :eth_type, :vlan_vid, - :ip_proto, :ipv4_src, :ipv4_dst, :tcp_dst,:udp_dst - ], - apply_setfield: [ - :eth_src, :eth_dst, :vlan_vid, :ipv4_src, :ipv4_dst, - :arp_spa, :arp_tpa, :arp_tha - ] - ), + name: "Custom L2 Src", + max_entries: 8192, + config: [:table_miss_mask] + ) ] TableFeatures.Request.new(tables) |> send_message(datapath_id) From ab21f6e2408db91105c978e7100bee1751926dd8 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Wed, 22 Nov 2017 15:55:04 +0900 Subject: [PATCH 12/50] Table Features are supported --- lib/openflow/match.ex | 14 +++---- test/flay.ex | 87 +++++++++++++++++++++++++++++++++++++++++-- test/flog_test.exs | 6 +-- 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/lib/openflow/match.ex b/lib/openflow/match.ex index 0bd54a7..0ca8f98 100644 --- a/lib/openflow/match.ex +++ b/lib/openflow/match.ex @@ -32,28 +32,28 @@ defmodule Openflow.Match do <> end - def codec_header(oxm_field) when is_atom(oxm_field) do - oxm_field = case has_mask(oxm_field) do + def codec_header(oxm_field0) when is_atom(oxm_field0) do + oxm_field = case has_mask(oxm_field0) do 1 -> - string = to_string(oxm_field) + string = to_string(oxm_field0) "masked_" <> field = string String.to_atom(field) 0 -> - oxm_field + oxm_field0 end 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) - has_mask = has_mask(oxm_field) + has_mask = has_mask(oxm_field0) <> experimenter when experimenter in [:nicira_ext_match, :onf_ext_match, :hp_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) - has_mask = has_mask(oxm_field) + has_mask = has_mask(oxm_field0) <> end end @@ -179,7 +179,7 @@ defmodule Openflow.Match do %{class: match_class, field: field_name, has_mask: false, value: value_bin} end - defp has_mask(oxm_field) when is_atom(oxm_field) do + def has_mask(oxm_field) when is_atom(oxm_field) do has_mask? = oxm_field |> to_string diff --git a/test/flay.ex b/test/flay.ex index 65ab055..a8af07f 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -132,9 +132,90 @@ defmodule Flay do tables = [ TableFeatures.Body.new( table_id: 0, - name: "Custom L2 Src", - max_entries: 8192, - config: [:table_miss_mask] + name: "classifier", + max_entries: 50, + config: [:table_miss_mask], + match: [ + :in_port, + :eth_type, + :eth_src, + :masked_eth_dst, + :ip_proto, + :vlan_vid, + :ipv4_src, + :udp_dst, + :tcp_dst + ], + wildcards: [ + :in_port, + :eth_src + :eth_type, + :masked_eth_dst, + :vlan_vid, + :ip_proto, + :ipv4_src, + :udp_dst, + :tcp_dst, + ], + instructions: [ + Openflow.Instruction.GotoTable, + Openflow.Instruction.ApplyActions + ], + apply_actions: [ + Openflow.Action.Output, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.SetField + ], + apply_setfield: [ + :eth_dst, + :vlan_vid + ], + next_tables: [ + 1, + ], + ), + TableFeatures.Body.new( + table_id: 1, + name: "admission_control", + max_entries: 50, + config: [:table_miss_mask], + match: [ + :in_port, + :eth_type, + :eth_src, + :vlan_vid, + :masked_eth_dst, + :ip_proto, + :udp_dst, + :tcp_dst, + ], + wildcards: [ + :in_port, + :eth_type, + :eth_src, + :masked_eth_dst, + :vlan_vid, + :ip_proto, + :udp_dst, + :tcp_dst, + ], + instructions: [ + Openflow.Instruction.GotoTable, + Openflow.Instruction.ApplyActions + ], + apply_actions: [ + Openflow.Action.Output, + Openflow.Action.PushVlan, + Openflow.Action.PopVlan, + Openflow.Action.SetField + ], + apply_setfield: [ + :eth_dst, + :vlan_vid, + :ipv4_src, + :ipv4_dst + ], ) ] TableFeatures.Request.new(tables) diff --git a/test/flog_test.exs b/test/flog_test.exs index 656b184..c833a6a 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -2,9 +2,9 @@ defmodule FlogTest do use ExUnit.Case, async: false use Bitwise - @vlan_trunk_port "veth0" # FIXME: - @access_port "veth3" # FIXME: - @vxlan_port "veth4" # FIXME: + @vlan_trunk_port "1" # FIXME: + @access_port "2" # FIXME: + @vxlan_port "5" # FIXME: @bootnet_vid 0x1000 ||| 5 @user_vid 0x1000 ||| 123 @vlan_present {0x1000, 0x1000} From f2768496c823e82f4af600a2c6434176393ef001 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 24 Nov 2017 10:50:35 +0900 Subject: [PATCH 13/50] Add simple LearningSwitch Example --- examples/learning_switch/.gitignore | 20 +++ examples/learning_switch/README.md | 21 +++ examples/learning_switch/config/config.exs | 17 +++ .../learning_switch/lib/learning_switch.ex | 18 +++ .../lib/learning_switch/application.ex | 20 +++ .../lib/learning_switch/fdb.ex | 66 +++++++++ .../lib/learning_switch/ofctl.ex | 138 ++++++++++++++++++ examples/learning_switch/mix.exs | 23 +++ examples/learning_switch/mix.lock | 2 + .../test/learning_switch_test.exs | 8 + examples/learning_switch/test/test_helper.exs | 1 + 11 files changed, 334 insertions(+) create mode 100644 examples/learning_switch/.gitignore create mode 100644 examples/learning_switch/README.md create mode 100644 examples/learning_switch/config/config.exs create mode 100644 examples/learning_switch/lib/learning_switch.ex create mode 100644 examples/learning_switch/lib/learning_switch/application.ex create mode 100644 examples/learning_switch/lib/learning_switch/fdb.ex create mode 100644 examples/learning_switch/lib/learning_switch/ofctl.ex create mode 100644 examples/learning_switch/mix.exs create mode 100644 examples/learning_switch/mix.lock create mode 100644 examples/learning_switch/test/learning_switch_test.exs create mode 100644 examples/learning_switch/test/test_helper.exs diff --git a/examples/learning_switch/.gitignore b/examples/learning_switch/.gitignore new file mode 100644 index 0000000..12179ea --- /dev/null +++ b/examples/learning_switch/.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/examples/learning_switch/README.md b/examples/learning_switch/README.md new file mode 100644 index 0000000..ca03145 --- /dev/null +++ b/examples/learning_switch/README.md @@ -0,0 +1,21 @@ +# LearningSwitch + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `learning_switch` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:learning_switch, "~> 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/learning_switch](https://hexdocs.pm/learning_switch). + diff --git a/examples/learning_switch/config/config.exs b/examples/learning_switch/config/config.exs new file mode 100644 index 0000000..a9393b7 --- /dev/null +++ b/examples/learning_switch/config/config.exs @@ -0,0 +1,17 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +config :tres, + protocol: :tcp, + port: 6653, + max_connections: 10, + num_acceptors: 10, + callback_module: LearningSwitch.Ofctl, + callback_args: [] + +config :logger, + level: :debug, + format: "$date $time [$level] $metadata$message\n", + metadata: [:application], + handle_otp_reports: true diff --git a/examples/learning_switch/lib/learning_switch.ex b/examples/learning_switch/lib/learning_switch.ex new file mode 100644 index 0000000..855e031 --- /dev/null +++ b/examples/learning_switch/lib/learning_switch.ex @@ -0,0 +1,18 @@ +defmodule LearningSwitch do + @moduledoc """ + Documentation for LearningSwitch. + """ + + @doc """ + Hello world. + + ## Examples + + iex> LearningSwitch.hello + :world + + """ + def hello do + :world + end +end diff --git a/examples/learning_switch/lib/learning_switch/application.ex b/examples/learning_switch/lib/learning_switch/application.ex new file mode 100644 index 0000000..fa4dab3 --- /dev/null +++ b/examples/learning_switch/lib/learning_switch/application.ex @@ -0,0 +1,20 @@ +defmodule LearningSwitch.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + def start(_type, _args) do + # List all child processes to be supervised + children = [ + # Starts a worker by calling: LearningSwitch.Worker.start_link(arg) + # {LearningSwitch.Worker, arg}, + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: LearningSwitch.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/examples/learning_switch/lib/learning_switch/fdb.ex b/examples/learning_switch/lib/learning_switch/fdb.ex new file mode 100644 index 0000000..dcc46e7 --- /dev/null +++ b/examples/learning_switch/lib/learning_switch/fdb.ex @@ -0,0 +1,66 @@ +defmodule LearningSwitch.FDB do + use Agent + + defmodule Entry do + defstruct [ + mac: nil, + port_no: nil, + age_max: 0, + last_update: 0 + ] + + def new(mac, port_no, age_max \\ 180) do + %Entry{ + mac: mac, + port_no: port_no, + age_max: age_max, + last_update: :os.timestamp + } + end + + def update(self, port_no) do + %{self|port_no: port_no, last_update: :os.timestamp} + end + + def aged_out?(self) do + :timer.now_diff(:os.timestamp, self.last_update) > (1000 * self.age_max) + end + end + + def start_link do + Agent.start_link(&Map.new/0) + end + + def lookup(self, mac) do + entry = Agent.get(self, &Map.get(&1, mac)) + entry && entry.port_no + end + + def learn(self, mac, port_no) do + entry = Agent.get(self, &Map.get(&1, mac)) + entry = if entry do + Entry.update(entry, port_no) + else + Entry.new(mac, port_no) + end + Agent.update(self, &Map.put(&1, mac, entry)) + end + + def age(self) do + mac_addrs = Agent.get(self, &find_aged_entries(&1)) + for mac <- mac_addrs do + Agent.update(self, &Map.delete(&1, mac)) + end + end + + # private function + + defp find_aged_entries(map) do + Enum.flat_map(map, fn({mac, entry}) -> + case Entry.aged_out?(entry) do + true -> [mac] + false -> [] + end + end) + end +end diff --git a/examples/learning_switch/lib/learning_switch/ofctl.ex b/examples/learning_switch/lib/learning_switch/ofctl.ex new file mode 100644 index 0000000..db1714c --- /dev/null +++ b/examples/learning_switch/lib/learning_switch/ofctl.ex @@ -0,0 +1,138 @@ +defmodule LearningSwitch.Ofctl do + use GenServer + use Tres.Controller + + import Logger + + alias LearningSwitch.FDB + + @ingress_filtering_table_id 0 + @forwarding_table_id 1 + + @aging_time 180 + + @mcast {"010000000000", "110000000000"} + @bcast "ffffffffffff" + @ipv6_mcast {"333300000000", "ffff00000000"} + + defmodule State do + defstruct [ + datapath_id: nil, + conn_ref: nil, + fdb_pid: nil + ] + end + + def start_link(datapath_id, args) do + GenServer.start_link(__MODULE__, [datapath_id, args]) + end + + def init([datapath_id, _args]) do + :ok = debug("Switch Ready: datapath_id: #{inspect(datapath_id)}") + conn_ref = SwitchRegistry.monitor(datapath_id) + {:ok, pid} = FDB.start_link + init_datapath(datapath_id) + state = %State{ + datapath_id: datapath_id, + conn_ref: conn_ref, + fdb_pid: pid + } + {:ok, state} + end + + def handle_info(%PacketIn{} = packet_in, state) do + <<_dhost::6-bytes, shost::6-bytes, _rest::bytes>> = packet_in.data + eth_src = Openflow.Utils.to_hex_string(shost) + FDB.learn(state.fdb_pid, eth_src, packet_in.in_port) + add_forwarding_flow_and_packet_out(packet_in, state) + :ok = debug("PacketIn: eth_src: #{eth_src} datapath_id: #{inspect(state.datapath_id)}") + {:noreply, state} + end + def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do + :ok = debug("Switch Disconnected: datapath_id: #{inspect(state.datapath_id)}") + {:stop, :normal, state} + end + def handle_info(info, state) do + :ok = warn("Unhandled message #{inspect(info)}: #{inspect(state.datapath_id)}") + {:noreply, state} + end + + # private functions + + defp init_datapath(datapath_id) do + init_flow_tables(datapath_id) + end + + defp init_flow_tables(datapath_id) do + for flow_options <- [ + add_default_broadcast_flow_entry(), + add_default_flooding_flow_entry(), + add_multicast_mac_drop_flow_entry(), + add_ipv6_multicast_mac_drop_flow_entry(), + add_default_forwarding_flow_entry()] do + send_flow_mod_add(datapath_id, flow_options) + end + end + + defp add_forwarding_flow_and_packet_out(packet_in, state) do + <> = packet_in.data + eth_dst = Openflow.Utils.to_hex_string(dhost) + port_no = FDB.lookup(state.fdb_pid, eth_dst) + add_forwarding_flow_entry(packet_in, port_no) + packet_out(packet_in, port_no || :flood) + end + + defp packet_out(%PacketIn{datapath_id: datapath_id, data: data}, port_no) do + send_packet_out( + datapath_id, + data: data, + actions: [Output.new(port_no)] + ) + end + + defp add_forwarding_flow_entry(_packet_in, nil), do: :noop + defp add_forwarding_flow_entry(%PacketIn{datapath_id: datapath_id, data: data} = packet_in, port_no) do + <> = data + send_flow_mod_add( + datapath_id, + idle_timeout: @aging_time, + priority: 2, + match: Match.new( + in_port: packet_in.in_port, + eth_dst: Openflow.Utils.to_hex_string(dhost), + eth_src: Openflow.Utils.to_hex_string(shost)), + instructions: [ApplyActions.new(Output.new(port_no))] + ) + end + + defp add_default_broadcast_flow_entry do + [table_id: @forwarding_table_id, + priority: 3, + match: Match.new(eth_dst: @bcast), + instructions: [ApplyActions.new(Output.new(:flood))]] + end + + defp add_default_flooding_flow_entry do + [table_id: @forwarding_table_id, + priority: 1, + instructions: [ApplyActions.new(Output.new(:controller))]] + end + + defp add_multicast_mac_drop_flow_entry do + [table_id: @ingress_filtering_table_id, + priority: 2, + match: Match.new(eth_dst: @mcast)] + end + + defp add_ipv6_multicast_mac_drop_flow_entry do + [table_id: @ingress_filtering_table_id, + priority: 2, + match: Match.new(eth_dst: @ipv6_mcast)] + end + + defp add_default_forwarding_flow_entry do + [table_id: @ingress_filtering_table_id, + priority: 1, + instructions: [GotoTable.new(@forwarding_table_id)]] + end +end diff --git a/examples/learning_switch/mix.exs b/examples/learning_switch/mix.exs new file mode 100644 index 0000000..97e36d2 --- /dev/null +++ b/examples/learning_switch/mix.exs @@ -0,0 +1,23 @@ +defmodule LearningSwitch.Mixfile do + use Mix.Project + + def project do + [ + app: :learning_switch, + 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, :tres], + mod: {LearningSwitch.Application, []}] + end + + defp deps do + [{:tres, path: "../../../tres"}] + end +end diff --git a/examples/learning_switch/mix.lock b/examples/learning_switch/mix.lock new file mode 100644 index 0000000..9380bbc --- /dev/null +++ b/examples/learning_switch/mix.lock @@ -0,0 +1,2 @@ +%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, + "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"}} diff --git a/examples/learning_switch/test/learning_switch_test.exs b/examples/learning_switch/test/learning_switch_test.exs new file mode 100644 index 0000000..095aaad --- /dev/null +++ b/examples/learning_switch/test/learning_switch_test.exs @@ -0,0 +1,8 @@ +defmodule LearningSwitchTest do + use ExUnit.Case + doctest LearningSwitch + + test "greets the world" do + assert LearningSwitch.hello() == :world + end +end diff --git a/examples/learning_switch/test/test_helper.exs b/examples/learning_switch/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/examples/learning_switch/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() From d454d7124fcddb511131d672b0e671300b9ece20 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sun, 26 Nov 2017 18:30:52 +0900 Subject: [PATCH 14/50] Refactored SecureChannel --- .../lib/learning_switch/ofctl.ex | 2 + lib/tres/secure_channel.ex | 169 ++++++++++-------- 2 files changed, 97 insertions(+), 74 deletions(-) diff --git a/examples/learning_switch/lib/learning_switch/ofctl.ex b/examples/learning_switch/lib/learning_switch/ofctl.ex index db1714c..19c6121 100644 --- a/examples/learning_switch/lib/learning_switch/ofctl.ex +++ b/examples/learning_switch/lib/learning_switch/ofctl.ex @@ -60,6 +60,8 @@ defmodule LearningSwitch.Ofctl do # private functions defp init_datapath(datapath_id) do + SetConfig.new(miss_send_len: :no_buffer) + |> send_message(datapath_id) init_flow_tables(datapath_id) end diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 31d0852..dbd7c41 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -5,7 +5,7 @@ defmodule Tres.SecureChannel do alias :tres_xact_kv, as: XACT_KV alias :queue, as: Queue - alias Tres.SecureChannelState + alias Tres.SecureChannelState, as: State alias Tres.SwitchRegistry alias Tres.MessageHandlerSup @@ -43,16 +43,16 @@ defmodule Tres.SecureChannel do # TCP handler def handle_event(:info, {:tcp, socket, packet}, state, - %SecureChannelState{socket: socket, transport: transport} = state_data) do + %State{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 + %State{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 + %State{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 @@ -74,7 +74,8 @@ defmodule Tres.SecureChannel do handle_WATING(type, message, state_data) end - def terminate(reason, state, %SecureChannelState{datapath_id: datapath_id, aux_id: aux_id, xact_kv_ref: kv_ref}) do + def terminate(reason, state, + %State{datapath_id: datapath_id, aux_id: aux_id, xact_kv_ref: kv_ref}) do warn("[#{__MODULE__}] termiate: #{inspect(reason)} state = #{inspect(state)}") true = XACT_KV.drop(kv_ref) :ok = SwitchRegistry.unregister({datapath_id, aux_id}) @@ -86,7 +87,7 @@ defmodule Tres.SecureChannel do init_process(ref) :ok = transport.setopts(socket, [active: :once]) kv_ref = XACT_KV.create - SecureChannelState.new(ref: ref, socket: socket, transport: transport, xact_kv_ref: kv_ref) + State.new(ref: ref, socket: socket, transport: transport, xact_kv_ref: kv_ref) end defp init_process(ref) do @@ -96,7 +97,7 @@ defmodule Tres.SecureChannel do end defp init_handler(state_data) do - %SecureChannelState{datapath_id: dpid, aux_id: aux_id} = state_data + %State{datapath_id: dpid, aux_id: aux_id} = state_data {:ok, pid} = MessageHandlerSup.start_child({dpid, aux_id}) ref = Process.monitor(pid) %{state_data|handler_pid: pid, handler_ref: ref} @@ -104,7 +105,8 @@ defmodule Tres.SecureChannel do # INIT state defp handle_INIT(:enter, _old_state, state_data) do - debug("[#{__MODULE__}] Initiate HELLO handshake: #{state_data.ip_addr}:#{state_data.port}") + 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 @@ -114,13 +116,15 @@ defmodule Tres.SecureChannel 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)}") + 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}") + 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 @@ -131,12 +135,15 @@ defmodule Tres.SecureChannel do close_connection(:features_handshake_timeout, state_data) end defp handle_CONNECTING(:internal, {:openflow, %Openflow.Features.Reply{} = features}, state_data) do - debug("[#{__MODULE__}] Switch connected datapath_id: #{features.datapath_id} auxiliary_id: #{features.aux_id}") + debug("[#{__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__)}") + 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 @@ -158,31 +165,19 @@ defmodule Tres.SecureChannel 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 + %State{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, %Openflow.Barrier.Reply{xid: xid}}, state_data) do - for {:xact_entry, _xid, message, _orig} <- XACT_KV.get(state_data.xact_kv_ref, xid) do - unless is_nil(message) do - send(state_data.handler_pid, message) - XACT_KV.delete(state_data.xact_kv_ref, message.xid) - end - end - {next_actions, action_queue} = - case Queue.out(state_data.action_queue) do - {:empty, action_queue} -> - {[], action_queue} - {{:value, next_action}, action_queue} -> - {[{:next_event, :internal, next_action}], action_queue} - end - {:keep_state, %{state_data|action_queue: action_queue}, next_actions} + defp handle_CONNECTED(:internal, {:openflow, %Openflow.Barrier.Reply{} = barrier}, state_data) do + {new_state_data, next_actions} = process_xact(barrier, state_data) + {:keep_state, new_state_data, next_actions} end defp handle_CONNECTED(:internal, {:openflow, message}, state_data) do - %SecureChannelState{datapath_id: dpid, aux_id: aux_id} = state_data + %State{datapath_id: dpid, aux_id: aux_id} = state_data new_message = %{message|datapath_id: dpid, aux_id: aux_id} state_data.xact_kv_ref |> XACT_KV.is_exists(message.xid) @@ -190,46 +185,20 @@ defmodule Tres.SecureChannel do :keep_state_and_data end defp handle_CONNECTED(:internal, {:send_message, message}, state_data) do - xid = SecureChannelState.increment_transaction_id(state_data.xid) - messages = [ - %{message|xid: xid}, - %{Openflow.Barrier.Request.new|xid: xid} - ] - XACT_KV.insert(state_data.xact_kv_ref, xid, message) - send_message(messages, state_data) + xactional_send_message(message, state_data) :keep_state_and_data end defp handle_CONNECTED(:cast, {:send_message, message} = action, state_data) do - if Queue.is_empty(state_data.action_queue) do - xid = SecureChannelState.increment_transaction_id(state_data.xid) - messages = [ - %{message|xid: xid}, - %{Openflow.Barrier.Request.new|xid: xid} - ] - XACT_KV.insert(state_data.xact_kv_ref, xid, message) - send_message(messages, state_data) - end + if Queue.is_empty(state_data.action_queue), + do: xactional_send_message(message, state_data) action_queue = Queue.in(action, state_data.action_queue) {:keep_state, %{state_data|action_queue: action_queue}} end - defp handle_message(_in_xact = true, message, state_data) do - case XACT_KV.get(state_data.xact_kv_ref, message.xid) do - [{:xact_entry, _xid, prev_message, _orig}|_] -> - new_message = Openflow.append_body(prev_message, message) - XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message) - _ -> - XACT_KV.delete(state_data.xact_kv_ref, message.xid) - end - end - defp handle_message(_in_xact = false, message, state_data) do - send(state_data.handler_pid, message) - end - # WATING state defp handle_WATING(:enter, :CONNECTING, state_data) do warn("[#{__MODULE__}] Possible HANG Detected on datapath_id: #{state_data.datapath_id} !") - %SecureChannelState{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data + %State{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data send(handler_pid, {:switch_hang, {dpid, aux_id}}) start_periodic_idle_check() :keep_state_and_data @@ -243,7 +212,7 @@ defmodule Tres.SecureChannel do handle_ping_timeout(state_data, :WAITING) end defp handle_WATING(:internal, {:openflow, message}, state_data) do - %SecureChannelState{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data + %State{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data send(handler_pid, %{message|datapath_id: dpid, aux_id: aux_id}) {:next_state, :CONNECTING, state_data} end @@ -256,11 +225,12 @@ defmodule Tres.SecureChannel do 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 + defp handle_packet(packet, %State{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}") + 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]) @@ -271,6 +241,41 @@ defmodule Tres.SecureChannel do end end + defp handle_message(_in_xact = true, message, state_data) do + case XACT_KV.get(state_data.xact_kv_ref, message.xid) do + [{:xact_entry, _xid, prev_message, _orig}|_] -> + new_message = Openflow.append_body(prev_message, message) + XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message) + _ -> + XACT_KV.delete(state_data.xact_kv_ref, message.xid) + end + end + defp handle_message(_in_xact = false, message, state_data), + do: send(state_data.handler_pid, message) + + defp process_xact(%Openflow.Barrier.Reply{xid: xid}, state_data) do + :ok = state_data.xact_kv_ref + |> XACT_KV.get(xid) + |> Enum.each(&process_xact_entry(&1, state_data)) + pop_action_queue(state_data) + end + + defp process_xact_entry({:xact_entry, xid, message, _orig}, state_data) do + unless is_nil(message), do: send(state_data.handler_pid, message) + XACT_KV.delete(state_data.xact_kv_ref, xid) + end + + defp pop_action_queue(%State{action_queue: queue} = state_data) do + {next_actions, new_queue} = + case Queue.out(queue) do + {:empty, action_queue} -> + {[], action_queue} + {{:value, next_action}, action_queue} -> + {[{:next_event, :internal, next_action}], action_queue} + end + {%{state_data|action_queue: new_queue}, next_actions} + end + defp initiate_hello_handshake(state_data) do send_hello(state_data) ref = :erlang.send_after(@hello_handshake_timeout, self(), :hello_timeout) @@ -279,7 +284,7 @@ defmodule Tres.SecureChannel do 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) + State.set_transaction_id(state_data.xid, hello.xid) if Openflow.Hello.supported_version?(hello) do {:next_state, :CONNECTING, %{state_data|timer_ref: nil}} else @@ -288,13 +293,14 @@ defmodule Tres.SecureChannel do end defp initiate_features_handshake(state_data) do - new_xid = SecureChannelState.increment_transaction_id(state_data.xid) + new_xid = State.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 + 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| @@ -343,25 +349,25 @@ defmodule Tres.SecureChannel do end end - defp should_be_ping?(%SecureChannelState{last_received: last_received, aux_id: 0}) do + defp should_be_ping?(%State{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) + defp send_ping(%State{xid: x_agent} = state_data) do + xid = State.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) + defp handle_ping_timeout(%State{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) + defp handle_ping_timeout(%State{ping_fail_count: fail_count} = state_data, :WAITING) when fail_count > @ping_fail_max_count do close_connection(:ping_failed, state_data) end @@ -374,8 +380,23 @@ defmodule Tres.SecureChannel 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})") + defp xactional_send_message(message, state_data) do + xid = State.increment_transaction_id(state_data.xid) + messages = [ + %{message|xid: xid}, + %{Openflow.Barrier.Request.new|xid: xid} + ] + XACT_KV.insert(state_data.xact_kv_ref, xid, message) + send_message(messages, state_data) + end + + defp send_message(message, %State{socket: socket, transport: transport}) do + if is_list(message) do + for message <- message, + do: debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + else + debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + end Tres.Utils.send_message(message, socket, transport) end @@ -386,11 +407,11 @@ defmodule Tres.SecureChannel do end defp handle_signal({:'DOWN', mon_ref, :process, _main_pid, reason}, - %SecureChannelState{main_monitor_ref: mon_ref} = state_data) do + %State{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 + %State{handler_ref: handler_ref} = state_data) do close_connection({:handler_down, reason}, state_data) end defp handle_signal({:'EXIT', _pid, reason}, state_data) do From 82b24f54110788a5e6d7128dc81932598ff74617 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sun, 26 Nov 2017 19:11:24 +0900 Subject: [PATCH 15/50] Add dependencies --- mix.exs | 3 ++- mix.lock | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index c6707a5..069e374 100644 --- a/mix.exs +++ b/mix.exs @@ -13,13 +13,14 @@ defmodule Tres.Mixfile do # Run "mix help compile.app" to learn about applications. def application do - [extra_applications: [:logger, :ranch], + [extra_applications: [:logger, :ranch, :eovsdb], mod: {Tres.Application, []}] end # Run "mix help deps" to learn about dependencies. defp deps do [{:ranch, "~> 1.4.0"}, + {:eovsdb, github: "shun159/eovsdb", branch: "master"}, {:binpp, github: "jtendo/binpp", branch: "master"}, {:pkt, github: "msantos/pkt", ref: "3afb196", only: :test, override: true}, {:epcap, github: "msantos/epcap", branch: "master", only: :test}] diff --git a/mix.lock b/mix.lock index c50c3ed..7fb1000 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,10 @@ %{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, + "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, "epcap": {:git, "https://github.com/msantos/epcap.git", "9566f0420a4dcf1292c1a1afd9339c35dbdfd041", [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"}, + "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, "pkt": {:git, "https://github.com/msantos/pkt.git", "3afb1967f34324c1dec5035a6e36232da815c2e6", [ref: "3afb196"]}, - "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"}} + "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"}, + "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}} From ae00e7b76d3ec78448bc1d6b59ee35a219f15923 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 27 Nov 2017 00:21:36 +0900 Subject: [PATCH 16/50] Implement an Open_vSwitch configuration module --- lib/ovsdb.ex | 22 ++++++ lib/ovsdb/open_vswitch.ex | 145 ++++++++++++++++++++++++++++++++++++++ lib/tres/application.ex | 3 +- 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 lib/ovsdb.ex create mode 100644 lib/ovsdb/open_vswitch.ex diff --git a/lib/ovsdb.ex b/lib/ovsdb.ex new file mode 100644 index 0000000..7a4c9d0 --- /dev/null +++ b/lib/ovsdb.ex @@ -0,0 +1,22 @@ +defmodule OVSDB do + @moduledoc false + + @behaviour :supervisor + + def start_link do + :supervisor.start_link({:local, __MODULE__}, __MODULE__, []) + end + + def init([]) do + child = OVSDB.OpenvSwitch + strategy = :simple_one_for_one + max_r = 1000 + intensity = 3600 + sup_flags = {strategy, max_r, intensity} + {:ok, {sup_flags, [{child, {child, :start_link, []}, :temporary, 1000, :worker, [child]}]}} + end + + def start_child(server) do + :supervisor.start_child(__MODULE__, [server]) + end +end diff --git a/lib/ovsdb/open_vswitch.ex b/lib/ovsdb/open_vswitch.ex new file mode 100644 index 0000000..67baaa9 --- /dev/null +++ b/lib/ovsdb/open_vswitch.ex @@ -0,0 +1,145 @@ +defmodule OVSDB.OpenvSwitch do + use GenServer + + defmodule State do + defstruct [ + server: nil, + client_pid: nil, + monitor_pid: nil, + ovs_uuid: nil + ] + end + + @database "Open_vSwitch" + + @open_vswitch "Open_vSwitch" + @interface "Interface" + @port "Port" + @controller "Controller" + @bridge "Bridge" + + def start_link(server) do + GenServer.start_link(__MODULE__, [server]) + end + + def find_by_name(pid, table, name) do + GenServer.call(pid, {:find_by_name, table, name}) + end + + def add_br(pid, bridge, options \\ []) do + br_uuids = GenServer.call(pid, {:sync_get, @open_vswitch, "bridges"}) + add_br_options = [ + bridge: bridge, + controller: options[:controller] || "tcp:127.0.0.1:6653", + protocol: options[:protocol] || "OpenFlow13", + fail_mode: options[:fail_mode] || "standalone", + br_uuids: br_uuids + ] + GenServer.call(pid, {:add_br, add_br_options}) + end + + def del_br(pid, bridge) do + case find_by_name(pid, @bridge, bridge) do + %{"_uuid" => uuid} -> + new_bridges = + case GenServer.call(pid, {:sync_get, @open_vswitch, "bridges"}) do + ["set", bridges] -> %{"bridges" => ["set", bridges -- [uuid]]} + ^uuid -> %{"bridges" => ["set", []]} + curr_bridges -> %{"bridges" => curr_bridges} + end + GenServer.call(pid, {:del_br, new_bridges}) + :not_found -> + {:error, :not_found} + end + end + + def init([server]) do + state = server + |> String.to_charlist + |> init_client + {:ok, state} + end + + def handle_call({:sync_get, table, col_name}, _from, state) do + [%{"rows" => [%{^col_name => values}|_]}] = + [col_name] + |> :eovsdb_op.select(table, []) + |> xact(state.client_pid) + {:reply, values, state} + end + def handle_call({:add_br, options}, _from, state) do + %State{client_pid: pid, ovs_uuid: ovs} = state + br_iface = %{name: options[:bridge], type: :internal} + br_port = %{name: options[:bridge], interfaces: ["named-uuid", "brinterface"]} + controller = %{target: options[:controller]} + new_bridge = %{ + name: options[:bridge], + ports: ["named-uuid", "brport"], + controller: ["named-uuid", "brcontroller"], + fail_mode: options[:fail_mode], + protocols: options[:protocol] + } + named_uuid = ["named-uuid", "bridge"] + new_bridges = + case options[:br_uuids] do + ["set", []] -> %{bridges: named_uuid} + ["set", bridges] -> %{bridges: ["set", bridges ++ [named_uuid]]} + ["uuid", _] = bridge -> %{bridges: ["set", [bridge] ++ [named_uuid]]} + end + next_config = [{"next_cfg", "+=", 1}] + eq_ovs_uuid = [{"_uuid", "==", ovs}] + replies = xact([ + :eovsdb_op.insert(@interface, br_iface, "brinterface"), + :eovsdb_op.insert(@port, br_port, "brport"), + :eovsdb_op.insert(@controller, controller, "brcontroller"), + :eovsdb_op.insert(@bridge, new_bridge, "bridge"), + :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), + :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) + ], pid) + {:reply, replies, state} + end + def handle_call({:del_br, new_bridges}, _from, state) do + %State{client_pid: pid, ovs_uuid: ovs} = state + eq_ovs_uuid = [{"_uuid", "==", ovs}] + next_config = [{"next_cfg", "+=", 1}] + replies = xact([ + :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), + :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) + ], pid) + {:reply, replies, state} + end + def handle_call({:find_by_name, table, name}, _from, state) do + %State{client_pid: pid} = state + query = :eovsdb_op.select('*', table, [{"name", "==", name}]) + reply = case xact(query, pid) do + [%{"rows" => []}] -> :not_found + [%{"rows" => [row]}] -> row + end + {:reply, reply, state} + end + + def handle_cast({:async_get, "_uuid"}, state) do + [%{"rows" => [%{"_uuid" => values}|_]}] = + ["_uuid"] + |> :eovsdb_op.select(@open_vswitch, []) + |> xact(state.client_pid) + {:noreply, %{state|ovs_uuid: values}} + end + + # private functions + + defp init_client(server) do + {:ok, pid} = :eovsdb_client.connect(server, [database: @database]) + :eovsdb_client.regist_schema(pid) + :ok = GenServer.cast(self(), {:async_get, "_uuid"}) + %State{server: server, client_pid: pid} + end + + defp xact(query, pid) when is_list(query) do + {:ok, res} = :eovsdb_client.transaction(pid, query) + res + end + defp xact(query, pid) when is_map(query) do + xact([query], pid) + end +end diff --git a/lib/tres/application.ex b/lib/tres/application.ex index 3186d9d..5af2b67 100644 --- a/lib/tres/application.ex +++ b/lib/tres/application.ex @@ -10,7 +10,8 @@ defmodule Tres.Application do {cb_mod, _cb_args} = Tres.Utils.get_callback_module children = [worker(Registry, [[keys: :unique, name: SwitchRegistry]], id: SwitchRegistry), - supervisor(Tres.MessageHandlerSup, [cb_mod], id: MessageHandlerSup)] + supervisor(Tres.MessageHandlerSup, [cb_mod], id: MessageHandlerSup), + supervisor(OVSDB, [], id: OVSDB)] opts = [strategy: :one_for_one, name: Tres.Supervisor] {:ok, _connection_pid} = Tres.Utils.start_openflow_listener Supervisor.start_link(children, opts) From cc8adda6910cb0cc481ff1cdb7b32442275f5f90 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 27 Nov 2017 00:43:10 +0900 Subject: [PATCH 17/50] Fix .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 12179ea..674fc49 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ erl_crash.dump # Also ignore archive artifacts (built via "mix archive.build"). *.ez + +# Examples +*/polaris/* From adba03927fa9ba6abfbafe1c1e71d8fed32113b2 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 27 Nov 2017 11:01:08 +0900 Subject: [PATCH 18/50] test: fix a bug which crashes when received table_features_reply that broken from openvswitch --- test/flay.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/flay.ex b/test/flay.ex index a8af07f..ffda1f3 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -20,8 +20,6 @@ defmodule Flay do def init(args) do state = init_controller(args) - TableFeatures.Request.new - |> send_message(state.datapath_id) init_bridge(state.datapath_id) {:ok, state} end @@ -148,7 +146,7 @@ defmodule Flay do ], wildcards: [ :in_port, - :eth_src + :eth_src, :eth_type, :masked_eth_dst, :vlan_vid, From 7b1f581642d47423b025924ea91ab6f77142a8a9 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 27 Nov 2017 13:41:46 +0900 Subject: [PATCH 19/50] test: fix to start eovsdb application --- test/flog_test.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/flog_test.exs b/test/flog_test.exs index c833a6a..669786f 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -624,6 +624,7 @@ defmodule FlogTest do Application.start(:pkt) Application.start(:epcap) Application.start(:ranch) + Application.start(:eovsdb) Application.start(:tres) end From ecfd278f2abc1329d81b336a4276842630a7fbc2 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 28 Nov 2017 18:06:57 +0900 Subject: [PATCH 20/50] Goto confession --- test/flay.ex | 17 +-- test/flog_test.exs | 216 ++++++++++++++++++++++++++--- test/packet_data/dhcp_discover.raw | Bin 0 -> 300 bytes test/pf.ex | 52 ++++--- test/test_helper.exs | 1 + 5 files changed, 239 insertions(+), 47 deletions(-) create mode 100644 test/packet_data/dhcp_discover.raw diff --git a/test/flay.ex b/test/flay.ex index ffda1f3..7016669 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -21,6 +21,7 @@ defmodule Flay do def init(args) do state = init_controller(args) init_bridge(state.datapath_id) + GenServer.cast(Flay, :desc_stats) {:ok, state} end @@ -28,15 +29,15 @@ defmodule Flay do send_message(PortDesc.Request.new, state.datapath_id) {:noreply, %{state|reply_to: from}} end - def handle_call(:desc_stats, from, state) do - send_message(Desc.Request.new, state.datapath_id) - {:noreply, %{state|reply_to: from}} - end def handle_call(:flow_stats, from, state) do send_message(Flow.Request.new, state.datapath_id) {:noreply, %{state|reply_to: from}} end + def handle_cast(:desc_stats, state) do + send_message(Desc.Request.new, state.datapath_id) + {:noreply, state} + end def handle_cast({:register_pid, tester_pid}, state) do {:noreply, %{state|tester_pid: tester_pid}} end @@ -49,10 +50,6 @@ defmodule Flay do send_flow_mod_delete(state.datapath_id, match: Match.new) {:noreply, state} end - def handle_cast(:restore_flow_profile, state) do - send_message(state.default_profile, state.datapath_id) - {:noreply, state} - end def handle_info(%ErrorMsg{} = error, state) do send(state.tester_pid, error) @@ -70,8 +67,8 @@ defmodule Flay do {:noreply, %{state|reply_to: nil}} end def handle_info(%Desc.Reply{} = desc, state) do - GenServer.reply(state.reply_to, desc) - {:noreply, %{state|reply_to: nil}} + IO.inspect(desc) + {:noreply, state} end def handle_info(%Flow.Reply{} = desc, state) do GenServer.reply(state.reply_to, desc) diff --git a/test/flog_test.exs b/test/flog_test.exs index 669786f..31db334 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -2,18 +2,27 @@ defmodule FlogTest do use ExUnit.Case, async: false use Bitwise - @vlan_trunk_port "1" # FIXME: - @access_port "2" # FIXME: - @vxlan_port "5" # FIXME: + @listen_port 6653 + + @vlan_trunk_port "veth0" # FIXME: + @access_port "veth3" # FIXME: + @vxlan_port "veth4" # FIXME: + + @vlan_trunk_port_sniffer "veth1" # FIXME: + @access_port_sniffer "veth2" # FIXME: + @vxlan_port_sniffer "veth5" # FIXME: + @bootnet_vid 0x1000 ||| 5 @user_vid 0x1000 ||| 123 @vlan_present {0x1000, 0x1000} @mcast {"010000000000", "010000000000"} @sdl_vmac "000000000001" @bcast "ffffffffffff" - @mac "010203040506" + @mac "000102030405" @auth_ipv4_address {192,168,5,4} @captive_ipv4_address {192,168,5,5} + @quad_0_ip {0,0,0,0} + @bcast_ip {255,255,255,255} @client_ip {192,168,5,10} @client_farm_ip {192,168,255,1} @gateway_ip {192,168,5,4} @@ -51,10 +60,6 @@ defmodule FlogTest do cookie: cookie, timeout: timeout ] - - on_exit fn -> - GenServer.cast(Flay, :restore_flow_profile) - end {:ok, options} end @@ -68,6 +73,26 @@ defmodule FlogTest do :ok = GenServer.cast(Flay, {:flow_install, options, self()}) refute_receive %Openflow.ErrorMsg{}, 1000 end + + test "Inject Packet(ARP)" do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + payload = <<0::size(16)-unit(8)>> + packet = [ + ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) + ] + Pf.inject!(pid1, packet, payload) + refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 1000 + refute_receive {@vxlan_port_sniffer, [^packet, ^payload]}, 1000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) + end end describe("switch:merged_handler:" <> @@ -77,7 +102,7 @@ defmodule FlogTest do match = Openflow.Match.new( in_port: state.vlan_trunk.number, eth_dst: @bcast, - vlan_vid: {0x0000, 0x1fff}, + vlan_vid: 0x0000, eth_type: 0x88cc ) action = Openflow.Action.Output.new(:controller) @@ -90,6 +115,28 @@ defmodule FlogTest do instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) refute_receive %Openflow.ErrorMsg{}, 1000 + + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) + + shost = Openflow.Match.Field.codec(state.vlan_trunk.hw_addr, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + packet = [ + ether(dhost: dhost, shost: shost, type: 0x88cc), + lldp(pdus: [chassis_id(value: "hogehoge"), + port_id(subtype: :mac_address, value: shost), + ttl(value: 128)]) + ] + Pf.inject!(pid2, packet) + in_port = state.vlan_trunk.number + assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ""]}, 1000 + refute_receive {@vxlan_port_sniffer, [^packet, ""]}, 1000 + refute_receive {@access_port_sniffer, [^packet, ""]}, 1000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) end end @@ -111,7 +158,7 @@ defmodule FlogTest do describe("switch:uplink_escalation_flow:" <> "table=0,priority=10,cookie=0x2000000000000000,arp,actions=controller") do - test "Install Flow" do + test "Install Flow", state do match = Openflow.Match.new(eth_type: 0x0806) action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) @@ -123,14 +170,34 @@ defmodule FlogTest do instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) refute_receive %Openflow.ErrorMsg{}, 1000 + + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + payload = <<0::size(16)-unit(8)>> + packet = [ + ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) + ] + Pf.inject!(pid1, packet, payload) + in_port = state.port.number + assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 1000 + refute_receive {@vxlan_port_sniffer, [^packet, ^payload]}, 1000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) end end describe("switch:uplink_escalation_flow:" <> "table=0,priority=10,cookie=0x2000000000000000,udp,udp_dst=67,actions=controller") do - test "Install Flow" do + test "Install Flow", state do match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) - action = Openflow.Action.Output.new(:controller) + action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) options = [cookie: 0x2000000000000000, @@ -140,6 +207,32 @@ defmodule FlogTest do instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) refute_receive %Openflow.ErrorMsg{}, 1000 + + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + + payload = File.read!("test/packet_data/dhcp_discover.raw") + length = 8 + byte_size(payload) + udp_header = udp(sport: 68, dport: 67, ulen: length) + ipv4_header = ipv4(saddr: @quad_0_ip, daddr: @bcast_ip, p: 17, len: 20 + length) + udp_sum = :pkt.makesum([ipv4_header, udp_header, payload]) + packet = [ + ether(dhost: dhost, shost: shost, type: 0x0800), + ipv4_header, + udp(udp_header, sum: udp_sum) + ] + Pf.inject!(pid1, packet, <>) + in_port = state.port.number + assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 1000 + refute_receive {@vxlan_port_sniffer, [^packet, ^payload]}, 1000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) end end @@ -155,6 +248,26 @@ defmodule FlogTest do :ok = GenServer.cast(Flay, {:flow_install, options, self()}) refute_receive %Openflow.ErrorMsg{}, 1000 end + + test "Inject Packet(ARP)" do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + payload = <<0::size(16)-unit(8)>> + packet = [ + ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) + ] + Pf.inject!(pid2, packet, payload) + refute_receive {@access_port_sniffer, [^packet, ^payload]}, 1000 + refute_receive {@vxlan_port_sniffer, [^packet, ^payload]}, 1000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) + end end describe("associate:register_bootstrap_rule:" <> @@ -184,6 +297,28 @@ defmodule FlogTest do :ok = GenServer.cast(Flay, {:flow_install, options, self()}) refute_receive %Openflow.ErrorMsg{}, 1000 end + + test "Inject Packet(ARP)" do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + packet = [ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip)] + expect = [ + ether(dhost: dhost, shost: shost, type: 0x8100), + {:"802.1q", 0, 0, 5, 0x0806}, + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip), + "" + ] + Pf.inject!(pid1, packet) + assert_receive {@vlan_trunk_port_sniffer, ^expect}, 1000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) + end end describe("associate:register_bootstrap_rule:" <> @@ -213,6 +348,30 @@ defmodule FlogTest do :ok = GenServer.cast(Flay, {:flow_install, options, self()}) refute_receive %Openflow.ErrorMsg{}, 1000 end + + test "Inject Packet(ARP)" do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + packet = [ + ether(dhost: dhost, shost: shost, type: 0x8100), + {:"802.1q", 0, 0, 5, 0x0806}, + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip), + ] + expect = [ + ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip), + "" + ] + Pf.inject!(pid2, packet) + assert_receive {@access_port_sniffer, ^expect}, 1000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) + end end describe("associate:register_bootstrap_rule:" <> @@ -242,6 +401,30 @@ defmodule FlogTest do :ok = GenServer.cast(Flay, {:flow_install, options, self()}) refute_receive %Openflow.ErrorMsg{}, 1000 end + + test "Inject Packet(ARP)" do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) + + shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) + dhost = Openflow.Match.Field.codec(@mac, :eth_dst) + packet = [ + ether(dhost: dhost, shost: shost, type: 0x8100), + {:"802.1q", 0, 0, 5, 0x0806}, + arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip), + ] + expect = [ + ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip), + "" + ] + Pf.inject!(pid2, packet) + assert_receive {@access_port_sniffer, ^expect}, 1000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) + end end describe("associate:register_bootstrap_rule:" <> @@ -615,7 +798,7 @@ defmodule FlogTest do defp setup_applications do Application.put_env(:tres, :protocol, :tcp, persistent: true) - Application.put_env(:tres, :port, 6633, persistent: true) + Application.put_env(:tres, :port, @listen_port, persistent: true) Application.put_env(:tres, :mac_connections, 1, persistent: true) Application.put_env(:tres, :mac_acceptors, 1, persistent: true) Application.put_env(:tres, :callback_module, Flay, persistent: true) @@ -641,11 +824,4 @@ defmodule FlogTest do port_desc = GenServer.call(Flay, :port_desc_stats, 5000) port_desc.ports end - - defp print_flows do - flow_stats = GenServer.call(Flay, :flow_stats, 5000) - for flow <- flow_stats.flows do - IO.inspect(flow) - end - end end diff --git a/test/packet_data/dhcp_discover.raw b/test/packet_data/dhcp_discover.raw new file mode 100644 index 0000000000000000000000000000000000000000..55f86d13e8a2436dbb236ba927669c9902dcfaf2 GIT binary patch literal 300 zcmZQ%WMfF^IG@CT1#A#-eEC-!i^LEWN^S~HHf3ZqVmYuPC6$M-IHNSr(9}{lC$XR; ZzrdWAQHF_`pRJrnM_;N^>pvDh0szWG5w-vT literal 0 HcmV?d00001 diff --git a/test/pf.ex b/test/pf.ex index 408e4fa..cad8f60 100644 --- a/test/pf.ex +++ b/test/pf.ex @@ -1,6 +1,12 @@ defmodule Pf do use GenServer + import Record + # Extract Erlang record for msantos/pkt + for {name, schema} <- extract_all(from_lib: "pkt/include/pkt.hrl") do + defrecord(name, schema) + end + defmodule State do defstruct [ ifname: nil, @@ -9,8 +15,12 @@ defmodule Pf do ] end - def inject!(pid, packet) do - GenServer.cast(pid, {:inject, packet}) + def inject!(pid, packet, payload \\ "") do + GenServer.cast(pid, {:inject, {packet, payload}}) + end + + def stop(pid) do + GenServer.cast(pid, :stop) end def start_link(ifname) do @@ -19,22 +29,23 @@ defmodule Pf do end def init([ifname, tester_pid]) do - {:ok, epcap_pid} = - :epcap.start_link( - interface: ifname, - promiscuous: true, - inject: true - ) - state = %State{ - pcap_ref: epcap_pid, - ifname: ifname, - tester_pid: tester_pid - } - {:ok, state} + {:ok, init_pf(ifname, tester_pid)} end - def handle_cast({:inject, packet}, state) do - :epcap.send(state.pcap_ref, packet) + def handle_cast({:inject, {headers, payload}}, state) do + headers_bin = for header <- headers do + case header do + ether() -> :pkt.ether(header) + {:"802.1q", _, _, _, _} = vlan -> :pkt_802_1q.codec(vlan) + arp() -> :pkt.arp(header) + ipv4() -> :pkt.ipv4(header) + lldp() -> :pkt.lldp(header) + udp() -> :pkt.udp(header) + tcp() -> :pkt.tcp(header) + end + end + binary = Enum.join(headers_bin, "") + :epcap.send(state.pcap_ref, <>) {:noreply, state} end def handle_cast(:stop, state) do @@ -46,10 +57,17 @@ defmodule Pf do def handle_info({:packet, _dlt, _time, _len, data}, state) do pkt = :pkt.decapsulate(data) - send(state.tester_pid, {pkt, self()}) + send(state.tester_pid, {to_string(state.ifname), pkt}) {:noreply, state} end def handle_info(_info, state) do {:noreply, state} end + + # private functions + + defp init_pf(ifname, tester_pid) do + {:ok, epcap_pid} = :epcap.start_link(interface: ifname, promiscuous: true, inject: true) + %State{pcap_ref: epcap_pid, ifname: ifname, tester_pid: tester_pid} + end end diff --git a/test/test_helper.exs b/test/test_helper.exs index 8f72200..135c19a 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1 +1,2 @@ +ExUnit.configure(seed: 0) ExUnit.start(trace: true) From 43f15e7d631cc450389bd116335530dfa7461e69 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 28 Nov 2017 21:04:09 +0900 Subject: [PATCH 21/50] test: Add test case for match and instructions --- lib/tres/message_helper.ex | 10 +- test/flay.ex | 6 +- test/flog_test.exs | 362 +++++++++++++++++++++---------------- 3 files changed, 220 insertions(+), 158 deletions(-) diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index 8cfe40e..1445288 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -1,7 +1,7 @@ defmodule Tres.MessageHelper do defmacro __using__(_) do quote location: :keep do - defp send_flow_mod_add(datapath_id, options) do + defp send_flow_mod_add(datapath_id, options \\ []) do flow_mod = %Openflow.FlowMod{ cookie: options[:cookie] || 0, priority: options[:priority] || 0, @@ -19,7 +19,7 @@ defmodule Tres.MessageHelper do send_message(flow_mod, datapath_id) end - defp send_flow_mod_modify(datapath_id, options) do + defp send_flow_mod_modify(datapath_id, options \\ []) do command = Tres.Utils.flow_command(:modify, options) flow_mod = %Openflow.FlowMod{ cookie: options[:cookie] || 0, @@ -35,7 +35,7 @@ defmodule Tres.MessageHelper do send_message(flow_mod, datapath_id) end - defp send_flow_mod_delete(datapath_id, options) do + defp send_flow_mod_delete(datapath_id, options \\ []) do command = Tres.Utils.flow_command(:delete, options) flow_mod = %Openflow.FlowMod{ cookie: options[:cookie] || 0, @@ -49,7 +49,7 @@ defmodule Tres.MessageHelper do send_message(flow_mod, datapath_id) end - defp send_packet_out(datapath_id, options) do + defp send_packet_out(datapath_id, options \\ []) do packet_out = %Openflow.PacketOut{ buffer_id: options[:buffer_id] || :no_buffer, in_port: options[:in_port] || :controller, @@ -59,7 +59,7 @@ defmodule Tres.MessageHelper do send_message(packet_out, datapath_id) end - defp send_group_mod_add(datapath_id, options) do + defp send_group_mod_add(datapath_id, options \\ []) do group_mod = Openflow.GroupMod.new( command: :add, type: options[:type] || :all, diff --git a/test/flay.ex b/test/flay.ex index 7016669..1ac78d8 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -47,7 +47,11 @@ defmodule Flay do {:noreply, %{state|tester_pid: tester_pid}} end def handle_cast(:flow_del, state) do - send_flow_mod_delete(state.datapath_id, match: Match.new) + send_flow_mod_delete(state.datapath_id) + {:noreply, state} + end + def handle_cast({:flow_del, cookie}, state) do + send_flow_mod_delete(state.datapath_id, cookie: cookie, cookie_mask: 0xffffffffffffffff) {:noreply, state} end diff --git a/test/flog_test.exs b/test/flog_test.exs index 31db334..bc9d46e 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -6,11 +6,9 @@ defmodule FlogTest do @vlan_trunk_port "veth0" # FIXME: @access_port "veth3" # FIXME: - @vxlan_port "veth4" # FIXME: @vlan_trunk_port_sniffer "veth1" # FIXME: @access_port_sniffer "veth2" # FIXME: - @vxlan_port_sniffer "veth5" # FIXME: @bootnet_vid 0x1000 ||| 5 @user_vid 0x1000 ||| 123 @@ -49,15 +47,15 @@ defmodule FlogTest do wait_for_connected() ports = get_ports_desc() vlan_trunk = Enum.find(ports, fn(port) -> port.name == @vlan_trunk_port end) - vxlan_port = Enum.find(ports, fn(port) -> port.name == @vxlan_port end) port = Enum.find(ports, fn(port) -> port.name == @access_port end) - cookie = 0x1000000000000001 - timeout = 32678 + cookie = 0x2000000000000001 + cookie2 = 0x2000000000000001 + timeout = 32_678 options = [ vlan_trunk: vlan_trunk, - vxlan_port: vxlan_port, port: port, cookie: cookie, + cookie2: cookie2, timeout: timeout ] {:ok, options} @@ -71,13 +69,12 @@ defmodule FlogTest do priority: 0 ] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end test "Inject Packet(ARP)" do {:ok, pid1} = Pf.start_link(@access_port_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) @@ -87,11 +84,9 @@ defmodule FlogTest do arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] Pf.inject!(pid1, packet, payload) - refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 1000 - refute_receive {@vxlan_port_sniffer, [^packet, ^payload]}, 1000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 2000 Pf.stop(pid1) Pf.stop(pid2) - Pf.stop(pid3) end end @@ -114,11 +109,10 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 {:ok, pid1} = Pf.start_link(@access_port_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) shost = Openflow.Match.Field.codec(state.vlan_trunk.hw_addr, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) @@ -131,12 +125,10 @@ defmodule FlogTest do Pf.inject!(pid2, packet) in_port = state.vlan_trunk.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, [^packet, ""]}, 1000 - refute_receive {@vxlan_port_sniffer, [^packet, ""]}, 1000 - refute_receive {@access_port_sniffer, [^packet, ""]}, 1000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ""]}, 2000 + refute_receive {@access_port_sniffer, [^packet, ""]}, 2000 Pf.stop(pid1) Pf.stop(pid2) - Pf.stop(pid3) end end @@ -151,7 +143,7 @@ defmodule FlogTest do priority: 201, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end end @@ -169,11 +161,10 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 {:ok, pid1} = Pf.start_link(@access_port_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) @@ -185,11 +176,9 @@ defmodule FlogTest do Pf.inject!(pid1, packet, payload) in_port = state.port.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 1000 - refute_receive {@vxlan_port_sniffer, [^packet, ^payload]}, 1000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 2000 Pf.stop(pid1) Pf.stop(pid2) - Pf.stop(pid3) end end @@ -206,11 +195,10 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 {:ok, pid1} = Pf.start_link(@access_port_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) @@ -228,11 +216,9 @@ defmodule FlogTest do Pf.inject!(pid1, packet, <>) in_port = state.port.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 1000 - refute_receive {@vxlan_port_sniffer, [^packet, ^payload]}, 1000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 2000 Pf.stop(pid1) Pf.stop(pid2) - Pf.stop(pid3) end end @@ -246,13 +232,12 @@ defmodule FlogTest do priority: 11, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end test "Inject Packet(ARP)" do {:ok, pid1} = Pf.start_link(@access_port_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) @@ -262,11 +247,9 @@ defmodule FlogTest do arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] Pf.inject!(pid2, packet, payload) - refute_receive {@access_port_sniffer, [^packet, ^payload]}, 1000 - refute_receive {@vxlan_port_sniffer, [^packet, ^payload]}, 1000 + refute_receive {@access_port_sniffer, [^packet, ^payload]}, 2000 Pf.stop(pid1) Pf.stop(pid2) - Pf.stop(pid3) end end @@ -295,13 +278,12 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end test "Inject Packet(ARP)" do {:ok, pid1} = Pf.start_link(@access_port_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) @@ -314,10 +296,9 @@ defmodule FlogTest do "" ] Pf.inject!(pid1, packet) - assert_receive {@vlan_trunk_port_sniffer, ^expect}, 1000 + assert_receive {@vlan_trunk_port_sniffer, ^expect}, 2000 Pf.stop(pid1) Pf.stop(pid2) - Pf.stop(pid3) end end @@ -346,13 +327,12 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end test "Inject Packet(ARP)" do {:ok, pid1} = Pf.start_link(@access_port_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) @@ -367,10 +347,9 @@ defmodule FlogTest do "" ] Pf.inject!(pid2, packet) - assert_receive {@access_port_sniffer, ^expect}, 1000 + assert_receive {@access_port_sniffer, ^expect}, 2000 Pf.stop(pid1) Pf.stop(pid2) - Pf.stop(pid3) end end @@ -399,13 +378,12 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end test "Inject Packet(ARP)" do {:ok, pid1} = Pf.start_link(@access_port_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@vxlan_port_sniffer) shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) dhost = Openflow.Match.Field.codec(@mac, :eth_dst) @@ -420,10 +398,9 @@ defmodule FlogTest do "" ] Pf.inject!(pid2, packet) - assert_receive {@access_port_sniffer, ^expect}, 1000 + assert_receive {@access_port_sniffer, ^expect}, 2000 Pf.stop(pid1) Pf.stop(pid2) - Pf.stop(pid3) end end @@ -445,7 +422,24 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 + end + + test "Inject Packet(ARP)" do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + payload = <<0::size(16)-unit(8)>> + packet = [ + ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) + ] + Pf.inject!(pid2, packet, payload) + refute_receive {@access_port_sniffer, [^packet, ^payload]}, 2000 + Pf.stop(pid1) + Pf.stop(pid2) end end @@ -476,7 +470,32 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 + end + end + + test "Inject Packet(ARP)" do + for trusted <- @trusted_macs do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + + shost = Openflow.Match.Field.codec(trusted, :eth_src) + dhost = Openflow.Match.Field.codec(@mac, :eth_dst) + payload = <<0::size(16)-unit(8)>> + packet = [ + ether(dhost: dhost, shost: shost, type: 0x8100), + {:"802.1q", 0, 0, 5, 0x0806}, + arp(op: 1, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) + ] + expect = [ + ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip), + payload + ] + Pf.inject!(pid2, packet, payload) + assert_receive {@access_port_sniffer, ^expect}, 2000 + Pf.stop(pid1) + Pf.stop(pid2) end end end @@ -497,89 +516,27 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end end describe("associate:register_usernet_rule:" <> - "table=0, priority=40,cookie={cookie},hard_timeout={htimeout},in_port={vxlan_port},dl_dst={mac}," <> - "actions=output:{port_no}") do + "in_port={vlan_trunk_port},dl_vlan={vlan}," <> + "actions=goto_table:1") do + setup [:flow_del_by_cookie] + test "Install Flow", state do - match = Openflow.Match.new( - in_port: state.vxlan_port.number, - eth_dst: @mac - ) - actions = [ - Openflow.Action.Output.new(state.port.number), - ] - ins = Openflow.Instruction.ApplyActions.new(actions) + match = Openflow.Match.new(in_port: state.vlan_trunk.number, vlan_vid: @user_vid) + ins = [Openflow.Instruction.GotoTable.new(1)] options = - [cookie: state.cookie, + [cookie: state.cookie2, table_id: 0, - priority: 40, - idle_timeout: state.timeout, - hard_timeout: state.timeout, + priority: 50, match: match, - instructions: [ins]] + instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe("associate:register_usernet_rule:" <> - "vlan_vid=0x1000/0x1000,tcp,tcp_dst=443,nw_dst={auth_ipv4_address}," <> - "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vxlan_port}") do - test "Install Flow", state do - match = Openflow.Match.new( - vlan_vid: @vlan_present, - eth_type: 0x0800, - ip_proto: 6, - ipv4_dst: @auth_ipv4_address, - tcp_dst: 443 - ) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(state.port.number), - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [cookie: 0x2000000000000001, - table_id: 1, - priority: 30, - match: match, - instructions: [ins]] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe("associate:register_usernet_rule:" <> - "vlan_vid=0x1000/0x1000,tcp,tcp_dst=80,nw_dst={captive_ipv4_address}," <> - "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vxlan_port}") do - test "Install Flow", state do - match = Openflow.Match.new( - vlan_vid: @vlan_present, - eth_type: 0x0800, - ip_proto: 6, - ipv4_dst: @captive_ipv4_address, - tcp_dst: 80 - ) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(state.port.number), - ] - ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [cookie: 0x2000000000000001, - table_id: 1, - priority: 30, - match: match, - instructions: [ins]] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end @@ -597,7 +554,7 @@ defmodule FlogTest do Openflow.Instruction.GotoTable.new(1) ] options = - [cookie: state.cookie, + [cookie: state.cookie2, table_id: 0, priority: 20, flags: [:send_flow_rem], @@ -606,7 +563,119 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 + end + end + + describe("associate:register_usernet_rule:" <> + "vlan_vid=0x1000/0x1000,tcp,tcp_dst=443,nw_dst={auth_ipv4_address}," <> + "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vlan_trunk_port}") do + test "Install Flow", state do + match = Openflow.Match.new( + vlan_vid: @vlan_present, + eth_type: 0x0800, + ip_proto: 6, + ipv4_dst: @auth_ipv4_address, + tcp_dst: 443 + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), + Openflow.Action.Output.new(state.vlan_trunk.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: state.cookie2, + table_id: 1, + priority: 30, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_receive %Openflow.ErrorMsg{}, 2000 + end + + test "Inject Packet(TCP:443)" do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) + + payload = "" + tcp_header = tcp( + sport: 53_688, + dport: 443, + seqno: 1_488_352_223, + off: 10, + syn: 1, + win: 29_200, + opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> + ) + ipv4_header = ipv4(saddr: @client_ip, daddr: @auth_ipv4_address, p: 6, len: 60) + tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) + ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) + packet = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] + expect = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum), payload] + Pf.inject!(pid1, packet, <>) + assert_receive {@vlan_trunk_port_sniffer, ^expect}, 2000 + Pf.stop(pid1) + Pf.stop(pid2) + end + end + + describe("associate:register_usernet_rule:" <> + "vlan_vid=0x1000/0x1000,tcp,tcp_dst=80,nw_dst={captive_ipv4_address}," <> + "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vlan_trunk_port}") do + test "Install Flow", state do + match = Openflow.Match.new( + vlan_vid: @vlan_present, + eth_type: 0x0800, + ip_proto: 6, + ipv4_dst: @captive_ipv4_address, + tcp_dst: 80 + ) + actions = [ + Openflow.Action.PopVlan.new, + Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), + Openflow.Action.Output.new(state.vlan_trunk.number), + ] + ins = Openflow.Instruction.ApplyActions.new(actions) + options = + [cookie: state.cookie2, + table_id: 1, + priority: 30, + match: match, + instructions: [ins]] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) + refute_receive %Openflow.ErrorMsg{}, 2000 + end + + test "Inject Packet(TCP:80)" do + {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) + + payload = "" + tcp_header = tcp( + sport: 53_688, + dport: 80, + seqno: 1_488_352_223, + off: 10, + syn: 1, + win: 29_200, + opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> + ) + ipv4_header = ipv4(saddr: @client_ip, daddr: @captive_ipv4_address, p: 6, len: 60) + tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) + ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) + packet = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] + expect = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum), payload] + Pf.inject!(pid1, packet, <>) + assert_receive {@vlan_trunk_port_sniffer, ^expect}, 2000 + Pf.stop(pid1) + Pf.stop(pid2) end end @@ -616,7 +685,7 @@ defmodule FlogTest do test "Install Flow", state do match = Openflow.Match.new(in_port: state.vlan_trunk.number, eth_src: @mac) options = - [cookie: state.cookie, + [cookie: state.cookie2, table_id: 0, priority: 19, flags: [:send_flow_rem], @@ -624,7 +693,7 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end @@ -636,7 +705,7 @@ defmodule FlogTest do actions = [Openflow.Action.Output.new(:controller)] ins = [Openflow.Instruction.ApplyActions.new(actions)] options = - [cookie: state.cookie, + [cookie: state.cookie2, table_id: 0, priority: 18, flags: [:send_flow_rem], @@ -645,7 +714,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end @@ -659,7 +728,7 @@ defmodule FlogTest do Openflow.Action.Output.new(state.port.number)] ins = [Openflow.Instruction.ApplyActions.new(actions)] options = - [cookie: state.cookie, + [cookie: state.cookie2, table_id: 1, priority: 50, flags: [:send_flow_rem], @@ -668,7 +737,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end @@ -680,16 +749,17 @@ defmodule FlogTest do actions = [ Openflow.Action.Output.new(state.vlan_trunk.number), Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(state.port.number)] + Openflow.Action.Output.new(state.port.number) + ] ins = [Openflow.Instruction.ApplyActions.new(actions)] options = - [cookie: 0x2000000000000001, + [cookie: state.cookie2, table_id: 1, priority: 60, match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end @@ -704,30 +774,13 @@ defmodule FlogTest do Openflow.Action.Output.new(state.port.number)] ins = [Openflow.Instruction.ApplyActions.new(actions)] options = - [cookie: 0x2000000000000001, + [cookie: state.cookie2, table_id: 1, priority: 20, match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe("associate:register_usernet_rule:" <> - "in_port={vlan_trunk_port},dl_vlan={vlan}," <> - "actions=goto_table:1") do - test "Install Flow", state do - match = Openflow.Match.new(in_port: state.vlan_trunk.number, vlan_vid: @user_vid) - ins = [Openflow.Instruction.GotoTable.new(1)] - options = - [cookie: 0x2000000000000001, - table_id: 0, - priority: 50, - match: match, - instructions: ins] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end @@ -759,7 +812,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end @@ -790,7 +843,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 1000 + refute_receive %Openflow.ErrorMsg{}, 2000 end end @@ -820,6 +873,11 @@ defmodule FlogTest do end end + defp flow_del_by_cookie(context) do + :ok = GenServer.cast(Flay, {:flow_del, context.cookie}) + :timer.sleep 2000 + end + defp get_ports_desc do port_desc = GenServer.call(Flay, :port_desc_stats, 5000) port_desc.ports From cfc2152b7d8c6fcc8da7ef3291b2a0b579ae3bd3 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Wed, 29 Nov 2017 21:35:15 +0900 Subject: [PATCH 22/50] test: Add test case for NAT and optimized test-controller --- test/flay.ex | 31 +++-- test/flog_test.exs | 274 ++++++++++++++++++++++++++++++++------------- test/pf.ex | 5 +- 3 files changed, 224 insertions(+), 86 deletions(-) diff --git a/test/flay.ex b/test/flay.ex index 1ac78d8..101bc3b 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -20,8 +20,8 @@ defmodule Flay do def init(args) do state = init_controller(args) - init_bridge(state.datapath_id) GenServer.cast(Flay, :desc_stats) + GenServer.cast(Flay, :flow_del) {:ok, state} end @@ -68,10 +68,16 @@ defmodule Flay do end def handle_info(%PortDesc.Reply{} = desc, state) do GenServer.reply(state.reply_to, desc) - {:noreply, %{state|reply_to: nil}} + {:noreply, state} end def handle_info(%Desc.Reply{} = desc, state) do - IO.inspect(desc) + info( + "[#{__MODULE__}] Switch Desc: " + <> "mfr = #{desc.mfr_desc} " + <> "hw = #{desc.hw_desc} " + <> "sw = #{desc.sw_desc} " + ) + init_bridge(state.datapath_id, desc) {:noreply, state} end def handle_info(%Flow.Reply{} = desc, state) do @@ -127,7 +133,8 @@ defmodule Flay do } end - defp init_bridge(datapath_id) do + defp init_bridge(datapath_id, %Desc.Reply{mfr_desc: "Aruba"}) do + :ok = info("Transform flow table pipeline") tables = [ TableFeatures.Body.new( table_id: 0, @@ -180,17 +187,19 @@ defmodule Flay do max_entries: 50, config: [:table_miss_mask], match: [ - :in_port, :eth_type, :eth_src, - :vlan_vid, :masked_eth_dst, + :vlan_vid, :ip_proto, :udp_dst, :tcp_dst, + :ipv4_src, + :ipv4_dst, + :arp_spa, + :arp_tpa ], wildcards: [ - :in_port, :eth_type, :eth_src, :masked_eth_dst, @@ -198,6 +207,10 @@ defmodule Flay do :ip_proto, :udp_dst, :tcp_dst, + :ipv4_src, + :ipv4_dst, + :arp_spa, + :arp_tpa ], instructions: [ Openflow.Instruction.GotoTable, @@ -221,4 +234,8 @@ defmodule Flay do |> send_message(datapath_id) send_flow_mod_delete(datapath_id, table_id: :all) end + defp init_bridge(_datapath_id, _mfr) do + :ok = info("Flow pipeline profile is not defined") + :ok + end end diff --git a/test/flog_test.exs b/test/flog_test.exs index bc9d46e..1148f6c 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -5,10 +5,12 @@ defmodule FlogTest do @listen_port 6653 @vlan_trunk_port "veth0" # FIXME: - @access_port "veth3" # FIXME: + @access_port1 "veth3" # FIXME: + @access_port2 "veth4" # FIXME: @vlan_trunk_port_sniffer "veth1" # FIXME: - @access_port_sniffer "veth2" # FIXME: + @access_port1_sniffer "veth2" # FIXME: + @access_port2_sniffer "veth5" # FIXME: @bootnet_vid 0x1000 ||| 5 @user_vid 0x1000 ||| 123 @@ -26,6 +28,7 @@ defmodule FlogTest do @gateway_ip {192,168,5,4} @farm_gw_ip {192,168,255,254} @farm_gw_mac "00000000000f" + @internet {8,8,8,8} @trusted_macs [ "0800274d3297", "0800274d3298", @@ -47,13 +50,15 @@ defmodule FlogTest do wait_for_connected() ports = get_ports_desc() vlan_trunk = Enum.find(ports, fn(port) -> port.name == @vlan_trunk_port end) - port = Enum.find(ports, fn(port) -> port.name == @access_port end) - cookie = 0x2000000000000001 + port = Enum.find(ports, fn(port) -> port.name == @access_port1 end) + port2 = Enum.find(ports, fn(port) -> port.name == @access_port2 end) + cookie = 0x1000000000000001 cookie2 = 0x2000000000000001 timeout = 32_678 options = [ vlan_trunk: vlan_trunk, port: port, + port2: port2, cookie: cookie, cookie2: cookie2, timeout: timeout @@ -69,11 +74,11 @@ defmodule FlogTest do priority: 0 ] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) @@ -84,7 +89,7 @@ defmodule FlogTest do arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] Pf.inject!(pid1, packet, payload) - refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 2000 + refute_receive {@vlan_trunk_port_sniffer, ^packet}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -109,9 +114,9 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(state.vlan_trunk.hw_addr, :eth_src) @@ -125,8 +130,8 @@ defmodule FlogTest do Pf.inject!(pid2, packet) in_port = state.vlan_trunk.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, [^packet, ""]}, 2000 - refute_receive {@access_port_sniffer, [^packet, ""]}, 2000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ""]}, 3000 + refute_receive {@access_port1_sniffer, [^packet, ""]}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -143,27 +148,27 @@ defmodule FlogTest do priority: 201, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end end describe("switch:uplink_escalation_flow:" <> - "table=0,priority=10,cookie=0x2000000000000000,arp,actions=controller") do + "table=0,priority=10,cookie=0x3000000000000000,arp,actions=controller") do test "Install Flow", state do match = Openflow.Match.new(eth_type: 0x0806) action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) options = - [cookie: 0x2000000000000000, + [cookie: 0x3000000000000000, table_id: 0, priority: 10, match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) @@ -176,28 +181,28 @@ defmodule FlogTest do Pf.inject!(pid1, packet, payload) in_port = state.port.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 2000 + refute_receive {@vlan_trunk_port_sniffer, ^packet}, 3000 Pf.stop(pid1) Pf.stop(pid2) end end describe("switch:uplink_escalation_flow:" <> - "table=0,priority=10,cookie=0x2000000000000000,udp,udp_dst=67,actions=controller") do + "table=0,priority=10,cookie=0x3000000000000000,udp,udp_dst=67,actions=controller") do test "Install Flow", state do match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) options = - [cookie: 0x2000000000000000, + [cookie: 0x3000000000000000, table_id: 0, priority: 10, match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) @@ -216,27 +221,27 @@ defmodule FlogTest do Pf.inject!(pid1, packet, <>) in_port = state.port.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, [^packet, ^payload]}, 2000 + refute_receive {@vlan_trunk_port_sniffer, ^packet}, 3000 Pf.stop(pid1) Pf.stop(pid2) end end describe("switch:uplink_escalation_flow:" <> - "table=0,priority=11,cookie=0x2000000000000000,in_port={trunk_port},actions=drop") do + "table=0,priority=11,cookie=0x3000000000000000,in_port={trunk_port},actions=drop") do test "Install Flow", state do match = Openflow.Match.new(in_port: state.vlan_trunk.number) options = - [cookie: 0x2000000000000000, + [cookie: 0x3000000000000000, table_id: 0, priority: 11, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) @@ -247,7 +252,7 @@ defmodule FlogTest do arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] Pf.inject!(pid2, packet, payload) - refute_receive {@access_port_sniffer, [^packet, ^payload]}, 2000 + refute_receive {@access_port1_sniffer, ^packet}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -278,11 +283,11 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) @@ -293,10 +298,9 @@ defmodule FlogTest do ether(dhost: dhost, shost: shost, type: 0x8100), {:"802.1q", 0, 0, 5, 0x0806}, arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip), - "" ] Pf.inject!(pid1, packet) - assert_receive {@vlan_trunk_port_sniffer, ^expect}, 2000 + assert_receive {@vlan_trunk_port_sniffer, ^expect}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -315,6 +319,7 @@ defmodule FlogTest do actions = [ Openflow.Action.PopVlan.new, Openflow.Action.Output.new(state.port.number), + Openflow.Action.Output.new(state.port2.number), ] ins = Openflow.Instruction.ApplyActions.new(actions) options = @@ -327,12 +332,13 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@access_port2_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) @@ -344,12 +350,13 @@ defmodule FlogTest do expect = [ ether(dhost: dhost, shost: shost, type: 0x0806), arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip), - "" ] Pf.inject!(pid2, packet) - assert_receive {@access_port_sniffer, ^expect}, 2000 + assert_receive {@access_port1_sniffer, ^expect}, 3000 + assert_receive {@access_port2_sniffer, ^expect}, 3000 Pf.stop(pid1) Pf.stop(pid2) + Pf.stop(pid3) end end @@ -378,11 +385,11 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) @@ -390,15 +397,14 @@ defmodule FlogTest do packet = [ ether(dhost: dhost, shost: shost, type: 0x8100), {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip), + arp(op: 2, sha: shost, tip: @client_ip, sip: @gateway_ip), ] expect = [ ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip), - "" + arp(op: 2, sha: shost, tip: @client_ip, sip: @gateway_ip), ] Pf.inject!(pid2, packet) - assert_receive {@access_port_sniffer, ^expect}, 2000 + assert_receive {@access_port1_sniffer, ^expect}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -422,11 +428,11 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) @@ -434,10 +440,10 @@ defmodule FlogTest do payload = <<0::size(16)-unit(8)>> packet = [ ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) + arp(op: 2, sha: shost, sip: @client_ip, tip: @gateway_ip) ] Pf.inject!(pid2, packet, payload) - refute_receive {@access_port_sniffer, [^packet, ^payload]}, 2000 + refute_receive {@access_port1_sniffer, ^packet}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -470,13 +476,13 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end test "Inject Packet(ARP)" do for trusted <- @trusted_macs do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(trusted, :eth_src) @@ -485,15 +491,14 @@ defmodule FlogTest do packet = [ ether(dhost: dhost, shost: shost, type: 0x8100), {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 1, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) + arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) ] expect = [ ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip), - payload + arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip), ] Pf.inject!(pid2, packet, payload) - assert_receive {@access_port_sniffer, ^expect}, 2000 + assert_receive {@access_port1_sniffer, ^expect}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -516,7 +521,7 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end end @@ -536,7 +541,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end @@ -563,7 +568,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end @@ -591,11 +596,11 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end test "Inject Packet(TCP:443)" do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) @@ -615,9 +620,9 @@ defmodule FlogTest do tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) packet = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] - expect = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum), payload] + expect = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] Pf.inject!(pid1, packet, <>) - assert_receive {@vlan_trunk_port_sniffer, ^expect}, 2000 + assert_receive {@vlan_trunk_port_sniffer, ^expect}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -647,11 +652,11 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end test "Inject Packet(TCP:80)" do - {:ok, pid1} = Pf.start_link(@access_port_sniffer) + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) shost = Openflow.Match.Field.codec(@mac, :eth_src) @@ -671,9 +676,9 @@ defmodule FlogTest do tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) packet = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] - expect = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum), payload] + expect = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] Pf.inject!(pid1, packet, <>) - assert_receive {@vlan_trunk_port_sniffer, ^expect}, 2000 + assert_receive {@vlan_trunk_port_sniffer, ^expect}, 3000 Pf.stop(pid1) Pf.stop(pid2) end @@ -693,7 +698,7 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end @@ -714,7 +719,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end @@ -722,7 +727,7 @@ defmodule FlogTest do "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,vlan_vid=0x1000/0x1000,dl_dst={mac}," <> "actions=strip_vlan,output:{port_no}") do test "Install Flow", state do - match = Openflow.Match.new(vlan_vid: @vlan_present, eth_src: @mac) + match = Openflow.Match.new(vlan_vid: @vlan_present, eth_dst: @mac) actions = [ Openflow.Action.PopVlan.new, Openflow.Action.Output.new(state.port.number)] @@ -737,7 +742,25 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 + end + + test "Inject Packet(ARP)" do + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + + shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) + dhost = Openflow.Match.Field.codec(@mac, :eth_dst) + vlan_ether_header = ether(dhost: dhost, shost: shost, type: 0x8100) + ether_header = ether(dhost: dhost, shost: shost, type: 0x0806) + vlan_header = {:"802.1q", 0, 1, 123, 0x0806} + arp_header = arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) + packet = [vlan_ether_header, vlan_header, arp_header] + expect = [ether_header, arp_header] + Pf.inject!(pid2, packet) + assert_receive {@access_port1_sniffer, ^expect}, 3000 + Pf.stop(pid1) + Pf.stop(pid2) end end @@ -749,7 +772,8 @@ defmodule FlogTest do actions = [ Openflow.Action.Output.new(state.vlan_trunk.number), Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(state.port.number) + Openflow.Action.Output.new(state.port.number), + Openflow.Action.Output.new(state.port2.number) ] ins = [Openflow.Instruction.ApplyActions.new(actions)] options = @@ -759,7 +783,51 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 + end + + test "Inject Packet(ARP) from VLAN TRUNK" do + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@access_port2_sniffer) + + shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + vlan_ether_header = ether(dhost: dhost, shost: shost, type: 0x8100) + ether_header = ether(dhost: dhost, shost: shost, type: 0x0806) + vlan_header = {:"802.1q", 0, 1, 123, 0x0806} + arp_header = arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip) + packet = [vlan_ether_header, vlan_header, arp_header] + Pf.inject!(pid2, packet) + expect = [ether_header, arp_header] + assert_receive {@access_port1_sniffer, ^expect}, 3000 + assert_receive {@access_port2_sniffer, ^expect}, 3000 + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) + end + + test "Inject Packet(ARP) from ACCESS" do + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + {:ok, pid3} = Pf.start_link(@access_port2_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + vlan_ether_header = ether(dhost: dhost, shost: shost, type: 0x8100) + ether_header = ether(dhost: dhost, shost: shost, type: 0x0806) + vlan_header = {:"802.1q", 0, 0, 123, 0x0806} + arp_header = arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip) + packet = [ether_header, arp_header] + Pf.inject!(pid1, packet) + expects = [ + {@vlan_trunk_port_sniffer, [vlan_ether_header, vlan_header, arp_header]}, + {@access_port2_sniffer, [ether_header, arp_header]} + ] + :ok = assert_receives(expects) + Pf.stop(pid1) + Pf.stop(pid2) + Pf.stop(pid3) end end @@ -780,7 +848,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end @@ -799,8 +867,9 @@ defmodule FlogTest do Openflow.Action.SetField.new({:eth_dst, @farm_gw_mac}), Openflow.Action.SetField.new({:arp_spa, @client_farm_ip}), Openflow.Action.SetField.new({:arp_tpa, @farm_gw_ip}), - Openflow.Action.PushVlan.new, - Openflow.Action.SetField.new({:vlan_vid, @user_vid}), + # Following is not work properly in "SwitchNode" configuration. + # Openflow.Action.PushVlan.new, + # Openflow.Action.SetField.new({:vlan_vid, @user_vid}), Openflow.Action.Output.new(state.vlan_trunk.number), ] ins = [Openflow.Instruction.ApplyActions.new(actions)] @@ -812,7 +881,7 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 end end @@ -825,12 +894,13 @@ defmodule FlogTest do eth_src: @mac, eth_type: 0x0800, ipv4_src: @client_ip, - ipv4_dst: {8,8,8,8} + ipv4_dst: @internet ) actions = [ Openflow.Action.SetField.new({:eth_dst, @farm_gw_mac}), - Openflow.Action.PushVlan.new, - Openflow.Action.SetField.new({:vlan_vid, @user_vid}), + # Following is not work properly in "SwitchNode" configuration. + # Openflow.Action.PushVlan.new, + # Openflow.Action.SetField.new({:vlan_vid, @user_vid}), Openflow.Action.SetField.new({:ipv4_src, @client_farm_ip}), Openflow.Action.Output.new(state.vlan_trunk.number), ] @@ -843,12 +913,62 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 2000 + refute_receive %Openflow.ErrorMsg{}, 3000 + end + + test "Inject Packet(TCP:80)" do + {:ok, pid1} = Pf.start_link(@access_port1_sniffer) + {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) + + payload = "" + tcp_header = tcp( + sport: 53_688, + dport: 80, + seqno: 1_488_352_223, + off: 10, + syn: 1, + win: 29_200, + opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> + ) + ipv4_header = ipv4(saddr: @client_ip, daddr: @internet, p: 6, len: 60) + tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) + ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) + packet = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] + Pf.inject!(pid1, packet, <>) + + shost = Openflow.Match.Field.codec(@mac, :eth_src) + dhost = Openflow.Match.Field.codec(@farm_gw_mac, :eth_dst) + ether_header = ether(dhost: dhost, shost: shost, type: 0x8100) + vlan_header = {:"802.1q", 0, 0, 123, 0x0800} + ipv4_header = ipv4(saddr: @client_farm_ip, daddr: @internet, p: 6, len: 60) + tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) + expect = [ether_header, vlan_header, ipv4(ipv4_header, sum: 1544), tcp(tcp_header, sum: tcp_sum)] + assert_receives([{@vlan_trunk_port_sniffer, expect}], 5000) + Pf.stop(pid1) + Pf.stop(pid2) end end # private functions + defp assert_receives(expects, timeout \\ 1000) do + assert_receives(expects, length(expects), timeout) + end + + defp assert_receives(_expects, 0, _timeout), do: :ok + defp assert_receives(expects, count, timeout) do + receive do + message -> + assert(message in expects) + assert_receives(expects, count - 1, timeout) + after timeout -> + flunk("Timeout") + end + end + defp setup_applications do Application.put_env(:tres, :protocol, :tcp, persistent: true) Application.put_env(:tres, :port, @listen_port, persistent: true) @@ -875,7 +995,7 @@ defmodule FlogTest do defp flow_del_by_cookie(context) do :ok = GenServer.cast(Flay, {:flow_del, context.cookie}) - :timer.sleep 2000 + :timer.sleep 3000 end defp get_ports_desc do diff --git a/test/pf.ex b/test/pf.ex index cad8f60..6257408 100644 --- a/test/pf.ex +++ b/test/pf.ex @@ -56,8 +56,9 @@ defmodule Pf do end def handle_info({:packet, _dlt, _time, _len, data}, state) do - pkt = :pkt.decapsulate(data) - send(state.tester_pid, {to_string(state.ifname), pkt}) + packet = :pkt.decapsulate(data) + packet_len = length(packet) + send(state.tester_pid, {to_string(state.ifname), Enum.take(packet, packet_len - 1)}) {:noreply, state} end def handle_info(_info, state) do From f2297c551a84831619dad38409cd21a284f66710 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 1 Dec 2017 11:11:11 +0900 Subject: [PATCH 23/50] Add an example controller for hardware switch test --- examples/heckle/.gitignore | 20 +++ examples/heckle/README.md | 21 +++ examples/heckle/config/config.exs | 30 ++++ examples/heckle/lib/heckle.ex | 18 +++ examples/heckle/lib/heckle/application.ex | 20 +++ examples/heckle/lib/heckle/controller.ex | 133 ++++++++++++++++++ examples/heckle/lib/heckle/flow_patterns.ex | 113 +++++++++++++++ .../heckle/lib/heckle/pipeline_profiles.ex | 31 ++++ examples/heckle/mix.exs | 22 +++ examples/heckle/mix.lock | 5 + examples/heckle/test/heckle_test.exs | 8 ++ examples/heckle/test/test_helper.exs | 1 + lib/tres/secure_channel.ex | 11 +- src/tres_xact_kv.erl | 9 +- test/flay.ex | 8 +- test/flog_test.exs | 101 ++++++++----- 16 files changed, 501 insertions(+), 50 deletions(-) create mode 100644 examples/heckle/.gitignore create mode 100644 examples/heckle/README.md create mode 100644 examples/heckle/config/config.exs create mode 100644 examples/heckle/lib/heckle.ex create mode 100644 examples/heckle/lib/heckle/application.ex create mode 100644 examples/heckle/lib/heckle/controller.ex create mode 100644 examples/heckle/lib/heckle/flow_patterns.ex create mode 100644 examples/heckle/lib/heckle/pipeline_profiles.ex create mode 100644 examples/heckle/mix.exs create mode 100644 examples/heckle/mix.lock create mode 100644 examples/heckle/test/heckle_test.exs create mode 100644 examples/heckle/test/test_helper.exs diff --git a/examples/heckle/.gitignore b/examples/heckle/.gitignore new file mode 100644 index 0000000..12179ea --- /dev/null +++ b/examples/heckle/.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/examples/heckle/README.md b/examples/heckle/README.md new file mode 100644 index 0000000..eabaf9d --- /dev/null +++ b/examples/heckle/README.md @@ -0,0 +1,21 @@ +# Heckle + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `heckle` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:heckle, "~> 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/heckle](https://hexdocs.pm/heckle). + diff --git a/examples/heckle/config/config.exs b/examples/heckle/config/config.exs new file mode 100644 index 0000000..b8b1eed --- /dev/null +++ b/examples/heckle/config/config.exs @@ -0,0 +1,30 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +config :heckle, + vlan_tagging: true, + vlan_id: 123, + vlan_trunk: "veth0", + access_port1: "veth4", + access_port2: "veth3", + receiver_mac: "00000000000f", + receiver_ip: {8,8,8,8}, + sender_mac: "001122334455", + inside_local: {192,168,5,10}, + outside_local: {192,168,255,1}, + flow_pattern: :bum + +config :tres, + protocol: :tcp, + port: 6653, + max_connections: 10, + num_acceptors: 10, + callback_module: Heckle.Controller, + callback_args: ["0000d2851d52d749"] + +config :logger, + level: :debug, + format: "$date $time [$level] $metadata$message\n", + metadata: [:application], + handle_otp_reports: true diff --git a/examples/heckle/lib/heckle.ex b/examples/heckle/lib/heckle.ex new file mode 100644 index 0000000..ff5d007 --- /dev/null +++ b/examples/heckle/lib/heckle.ex @@ -0,0 +1,18 @@ +defmodule Heckle do + @moduledoc """ + Documentation for Heckle. + """ + + @doc """ + Hello world. + + ## Examples + + iex> Heckle.hello + :world + + """ + def hello do + :world + end +end diff --git a/examples/heckle/lib/heckle/application.ex b/examples/heckle/lib/heckle/application.ex new file mode 100644 index 0000000..7f46096 --- /dev/null +++ b/examples/heckle/lib/heckle/application.ex @@ -0,0 +1,20 @@ +defmodule Heckle.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + def start(_type, _args) do + # List all child processes to be supervised + children = [ + # Starts a worker by calling: Heckle.Worker.start_link(arg) + # {Heckle.Worker, arg}, + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Heckle.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/examples/heckle/lib/heckle/controller.ex b/examples/heckle/lib/heckle/controller.ex new file mode 100644 index 0000000..f4944e9 --- /dev/null +++ b/examples/heckle/lib/heckle/controller.ex @@ -0,0 +1,133 @@ +defmodule Heckle.Controller do + use GenServer + use Bitwise + use Tres.Controller + + import Logger + + alias Heckle.PipelineProfiles + alias Heckle.FlowPatterns + + defmodule State do + defstruct [ + dpid: nil, + conn_ref: nil, + access_port1_name: nil, + access_port2_name: nil, + trunk_port_name: nil, + access_port1: nil, + access_port2: nil, + trunk_port: nil, + vlan_tagging: nil, + vlan_id: nil, + receiver_mac: nil, + receiver_ip: nil, + sender_mac: nil, + inside_local: nil, + outside_local: nil, + flow_pattern: nil + ] + end + + def start_link(dpid, args) do + GenServer.start(__MODULE__, [dpid, args]) + end + + def init([{dpid, _aux_id}, [dpid]]) do + :ok = info("Switch Ready: dpid: #{inspect(dpid)} on #{inspect(self())}") + state = init_state(dpid) + {:ok, state} + end + def init([{dpid, _aux_id}, [_dpid]]) do + :ok = info("Switch Ready: dpid: #{inspect(dpid)} but not acceptable") + :ignore + end + + def handle_cast(:send_flows, state) do + state + |> FlowPatterns.flows + |> Enum.each(&send_flow_mod_add(state.dpid, &1)) + {:noreply, state} + end + + def handle_info(%Desc.Reply{mfr_desc: "Aruba"} = desc, %State{dpid: dpid} = state) do + info("Switch Desc: mfr = #{desc.mfr_desc} hw = #{desc.hw_desc} sw = #{desc.sw_desc}") + :ok = PipelineProfiles.of_aruba() + |> TableFeatures.Request.new + |> send_message(dpid) + {:noreply, state} + end + def handle_info(%Desc.Reply{} = desc, state) do + :ok = info("Switch Desc: mfr = #{desc.mfr_desc} hw = #{desc.hw_desc} sw = #{desc.sw_desc}") + :ok = GenServer.cast(self(), :send_flows) + {:noreply, state} + end + def handle_info(%TableFeatures.Reply{xid: xid}, state) do + :ok = info("Pipeline modification is success (xid: #{xid})") + :ok = GenServer.cast(self(), :send_flows) + {:noreply, state} + end + def handle_info(%PortDesc.Reply{ports: ports}, state) do + info("Received Port Desc") + access_port1 = Enum.find(ports, fn(port) -> port.name == state.access_port1_name end) + access_port2 = Enum.find(ports, fn(port) -> port.name == state.access_port2_name end) + trunk_port = Enum.find(ports, fn(port) -> port.name == state.trunk_port_name end) + :ok = desc_stats_request(state.dpid) + {:noreply, %{state|access_port1: access_port1, access_port2: access_port2, trunk_port: trunk_port}} + end + def handle_info(%ErrorMsg{code: code, type: type, data: data, xid: xid}, state) do + :ok = warn("Request Failed(xid: #{xid}):"<> + " code: #{code}"<> + " type: #{type}"<> + " data: #{inspect(data)}"<> + " dpid: #{inspect(state.dpid)}") + {:stop, :request_failed, state} + end + def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do + :ok = debug("Switch Disconnected: dpid: #{inspect(state.dpid)}") + {:stop, :normal, state} + end + def handle_info(_info, state) do + {:noreply, state} + end + + # private functions + defp init_state(dpid) do + :ok = init_datapath(dpid) + conn_ref = SwitchRegistry.monitor(dpid) + config = Application.get_all_env(:heckle) + %State{ + dpid: dpid, + conn_ref: conn_ref, + access_port1_name: config[:access_port1], + access_port2_name: config[:access_port2], + trunk_port_name: config[:vlan_trunk], + vlan_tagging: config[:vlan_tagging] || true, + vlan_id: 0x1000 ||| (config[:vlan_id] || 0), + receiver_mac: config[:receiver_mac], + receiver_ip: config[:receiver_ip], + sender_mac: config[:sender_mac], + inside_local: config[:inside_local], + outside_local: config[:outside_local], + flow_pattern: config[:flow_pattern] || :nat + } + end + + defp init_datapath(dpid) do + :ok = send_flow_mod_delete(dpid) + :ok = port_desc_stats_request(dpid) + :ok = set_config(dpid) + end + + defp desc_stats_request(dpid) do + :ok = send_message(Desc.Request.new, dpid) + end + + defp port_desc_stats_request(dpid) do + :ok = send_message(PortDesc.Request.new, dpid) + end + + defp set_config(dpid) do + :ok = send_message(SetConfig.new(miss_send_len: :no_buffer), dpid) + end +end diff --git a/examples/heckle/lib/heckle/flow_patterns.ex b/examples/heckle/lib/heckle/flow_patterns.ex new file mode 100644 index 0000000..2a8cad2 --- /dev/null +++ b/examples/heckle/lib/heckle/flow_patterns.ex @@ -0,0 +1,113 @@ +defmodule Heckle.FlowPatterns do + use Bitwise + use Tres.Controller + + @mcast {"010000000000", "010000000000"} + + def flows(%Heckle.Controller.State{flow_pattern: :nat} = state) do + [[table_id: 1, + priority: 30, + cookie: 0x3000000000000001, + match: Match.new( + eth_dst: state.receiver_mac, + eth_src: state.sender_mac, + eth_type: 0x0800, + ipv4_src: state.inside_local, + ipv4_dst: state.receiver_ip + ), + instructions: [ + ApplyActions.new([ + SetField.new({:eth_dst, state.receiver_mac}), + SetField.new({:ipv4_src, state.outside_local}), + Output.new(state.trunk_port.number) + ])] + ], + [table_id: 1, + priority: 30, + cookie: 0x3000000000000001, + match: Match.new( + eth_dst: state.sender_mac, + eth_src: state.receiver_mac, + eth_type: 0x0800, + ipv4_src: state.receiver_ip, + ipv4_dst: state.outside_local + ), + instructions: [ + ApplyActions.new([ + SetField.new({:eth_src, state.receiver_mac}), + SetField.new({:ipv4_dst, state.inside_local}), + Output.new(state.access_port1.number) + ])] + ], + ] ++ classifier(state) + end + def flows(%Heckle.Controller.State{flow_pattern: :bum} = state) do + [[table_id: 1, + priority: 30, + cookie: 0x3000000000000001, + match: Match.new( + vlan_vid: state.vlan_id, + eth_dst: @mcast + ), + instructions: [ + ApplyActions.new([ + Openflow.Action.Output.new(state.trunk_port.number), + Openflow.Action.PopVlan.new, + Openflow.Action.Output.new(state.access_port1.number), + Openflow.Action.Output.new(state.access_port2.number) + ])] + ]] ++ classifier(state) + end + + # private functions + + defp classifier(state) do + [ + [table_id: 0, + priority: 50, + cookie: 0x1000000000000001, + match: Match.new( + in_port: state.trunk_port.number, + vlan_vid: state.vlan_id + ), + instructions: [ + ApplyActions.new([ + PushVlan.new, + SetField.new({:vlan_vid, state.vlan_id}) + ]), + GotoTable.new(1) + ] + ], + [table_id: 0, + priority: 20, + cookie: 0x1000000000000001, + match: Match.new( + in_port: state.access_port1.number, + eth_src: state.sender_mac + ), + instructions: [ + ApplyActions.new([ + PushVlan.new, + SetField.new({:vlan_vid, state.vlan_id}) + ]), + GotoTable.new(1) + ] + ], + [table_id: 0, + priority: 20, + cookie: 0x1000000000000001, + match: Match.new( + in_port: state.access_port2.number, + eth_src: state.sender_mac + ), + instructions: [ + ApplyActions.new([ + PushVlan.new, + SetField.new({:vlan_vid, state.vlan_id}) + ]), + GotoTable.new(1) + ] + ], + ] + end +end diff --git a/examples/heckle/lib/heckle/pipeline_profiles.ex b/examples/heckle/lib/heckle/pipeline_profiles.ex new file mode 100644 index 0000000..492febd --- /dev/null +++ b/examples/heckle/lib/heckle/pipeline_profiles.ex @@ -0,0 +1,31 @@ +defmodule Heckle.PipelineProfiles do + use Tres.Controller + + def of_aruba do + [ + TableFeatures.Body.new( + table_id: 0, + name: "classifier", + max_entries: 10, + config: [:table_miss_mask], # deprecated mask. + match: [:in_port, :masked_eth_src], + wildcards: [:in_port, :masked_eth_src], + instructions: [GotoTable], + write_actions: [Output], + next_tables: [1], + ), + TableFeatures.Body.new( + table_id: 1, + name: "NAT", + max_entries: 10, + config: [:table_miss_mask], + match: [:eth_dst, :eth_src, :eth_type, :ipv4_src, :ipv4_dst], + wildcards: [:eth_dst, :eth_src, :eth_type, :ipv4_src, :ipv4_dst], + instructions: [ApplyActions], + write_actions: [SetField, PopVlan, PushVlan, Output], + apply_setfield: [:eth_dst, :vlan_vid, :ipv4_src, :ipv4_dst], + next_tables: [], + ) + ] + end +end diff --git a/examples/heckle/mix.exs b/examples/heckle/mix.exs new file mode 100644 index 0000000..ad0528a --- /dev/null +++ b/examples/heckle/mix.exs @@ -0,0 +1,22 @@ +defmodule Heckle.Mixfile do + use Mix.Project + + @tres_path "../../../tres" + + def project do + [app: :heckle, + version: "0.1.0", + elixir: "~> 1.5", + start_permanent: Mix.env == :prod, + deps: deps()] + end + + def application do + [extra_applications: [:logger, :tres], + mod: {Heckle.Application, []}] + end + + defp deps do + [{:tres, path: @tres_path}] + end +end diff --git a/examples/heckle/mix.lock b/examples/heckle/mix.lock new file mode 100644 index 0000000..eeade00 --- /dev/null +++ b/examples/heckle/mix.lock @@ -0,0 +1,5 @@ +%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, + "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, + "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, + "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"}, + "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}} diff --git a/examples/heckle/test/heckle_test.exs b/examples/heckle/test/heckle_test.exs new file mode 100644 index 0000000..855f884 --- /dev/null +++ b/examples/heckle/test/heckle_test.exs @@ -0,0 +1,8 @@ +defmodule HeckleTest do + use ExUnit.Case + doctest Heckle + + test "greets the world" do + assert Heckle.hello() == :world + end +end diff --git a/examples/heckle/test/test_helper.exs b/examples/heckle/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/examples/heckle/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index dbd7c41..1e3153a 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -189,10 +189,13 @@ defmodule Tres.SecureChannel do :keep_state_and_data end defp handle_CONNECTED(:cast, {:send_message, message} = action, state_data) do - if Queue.is_empty(state_data.action_queue), - do: xactional_send_message(message, state_data) - action_queue = Queue.in(action, state_data.action_queue) - {:keep_state, %{state_data|action_queue: action_queue}} + new_action_queue = if XACT_KV.is_empty(state_data.xact_kv_ref) do + xactional_send_message(message, state_data) + state_data.action_queue + else + Queue.in(action, state_data.action_queue) + end + {:keep_state, %{state_data|action_queue: new_action_queue}} end # WATING state diff --git a/src/tres_xact_kv.erl b/src/tres_xact_kv.erl index e7a99a1..cb6c33a 100644 --- a/src/tres_xact_kv.erl +++ b/src/tres_xact_kv.erl @@ -3,7 +3,7 @@ -include_lib("stdlib/include/ms_transform.hrl"). -export([create/0, drop/1]). --export([insert/3, update/3, get/2, delete/2, is_exists/2]). +-export([insert/3, update/3, get/2, delete/2, is_exists/2, is_empty/1]). -define(TABLE, xact_kv). -define(ENTRY, xact_entry). @@ -44,6 +44,13 @@ is_exists(Tid, Xid) -> [] -> false end. +-spec is_empty(reference()) -> boolean(). +is_empty(Tid) -> + case ets:info(Tid, size) of + 0 -> true; + _ -> false + end. + %% Private functions ms_for_exists(Xid) -> diff --git a/test/flay.ex b/test/flay.ex index 101bc3b..a6fbccf 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -195,9 +195,7 @@ defmodule Flay do :udp_dst, :tcp_dst, :ipv4_src, - :ipv4_dst, - :arp_spa, - :arp_tpa + :ipv4_dst ], wildcards: [ :eth_type, @@ -208,9 +206,7 @@ defmodule Flay do :udp_dst, :tcp_dst, :ipv4_src, - :ipv4_dst, - :arp_spa, - :arp_tpa + :ipv4_dst ], instructions: [ Openflow.Instruction.GotoTable, diff --git a/test/flog_test.exs b/test/flog_test.exs index 1148f6c..3037cc7 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -74,7 +74,8 @@ defmodule FlogTest do priority: 0 ] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(ARP)" do @@ -89,7 +90,7 @@ defmodule FlogTest do arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] Pf.inject!(pid1, packet, payload) - refute_receive {@vlan_trunk_port_sniffer, ^packet}, 3000 + refute_receive {@vlan_trunk_port_sniffer, ^packet}, 1000 Pf.stop(pid1) Pf.stop(pid2) end @@ -114,7 +115,8 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) @@ -129,9 +131,9 @@ defmodule FlogTest do ] Pf.inject!(pid2, packet) in_port = state.vlan_trunk.number - assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, [^packet, ""]}, 3000 - refute_receive {@access_port1_sniffer, [^packet, ""]}, 3000 + assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 + refute_receive {@vlan_trunk_port_sniffer, [^packet, ""]}, 1000 + refute_receive {@access_port1_sniffer, [^packet, ""]}, 1000 Pf.stop(pid1) Pf.stop(pid2) end @@ -148,25 +150,27 @@ defmodule FlogTest do priority: 201, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end end describe("switch:uplink_escalation_flow:" <> - "table=0,priority=10,cookie=0x3000000000000000,arp,actions=controller") do + "table=0,priority=10,cookie=0x1000000000000000,arp,actions=controller") do test "Install Flow", state do match = Openflow.Match.new(eth_type: 0x0806) action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) options = - [cookie: 0x3000000000000000, + [cookie: 0x1000000000000000, table_id: 0, priority: 10, match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) @@ -180,27 +184,28 @@ defmodule FlogTest do ] Pf.inject!(pid1, packet, payload) in_port = state.port.number - assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, ^packet}, 3000 + assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 + refute_receive {@vlan_trunk_port_sniffer, ^packet}, 1000 Pf.stop(pid1) Pf.stop(pid2) end end describe("switch:uplink_escalation_flow:" <> - "table=0,priority=10,cookie=0x3000000000000000,udp,udp_dst=67,actions=controller") do + "table=0,priority=10,cookie=0x1000000000000000,udp,udp_dst=67,actions=controller") do test "Install Flow", state do match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) options = - [cookie: 0x3000000000000000, + [cookie: 0x1000000000000000, table_id: 0, priority: 10, match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 {:ok, pid1} = Pf.start_link(@access_port1_sniffer) {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) @@ -220,24 +225,25 @@ defmodule FlogTest do ] Pf.inject!(pid1, packet, <>) in_port = state.port.number - assert_receive %Openflow.PacketIn{in_port: ^in_port}, 3000 - refute_receive {@vlan_trunk_port_sniffer, ^packet}, 3000 + assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 + refute_receive {@vlan_trunk_port_sniffer, ^packet}, 1000 Pf.stop(pid1) Pf.stop(pid2) end end describe("switch:uplink_escalation_flow:" <> - "table=0,priority=11,cookie=0x3000000000000000,in_port={trunk_port},actions=drop") do + "table=0,priority=11,cookie=0x1000000000000000,in_port={trunk_port},actions=drop") do test "Install Flow", state do match = Openflow.Match.new(in_port: state.vlan_trunk.number) options = - [cookie: 0x3000000000000000, + [cookie: 0x1000000000000000, table_id: 0, priority: 11, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(ARP)" do @@ -252,7 +258,7 @@ defmodule FlogTest do arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] Pf.inject!(pid2, packet, payload) - refute_receive {@access_port1_sniffer, ^packet}, 3000 + refute_receive {@access_port1_sniffer, ^packet}, 1000 Pf.stop(pid1) Pf.stop(pid2) end @@ -283,7 +289,8 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(ARP)" do @@ -332,7 +339,8 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(ARP)" do @@ -385,7 +393,8 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(ARP)" do @@ -428,7 +437,8 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(ARP)" do @@ -476,7 +486,8 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -521,7 +532,8 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end end @@ -541,7 +553,8 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -568,7 +581,8 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -596,7 +610,8 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(TCP:443)" do @@ -652,7 +667,8 @@ defmodule FlogTest do match: match, instructions: [ins]] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(TCP:80)" do @@ -698,7 +714,8 @@ defmodule FlogTest do hard_timeout: state.timeout, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -719,7 +736,8 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -742,7 +760,8 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(ARP)" do @@ -758,7 +777,7 @@ defmodule FlogTest do packet = [vlan_ether_header, vlan_header, arp_header] expect = [ether_header, arp_header] Pf.inject!(pid2, packet) - assert_receive {@access_port1_sniffer, ^expect}, 3000 + assert_receive {@access_port1_sniffer, ^expect}, 1000 Pf.stop(pid1) Pf.stop(pid2) end @@ -783,7 +802,8 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(ARP) from VLAN TRUNK" do @@ -848,7 +868,8 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -881,7 +902,8 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -913,7 +935,8 @@ defmodule FlogTest do match: match, instructions: ins] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - refute_receive %Openflow.ErrorMsg{}, 3000 + :timer.sleep 1000 + refute_receive %Openflow.ErrorMsg{}, 1000 end test "Inject Packet(TCP:80)" do From 49d40bf2c011ac638562c7322b57144485c33a17 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 1 Dec 2017 11:33:53 +0900 Subject: [PATCH 24/50] Fix to work on Aruba --- examples/heckle/config/config.exs | 12 ++++++------ .../heckle/lib/heckle/pipeline_profiles.ex | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/examples/heckle/config/config.exs b/examples/heckle/config/config.exs index b8b1eed..abcf6ee 100644 --- a/examples/heckle/config/config.exs +++ b/examples/heckle/config/config.exs @@ -5,23 +5,23 @@ use Mix.Config config :heckle, vlan_tagging: true, vlan_id: 123, - vlan_trunk: "veth0", - access_port1: "veth4", - access_port2: "veth3", + vlan_trunk: "1", + access_port1: "3", + access_port2: "4", receiver_mac: "00000000000f", receiver_ip: {8,8,8,8}, sender_mac: "001122334455", inside_local: {192,168,5,10}, outside_local: {192,168,255,1}, - flow_pattern: :bum + flow_pattern: :bum # :nat || :bum config :tres, protocol: :tcp, - port: 6653, + port: 6633, max_connections: 10, num_acceptors: 10, callback_module: Heckle.Controller, - callback_args: ["0000d2851d52d749"] + callback_args: ["0002b05ada98d790"] config :logger, level: :debug, diff --git a/examples/heckle/lib/heckle/pipeline_profiles.ex b/examples/heckle/lib/heckle/pipeline_profiles.ex index 492febd..6327d1a 100644 --- a/examples/heckle/lib/heckle/pipeline_profiles.ex +++ b/examples/heckle/lib/heckle/pipeline_profiles.ex @@ -8,22 +8,23 @@ defmodule Heckle.PipelineProfiles do name: "classifier", max_entries: 10, config: [:table_miss_mask], # deprecated mask. - match: [:in_port, :masked_eth_src], - wildcards: [:in_port, :masked_eth_src], - instructions: [GotoTable], - write_actions: [Output], + match: [:in_port, :vlan_vid, :eth_src], + wildcards: [:in_port, :vlan_vid, :eth_src], + instructions: [GotoTable, ApplyActions], + apply_actions: [Output, PushVlan, SetField, PopVlan], + apply_setfield: [:eth_dst, :vlan_vid], next_tables: [1], ), TableFeatures.Body.new( table_id: 1, - name: "NAT", + name: "process", max_entries: 10, config: [:table_miss_mask], - match: [:eth_dst, :eth_src, :eth_type, :ipv4_src, :ipv4_dst], - wildcards: [:eth_dst, :eth_src, :eth_type, :ipv4_src, :ipv4_dst], + match: [:eth_src, :masked_eth_dst, :vlan_vid, :eth_type, :ipv4_src, :ipv4_dst], + wildcards: [:eth_src, :masked_eth_dst, :vlan_vid, :eth_type, :ipv4_src, :ipv4_dst], instructions: [ApplyActions], - write_actions: [SetField, PopVlan, PushVlan, Output], - apply_setfield: [:eth_dst, :vlan_vid, :ipv4_src, :ipv4_dst], + apply_actions: [SetField, PopVlan, PushVlan, Output], + apply_setfield: [:eth_src, :eth_dst, :vlan_vid, :ipv4_src, :ipv4_dst], next_tables: [], ) ] From 9b7fb2dc9667223d27044e0e546c42ec42ba4da9 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 1 Dec 2017 20:51:43 +0900 Subject: [PATCH 25/50] VLAN trunk test and NAT test --- examples/heckle/config/config.exs | 21 +++--- examples/heckle/lib/heckle/controller.ex | 80 +++++++++++++++++++-- examples/heckle/lib/heckle/flow_patterns.ex | 36 ++++++---- examples/heckle/mix.exs | 3 +- examples/heckle/mix.lock | 1 + 5 files changed, 112 insertions(+), 29 deletions(-) diff --git a/examples/heckle/config/config.exs b/examples/heckle/config/config.exs index abcf6ee..0b8ca46 100644 --- a/examples/heckle/config/config.exs +++ b/examples/heckle/config/config.exs @@ -5,15 +5,16 @@ use Mix.Config config :heckle, vlan_tagging: true, vlan_id: 123, - vlan_trunk: "1", - access_port1: "3", - access_port2: "4", - receiver_mac: "00000000000f", - receiver_ip: {8,8,8,8}, - sender_mac: "001122334455", - inside_local: {192,168,5,10}, - outside_local: {192,168,255,1}, - flow_pattern: :bum # :nat || :bum + vlan_trunk1: "1", + vlan_trunk2: "4", + access_port1: "2", + access_port2: "3", + receiver_mac: "1a2c3d0e0191", + receiver_ip: {192,168,123,1}, + sender_mac: "cec9bd531dfc", + inside_local: {192,168,11,1}, + outside_local: {192,168,123,2}, + flow_pattern: :nat # :nat || :bum config :tres, protocol: :tcp, @@ -24,7 +25,7 @@ config :tres, callback_args: ["0002b05ada98d790"] config :logger, - level: :debug, + level: :info, format: "$date $time [$level] $metadata$message\n", metadata: [:application], handle_otp_reports: true diff --git a/examples/heckle/lib/heckle/controller.ex b/examples/heckle/lib/heckle/controller.ex index f4944e9..0eded9e 100644 --- a/examples/heckle/lib/heckle/controller.ex +++ b/examples/heckle/lib/heckle/controller.ex @@ -14,10 +14,12 @@ defmodule Heckle.Controller do conn_ref: nil, access_port1_name: nil, access_port2_name: nil, - trunk_port_name: nil, + trunk_port1_name: nil, + trunk_port2_name: nil, access_port1: nil, access_port2: nil, - trunk_port: nil, + trunk_port1: nil, + trunk_port2: nil, vlan_tagging: nil, vlan_id: nil, receiver_mac: nil, @@ -44,6 +46,8 @@ defmodule Heckle.Controller do end def handle_cast(:send_flows, state) do + :ok = print_flows(state) + state |> FlowPatterns.flows |> Enum.each(&send_flow_mod_add(state.dpid, &1)) @@ -67,13 +71,31 @@ defmodule Heckle.Controller do :ok = GenServer.cast(self(), :send_flows) {:noreply, state} end + def handle_info(%Flow.Reply{flows: flows}, state) do + for flow <- flows do + :ok = info( + "table_id: #{flow.table_id} " <> + "pkt_count: #{flow.packet_count} "<> + "byt_count: #{flow.byte_count} "<> + "match: #{inspect(flow.match)} "<> + "insts: #{inspect(flow.instructions)} " + ) + end + {:noreply, state} + end def handle_info(%PortDesc.Reply{ports: ports}, state) do info("Received Port Desc") access_port1 = Enum.find(ports, fn(port) -> port.name == state.access_port1_name end) access_port2 = Enum.find(ports, fn(port) -> port.name == state.access_port2_name end) - trunk_port = Enum.find(ports, fn(port) -> port.name == state.trunk_port_name end) + trunk_port1 = Enum.find(ports, fn(port) -> port.name == state.trunk_port1_name end) + trunk_port2 = Enum.find(ports, fn(port) -> port.name == state.trunk_port2_name end) :ok = desc_stats_request(state.dpid) - {:noreply, %{state|access_port1: access_port1, access_port2: access_port2, trunk_port: trunk_port}} + {:noreply, %{state| + access_port1: access_port1, + access_port2: access_port2, + trunk_port1: trunk_port1, + trunk_port2: trunk_port2} + } end def handle_info(%ErrorMsg{code: code, type: type, data: data, xid: xid}, state) do :ok = warn("Request Failed(xid: #{xid}):"<> @@ -83,11 +105,18 @@ defmodule Heckle.Controller do " dpid: #{inspect(state.dpid)}") {:stop, :request_failed, state} end + def handle_info(%PacketIn{data: data}, state) do + :ok = warn("Table miss occured:"<> + " data: #{inspect(:pkt.decapsulate(data))}"<> + " dpid: #{inspect(state.dpid)}") + {:noreply, state} + end def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do :ok = debug("Switch Disconnected: dpid: #{inspect(state.dpid)}") {:stop, :normal, state} end - def handle_info(_info, state) do + def handle_info(info, state) do + :ok = info("Unhandled message: #{inspect(info)}") {:noreply, state} end @@ -101,7 +130,8 @@ defmodule Heckle.Controller do conn_ref: conn_ref, access_port1_name: config[:access_port1], access_port2_name: config[:access_port2], - trunk_port_name: config[:vlan_trunk], + trunk_port1_name: config[:vlan_trunk1], + trunk_port2_name: config[:vlan_trunk2], vlan_tagging: config[:vlan_tagging] || true, vlan_id: 0x1000 ||| (config[:vlan_id] || 0), receiver_mac: config[:receiver_mac], @@ -130,4 +160,42 @@ defmodule Heckle.Controller do defp set_config(dpid) do :ok = send_message(SetConfig.new(miss_send_len: :no_buffer), dpid) end + + defp print_flows(state) do + state + |> FlowPatterns.flows + |> Enum.each(&print_flow/1) + end + + defp print_flow(flow_opts) do + flow_opts + |> FlowMod.new + |> Openflow.to_binary + |> binary_to_space_delimited_hex + |> ofp_print_cmd + |> Logger.info + end + + defp ofp_print_cmd(print_args) do + {result, _code} = System.cmd("ovs-ofctl", ["ofp-print", "#{print_args}"]) + result + end + + defp binary_to_space_delimited_hex(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/examples/heckle/lib/heckle/flow_patterns.ex b/examples/heckle/lib/heckle/flow_patterns.ex index 2a8cad2..1c2e036 100644 --- a/examples/heckle/lib/heckle/flow_patterns.ex +++ b/examples/heckle/lib/heckle/flow_patterns.ex @@ -19,7 +19,7 @@ defmodule Heckle.FlowPatterns do ApplyActions.new([ SetField.new({:eth_dst, state.receiver_mac}), SetField.new({:ipv4_src, state.outside_local}), - Output.new(state.trunk_port.number) + Output.new(state.trunk_port2.number) ])] ], [table_id: 1, @@ -29,12 +29,15 @@ defmodule Heckle.FlowPatterns do eth_dst: state.sender_mac, eth_src: state.receiver_mac, eth_type: 0x0800, + vlan_vid: 123, ipv4_src: state.receiver_ip, ipv4_dst: state.outside_local ), instructions: [ ApplyActions.new([ + PopVlan.new, SetField.new({:eth_src, state.receiver_mac}), + SetField.new({:eth_dst, state.sender_mac}), SetField.new({:ipv4_dst, state.inside_local}), Output.new(state.access_port1.number) ])] @@ -51,7 +54,7 @@ defmodule Heckle.FlowPatterns do ), instructions: [ ApplyActions.new([ - Openflow.Action.Output.new(state.trunk_port.number), + Openflow.Action.Output.new(state.trunk_port1.number), Openflow.Action.PopVlan.new, Openflow.Action.Output.new(state.access_port1.number), Openflow.Action.Output.new(state.access_port2.number) @@ -67,23 +70,26 @@ defmodule Heckle.FlowPatterns do priority: 50, cookie: 0x1000000000000001, match: Match.new( - in_port: state.trunk_port.number, + in_port: state.trunk_port1.number, vlan_vid: state.vlan_id ), - instructions: [ - ApplyActions.new([ - PushVlan.new, - SetField.new({:vlan_vid, state.vlan_id}) - ]), - GotoTable.new(1) - ] + instructions: [GotoTable.new(1)] + ], + [table_id: 0, + priority: 50, + cookie: 0x1000000000000001, + match: Match.new( + in_port: state.trunk_port2.number, + vlan_vid: state.vlan_id + ), + instructions: [GotoTable.new(1)] ], [table_id: 0, priority: 20, cookie: 0x1000000000000001, match: Match.new( in_port: state.access_port1.number, - eth_src: state.sender_mac + eth_src: state.sender_mac, ), instructions: [ ApplyActions.new([ @@ -98,7 +104,7 @@ defmodule Heckle.FlowPatterns do cookie: 0x1000000000000001, match: Match.new( in_port: state.access_port2.number, - eth_src: state.sender_mac + eth_src: state.sender_mac, ), instructions: [ ApplyActions.new([ @@ -108,6 +114,12 @@ defmodule Heckle.FlowPatterns do GotoTable.new(1) ] ], + [table_id: 0, + priority: 1, + match: Match.new, + cookie: 0x1000000000000001, + instructions: [ApplyActions.new(Output.new(:controller))] + ], ] end end diff --git a/examples/heckle/mix.exs b/examples/heckle/mix.exs index ad0528a..06e195a 100644 --- a/examples/heckle/mix.exs +++ b/examples/heckle/mix.exs @@ -17,6 +17,7 @@ defmodule Heckle.Mixfile do end defp deps do - [{:tres, path: @tres_path}] + [{:tres, path: @tres_path}, + {:pkt, github: "msantos/pkt", branch: "master"}] end end diff --git a/examples/heckle/mix.lock b/examples/heckle/mix.lock index eeade00..e337173 100644 --- a/examples/heckle/mix.lock +++ b/examples/heckle/mix.lock @@ -1,5 +1,6 @@ %{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, + "pkt": {:git, "https://github.com/msantos/pkt.git", "3afb1967f34324c1dec5035a6e36232da815c2e6", [branch: "master"]}, "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"}, "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}} From 33d406a53011f298eae437d56bd65b4f8caa31c5 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 4 Dec 2017 20:17:58 +0900 Subject: [PATCH 26/50] Update README --- README.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 633bbbb..305c5c7 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,30 @@ -# Tres +# Tres - a Elixir OpenFlow development platform -**TODO: Add description** +## Overview + +Tres is a framework and set of helper libraries to develop OpenFlow controllers in Elixir. + +the purpose of this project is to design and prototype an experimental network switch controller that implements the OpenFlow 1.3.x. The project explores the scalability and robustness of such controllers on a scale much larger than typically considered: 100,000s of endpoints, 10,000s of switches. ## 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"} + {:tres, git: "https://gh.iiji.jp/Isono/tres", branch: "develop"} ] 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). +## callbacks + +```elixir +config :tres, + callback_module: Tres.ExampleHandler, + callback_args: [] +``` +To use `Tres.Controller` with your code, set the handler callback_module to your callback module. +This module must implement the `Module.start_link/2` that returns `on_start`. + +Set the callback_args to the terms you want pass to the `start_link/2` callback function. From fadfcd76bdf89a04f2b743dc154c0aac898b30e0 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 30 Jan 2018 22:38:54 +0900 Subject: [PATCH 27/50] Fix def function order of enum macro, and more human readable disconnected handler --- lib/openflow/enums.ex | 9 +++++- lib/openflow/features/reply.ex | 2 +- lib/openflow/meter_band/experimenter.ex | 2 +- lib/tres/example_handler.ex | 37 ++++++------------------- lib/tres/secure_channel.ex | 28 ++++++++++++++----- mix.exs | 2 -- 6 files changed, 39 insertions(+), 41 deletions(-) diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex index 4d43189..b29f59d 100644 --- a/lib/openflow/enums.ex +++ b/lib/openflow/enums.ex @@ -1071,6 +1071,7 @@ defmodule Openflow.Enums do 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") @@ -1091,6 +1092,7 @@ defmodule Openflow.Enums do end end end + def to_int(_int, unquote(String.to_atom(enum_name))) do throw(:bad_enum) end @@ -1101,9 +1103,14 @@ defmodule Openflow.Enums do for {key, value} <- enum_def do def unquote(to_int_fn_name)(unquote(key)), do: unquote(value) + end + + def unquote(to_int_fn_name)(_), do: throw(:bad_enum) + + for {key, value} <- enum_def do 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 diff --git a/lib/openflow/features/reply.ex b/lib/openflow/features/reply.ex index 1bac1a5..b22b384 100644 --- a/lib/openflow/features/reply.ex +++ b/lib/openflow/features/reply.ex @@ -6,7 +6,7 @@ defmodule Openflow.Features.Reply do n_buffers: 0, n_tables: 0, aux_id: 0, - capabilities: [], + capabilities: [] ) alias __MODULE__ diff --git a/lib/openflow/meter_band/experimenter.ex b/lib/openflow/meter_band/experimenter.ex index fa70d4d..8a73171 100644 --- a/lib/openflow/meter_band/experimenter.ex +++ b/lib/openflow/meter_band/experimenter.ex @@ -2,7 +2,7 @@ defmodule Openflow.MeterBand.Experimenter do defstruct( rate: 0, burst_size: 0, - experimenter: 0, + experimenter: 0 ) alias __MODULE__ diff --git a/lib/tres/example_handler.ex b/lib/tres/example_handler.ex index deaf722..ded3129 100644 --- a/lib/tres/example_handler.ex +++ b/lib/tres/example_handler.ex @@ -20,20 +20,13 @@ defmodule Tres.ExampleHandler do info("[#{__MODULE__}] Switch Ready: " <> "datapath_id: #{datapath_id} " <> "aux_id: #{aux_id} " - <> "in #{inspect(self())}") - _ = send_flows_for_test(datapath_id) - _ = send_flow_stats_request(datapath_id) + <> "on #{inspect(self())}") _ = send_desc_stats_request(datapath_id) _ = send_port_desc_stats_request(datapath_id) - conn_ref = SwitchRegistry.monitor(datapath_id) - state = %State{datapath_id: datapath_id, aux_id: aux_id, conn_ref: conn_ref} + state = %State{datapath_id: datapath_id, aux_id: aux_id} {:ok, state} end - def handle_info(%Flow.Reply{datapath_id: datapath_id} = desc, state) do - handle_flow_stats_reply(desc, datapath_id) - {:noreply, state} - end def handle_info(%PortDesc.Reply{datapath_id: datapath_id} = desc, state) do handle_port_desc_stats_reply(desc, datapath_id) {:noreply, state} @@ -42,13 +35,14 @@ defmodule Tres.ExampleHandler do handle_desc_stats_reply(desc, datapath_id) {:noreply, state} end - - # To prevent process leakage, following section is required. - def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do - :ok = warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id}") + def handle_info({:switch_disconnected, reason}, state) do + :ok = warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id} by #{reason}") {:stop, :normal, state} end - + def handle_info({:switch_hang, _datapath_id}, state) do + :ok = warn("[#{__MODULE__}] Switch possible hang: datapath_id: #{state.datapath_id}") + {:noreply, state} + end # `Catch all` function is required. def handle_info(info, state) do :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}: #{state.datapath_id}") @@ -57,17 +51,6 @@ defmodule Tres.ExampleHandler do # private functions - defp send_flows_for_test(datapath_id) do - for count <- Range.new(1, 1024) do - send_flow_mod_add(datapath_id, match: Match.new(metadata: count)) - end - end - - defp send_flow_stats_request(datapath_id) do - Flow.Request.new - |> send_message(datapath_id) - end - defp send_desc_stats_request(datapath_id) do Desc.Request.new |> send_message(datapath_id) @@ -78,10 +61,6 @@ defmodule Tres.ExampleHandler do |> send_message(datapath_id) end - defp handle_flow_stats_reply(desc, datapath_id) do - info("[#{__MODULE__}] Switch #{length(desc.flows)} installed on #{datapath_id}") - end - defp handle_desc_stats_reply(desc, datapath_id) do info( "[#{__MODULE__}] Switch Desc: " diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 1e3153a..78c3350 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -433,32 +433,46 @@ defmodule Tres.SecureChannel do warn("[#{__MODULE__}] connection terminated: Features handshake timed out") {:stop, :normal, %{state_data|socket: nil}} end - defp close_connection(:handler_error, state_data) do + defp close_connection(:handler_error = disconnected_reason, state_data) do warn("[#{__MODULE__}] connection terminated: Got handler error") + %State{handler_pid: handler_pid} = state_data + send(handler_pid, {:switch_disconnected, disconnected_reason}) {:stop, :normal, %{state_data|socket: nil}} end - defp close_connection(:ping_failed, state_data) do + defp close_connection(:ping_failed = disconnected_reason, state_data) do warn("[#{__MODULE__}] connection terminated: Exceeded to max_ping_fail_count") + %State{handler_pid: handler_pid} = state_data + send(handler_pid, {:switch_disconnected, disconnected_reason}) {:stop, :normal, %{state_data|socket: nil}} end - defp close_connection({:main_closed, reason}, state_data) do + defp close_connection({:main_closed = disconnected_reason, reason}, state_data) do warn("[#{__MODULE__}] connection terminated: Main connection down by #{reason}") + %State{handler_pid: handler_pid} = state_data + send(handler_pid, {:switch_disconnected, disconnected_reason}) {:stop, :normal, %{state_data|socket: nil}} end - defp close_connection({:handler_down, reason}, state_data) do + defp close_connection({:handler_down = disconnected_reason, reason}, state_data) do warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}") + %State{handler_pid: handler_pid} = state_data + send(handler_pid, {:switch_disconnected, disconnected_reason}) {:stop, :normal, %{state_data|socket: nil}} end - defp close_connection({:trap_detected, reason}, state_data) do + defp close_connection({:trap_detected = disconnected_reason, reason}, state_data) do warn("[#{__MODULE__}] connection terminated: Trapped by #{reason}") + %State{handler_pid: handler_pid} = state_data + send(handler_pid, {:switch_disconnected, disconnected_reason}) {:stop, :normal, %{state_data|socket: nil}} end - defp close_connection(:tcp_closed, state_data) do + defp close_connection(:tcp_closed = disconnected_reason, state_data) do warn("[#{__MODULE__}] connection terminated: TCP Closed by peer") + %State{handler_pid: handler_pid} = state_data + send(handler_pid, {:switch_disconnected, disconnected_reason}) {:stop, :normal, %{state_data|socket: nil}} end - defp close_connection({:tcp_error, reason}, state_data) do + defp close_connection({:tcp_error, reason} = disconnected_reason, state_data) do warn("[#{__MODULE__}] connection terminated: TCP Error occured: #{reason}") + %State{handler_pid: handler_pid} = state_data + send(handler_pid, {:switch_disconnected, disconnected_reason}) {:stop, :normal, %{state_data|socket: nil}} end end diff --git a/mix.exs b/mix.exs index 069e374..ffbf3d9 100644 --- a/mix.exs +++ b/mix.exs @@ -21,8 +21,6 @@ defmodule Tres.Mixfile do defp deps do [{:ranch, "~> 1.4.0"}, {:eovsdb, github: "shun159/eovsdb", branch: "master"}, - {:binpp, github: "jtendo/binpp", branch: "master"}, - {:pkt, github: "msantos/pkt", ref: "3afb196", only: :test, override: true}, {:epcap, github: "msantos/epcap", branch: "master", only: :test}] end end From 7635272fbda4e0844871633a08b7ddd8baf7704c Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Tue, 30 Jan 2018 22:47:31 +0900 Subject: [PATCH 28/50] Formatted --- .formatter.exs | 18 + config/config.exs | 8 +- lib/openflow.ex | 22 +- lib/openflow/action.ex | 10 +- lib/openflow/actions/experimenter.ex | 8 +- lib/openflow/actions/nx_bundle.ex | 78 +- lib/openflow/actions/nx_bundle_load.ex | 105 +- lib/openflow/actions/nx_clone.ex | 8 +- lib/openflow/actions/nx_conjunction.ex | 14 +- lib/openflow/actions/nx_conntrack.ex | 94 +- lib/openflow/actions/nx_controller.ex | 20 +- lib/openflow/actions/nx_controller2.ex | 124 +- lib/openflow/actions/nx_ct_clear.ex | 2 +- lib/openflow/actions/nx_dec_mpls_ttl.ex | 2 +- lib/openflow/actions/nx_dec_ttl.ex | 2 +- lib/openflow/actions/nx_dec_ttl_cnt_ids.ex | 13 +- lib/openflow/actions/nx_exit.ex | 2 +- lib/openflow/actions/nx_fin_timeout.ex | 8 +- lib/openflow/actions/nx_flow_spec.ex | 10 +- lib/openflow/actions/nx_flow_spec_load.ex | 66 +- lib/openflow/actions/nx_flow_spec_match.ex | 66 +- lib/openflow/actions/nx_flow_spec_output.ex | 22 +- lib/openflow/actions/nx_learn.ex | 81 +- lib/openflow/actions/nx_learn2.ex | 110 +- lib/openflow/actions/nx_multipath.ex | 90 +- lib/openflow/actions/nx_nat.ex | 74 +- lib/openflow/actions/nx_note.ex | 4 +- lib/openflow/actions/nx_output_reg.ex | 29 +- lib/openflow/actions/nx_output_reg2.ex | 23 +- lib/openflow/actions/nx_output_trunc.ex | 4 +- lib/openflow/actions/nx_pop_mpls.ex | 4 +- lib/openflow/actions/nx_pop_queue.ex | 6 +- lib/openflow/actions/nx_push_mpls.ex | 4 +- lib/openflow/actions/nx_reg_load.ex | 29 +- lib/openflow/actions/nx_reg_load2.ex | 20 +- lib/openflow/actions/nx_reg_move.ex | 59 +- lib/openflow/actions/nx_resubmit.ex | 6 +- lib/openflow/actions/nx_resubmit_table.ex | 11 +- lib/openflow/actions/nx_resubmit_table_ct.ex | 10 +- lib/openflow/actions/nx_sample.ex | 53 +- lib/openflow/actions/nx_sample2.ex | 61 +- lib/openflow/actions/nx_sample3.ex | 71 +- lib/openflow/actions/nx_set_mpls_label.ex | 4 +- lib/openflow/actions/nx_set_mpls_tc.ex | 4 +- lib/openflow/actions/nx_set_mpls_ttl.ex | 4 +- lib/openflow/actions/nx_set_queue.ex | 6 +- lib/openflow/actions/nx_set_tunnel.ex | 4 +- lib/openflow/actions/nx_set_tunnel64.ex | 6 +- lib/openflow/actions/nx_stack_pop.ex | 23 +- lib/openflow/actions/nx_stack_push.ex | 23 +- lib/openflow/actions/nx_write_metadata.ex | 13 +- lib/openflow/actions/output.ex | 1 + lib/openflow/actions/push_pbb.ex | 2 +- lib/openflow/actions/set_field.ex | 6 +- lib/openflow/barrier/reply.ex | 10 +- lib/openflow/barrier/request.ex | 10 +- lib/openflow/buckets.ex | 39 +- lib/openflow/echo/reply.ex | 12 +- lib/openflow/echo/request.ex | 13 +- lib/openflow/enums.ex | 1458 ++++++++--------- lib/openflow/error_msg.ex | 46 +- lib/openflow/experimenter.ex | 24 +- lib/openflow/features/reply.ex | 23 +- lib/openflow/features/request.ex | 10 +- lib/openflow/flow_mod.ex | 105 +- lib/openflow/flow_removed.ex | 60 +- lib/openflow/get_async/reply.ex | 60 +- lib/openflow/get_async/request.ex | 10 +- lib/openflow/get_config/reply.ex | 11 +- lib/openflow/get_config/request.ex | 10 +- lib/openflow/group_mod.ex | 17 +- lib/openflow/hello.ex | 47 +- lib/openflow/instruction.ex | 10 +- lib/openflow/instructions/experimenter.ex | 4 +- lib/openflow/instructions/write_metadata.ex | 4 +- lib/openflow/match.ex | 117 +- lib/openflow/match/field.ex | 991 +++++------ lib/openflow/meter_band.ex | 10 +- lib/openflow/meter_band/drop.ex | 2 +- lib/openflow/meter_band/experimenter.ex | 8 +- lib/openflow/meter_band/remark.ex | 2 +- lib/openflow/meter_mod.ex | 2 +- lib/openflow/multipart/aggregate/reply.ex | 15 +- lib/openflow/multipart/aggregate/request.ex | 84 +- lib/openflow/multipart/desc/reply.ex | 43 +- lib/openflow/multipart/desc/request.ex | 7 +- lib/openflow/multipart/flow/reply.ex | 85 +- lib/openflow/multipart/flow/request.ex | 86 +- lib/openflow/multipart/group/reply.ex | 75 +- lib/openflow/multipart/group/request.ex | 9 +- lib/openflow/multipart/group_desc/reply.ex | 33 +- lib/openflow/multipart/group_desc/request.ex | 7 +- .../multipart/group_features/reply.ex | 66 +- .../multipart/group_features/request.ex | 7 +- lib/openflow/multipart/meter/reply.ex | 73 +- lib/openflow/multipart/meter/request.ex | 9 +- .../multipart/meter_config/request.ex | 9 +- lib/openflow/multipart/port_desc/reply.ex | 28 +- lib/openflow/multipart/port_desc/request.ex | 7 +- lib/openflow/multipart/port_stats/reply.ex | 101 +- lib/openflow/multipart/port_stats/request.ex | 7 +- lib/openflow/multipart/queue/reply.ex | 64 +- lib/openflow/multipart/queue/request.ex | 9 +- lib/openflow/multipart/reply.ex | 4 +- lib/openflow/multipart/request.ex | 4 +- lib/openflow/multipart/table/reply.ex | 48 +- lib/openflow/multipart/table/request.ex | 7 +- lib/openflow/multipart/table_features/body.ex | 265 +-- .../multipart/table_features/reply.ex | 26 +- .../multipart/table_features/request.ex | 26 +- lib/openflow/nx_packet_in2.ex | 130 +- lib/openflow/nx_packet_in_format.ex | 6 +- lib/openflow/nx_set_controller_id.ex | 4 +- lib/openflow/packet_in.ex | 75 +- lib/openflow/packet_out.ex | 32 +- lib/openflow/port.ex | 49 +- lib/openflow/port_mod.ex | 39 +- lib/openflow/port_status.ex | 10 +- lib/openflow/role/reply.ex | 10 +- lib/openflow/role/request.ex | 10 +- lib/openflow/set_async.ex | 60 +- lib/openflow/set_config.ex | 11 +- lib/openflow/table_mod.ex | 10 +- lib/openflow/utils.ex | 22 +- lib/ovsdb/open_vswitch.ex | 119 +- lib/tres/application.ex | 14 +- lib/tres/example_handler.ex | 50 +- lib/tres/message_handler_sup.ex | 2 +- lib/tres/message_helper.ex | 52 +- lib/tres/secure_channel.ex | 300 ++-- lib/tres/secure_channel_state.ex | 57 +- lib/tres/switch_registry.ex | 4 +- lib/tres/utils.ex | 7 +- mix.exs | 27 +- test/flay.ex | 81 +- test/flog_test.exs | 938 ++++++----- test/ofp_action_test.exs | 364 ++-- test/ofp_echo_test.exs | 18 +- test/ofp_error_test.exs | 45 +- test/ofp_features_test.exs | 29 +- test/ofp_flow_mod_test.exs | 289 ++-- test/ofp_flow_removed_test.exs | 31 +- test/ofp_get_config_test.exs | 18 +- test/ofp_group_mod_test.exs | 16 +- test/ofp_hello_test.exs | 9 +- test/ofp_packet_in_test.exs | 114 +- test/ofp_packet_out_test.exs | 119 +- test/ofp_port_status_test.exs | 8 +- test/ofp_set_config_test.exs | 9 +- test/pf.ex | 33 +- 150 files changed, 5055 insertions(+), 4032 deletions(-) create mode 100644 .formatter.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..b874ba3 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,18 @@ +[ + inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"], + + locals_without_parens: [ + # Formatter tests + assert_format: 2, + assert_format: 3, + assert_same: 1, + assert_same: 2, + + # Errors tests + assert_eval_raise: 3, + + # Mix tests + in_fixture: 2, + in_tmp: 2 + ] +] diff --git a/config/config.exs b/config/config.exs index e8565e6..40896b3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -3,12 +3,12 @@ use Mix.Config config :tres, - protocol: :tcp, - port: 6653, + protocol: :tcp, + port: 6653, max_connections: 10, - num_acceptors: 10, + num_acceptors: 10, callback_module: Tres.ExampleHandler, - callback_args: [] + callback_args: [] config :logger, level: :info, diff --git a/lib/openflow.ex b/lib/openflow.ex index a0e5f0d..ac30ab8 100644 --- a/lib/openflow.ex +++ b/lib/openflow.ex @@ -5,23 +5,26 @@ defmodule Openflow do @spec read(binary()) :: {:ok, map()} | {:error, :binary_too_small} def read(binary) - when byte_size(binary) < @ofp_header_size do + 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 + 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) + result = + type + |> Openflow.Enums.to_atom(:openflow_codec) + |> do_read(body_bin) case result do - {:ok, struct} -> {:ok, %{struct|version: ver, xid: xid}, rest} + {:ok, struct} -> {:ok, %{struct | version: ver, xid: xid}, rest} {:error, reason} -> {:error, reason} end end @@ -30,19 +33,23 @@ defmodule Openflow do binaries = for message <- messages, do: to_binary(message) Enum.join(binaries, "") 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 def append_body(nil, message), do: message + def append_body(message, continue) do mod = message.__struct__ + if function_exported?(mod, :append_body, 2) do mod.append_body(message, continue) else @@ -55,6 +62,7 @@ defmodule Openflow do 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} diff --git a/lib/openflow/action.ex b/lib/openflow/action.ex index 2bccd6d..6ffbe57 100644 --- a/lib/openflow/action.ex +++ b/lib/openflow/action.ex @@ -1,5 +1,4 @@ defmodule Openflow.Action do - def read(action_bin) do do_read([], action_bin) end @@ -7,6 +6,7 @@ defmodule Openflow.Action do def to_binary(actions) when is_list(actions) do to_binary(<<>>, actions) end + def to_binary(action) do to_binary([action]) end @@ -15,15 +15,17 @@ defmodule Openflow.Action do 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) + do_read([codec.read(action_bin) | acc], rest) end defp to_binary(acc, []), do: acc - defp to_binary(acc, [action|rest]) do + + defp to_binary(acc, [action | rest]) do codec = action.__struct__ - to_binary(<>, rest) + to_binary(<>, rest) end end diff --git a/lib/openflow/actions/experimenter.ex b/lib/openflow/actions/experimenter.ex index 8390026..b2b7e99 100644 --- a/lib/openflow/actions/experimenter.ex +++ b/lib/openflow/actions/experimenter.ex @@ -5,7 +5,7 @@ defmodule Openflow.Action.Experimenter do @experimter_size 8 - def ofpat, do: 0xffff + def ofpat, do: 0xFFFF def new(exp_id, data \\ "") do %Experimenter{exp_id: exp_id, data: data} @@ -13,17 +13,19 @@ defmodule Openflow.Action.Experimenter do 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>> + <<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 + 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 diff --git a/lib/openflow/actions/nx_bundle.ex b/lib/openflow/actions/nx_bundle.ex index 8fcd5f0..da16230 100644 --- a/lib/openflow/actions/nx_bundle.ex +++ b/lib/openflow/actions/nx_bundle.ex @@ -1,11 +1,11 @@ defmodule Openflow.Action.NxBundle do defstruct( - algorithm: :active_backup, + algorithm: :active_backup, hash_field: :eth_src, - basis: 0, + basis: 0, slave_type: :nx_in_port, - n_slaves: 0, - slaves: [] + n_slaves: 0, + slaves: [] ) @experimenter 0x00002320 @@ -15,36 +15,44 @@ defmodule Openflow.Action.NxBundle do def new(options) do slaves = options[:slaves] || [] - %NxBundle{algorithm: options[:algorithm] || :active_backup, - hash_field: options[:hash_field] || :eth_src, - basis: options[:basis] || 0, - n_slaves: length(slaves), - slaves: slaves} + + %NxBundle{ + algorithm: options[:algorithm] || :active_backup, + hash_field: options[:hash_field] || :eth_src, + basis: options[:basis] || 0, + 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 + 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 = <> + + body = + <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> - exp_body_size = byte_size(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, 0::size(padding_length)-unit(8)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do - <> = body + <> = body + slave_len = n_slaves * 2 <> = rest alg = Openflow.Enums.to_atom(alg_int, :nx_bd_algorithm) @@ -52,23 +60,29 @@ defmodule Openflow.Action.NxBundle do 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} + + %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 + 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) diff --git a/lib/openflow/actions/nx_bundle_load.ex b/lib/openflow/actions/nx_bundle_load.ex index b9c4ef1..f213fe0 100644 --- a/lib/openflow/actions/nx_bundle_load.ex +++ b/lib/openflow/actions/nx_bundle_load.ex @@ -2,15 +2,15 @@ defmodule Openflow.Action.NxBundleLoad do import Bitwise defstruct( - algorithm: :active_backup, + algorithm: :active_backup, hash_field: :eth_src, - basis: 0, + basis: 0, slave_type: :nx_in_port, - n_slaves: 0, - slaves: [], - offset: 0, - n_bits: 0, - dst_field: nil + n_slaves: 0, + slaves: [], + offset: 0, + n_bits: 0, + dst_field: nil ) @experimenter 0x00002320 @@ -22,45 +22,52 @@ defmodule Openflow.Action.NxBundleLoad do dst_field = options[:dst_field] default_n_bits = Openflow.Match.Field.n_bits_of(dst_field) slaves = options[:slaves] || [] - %NxBundleLoad{algorithm: options[:algorithm] || :active_backup, - hash_field: options[:hash_field] || :eth_src, - basis: options[:basis] || 0, - n_slaves: length(slaves), - slaves: slaves, - offset: options[:offset] || 0, - n_bits: options[:n_bits] || default_n_bits, - dst_field: options[:dst_field]} + + %NxBundleLoad{ + algorithm: options[:algorithm] || :active_backup, + hash_field: options[:hash_field] || :eth_src, + basis: options[:basis] || 0, + n_slaves: length(slaves), + slaves: slaves, + offset: options[:offset] || 0, + n_bits: options[:n_bits] || default_n_bits, + dst_field: options[: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 + 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) + ofs_nbits = ofs <<< 6 ||| n_bits - 1 dst_field_bin = Openflow.Match.codec_header(dst_field) - body = <> + + body = + <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> - exp_body_size = byte_size(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, 0::size(padding_length)-unit(8)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do - <> = body + <> = body + slave_len = n_slaves * 2 <> = rest alg = Openflow.Enums.to_atom(alg_int, :nx_bd_algorithm) @@ -69,26 +76,32 @@ defmodule Openflow.Action.NxBundleLoad do 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} + + %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 + 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) diff --git a/lib/openflow/actions/nx_clone.ex b/lib/openflow/actions/nx_clone.ex index afa8618..971721b 100644 --- a/lib/openflow/actions/nx_clone.ex +++ b/lib/openflow/actions/nx_clone.ex @@ -1,7 +1,5 @@ defmodule Openflow.Action.NxClone do - defstruct( - actions: [] - ) + defstruct(actions: []) @experimenter 0x00002320 @nxast 42 @@ -15,10 +13,10 @@ defmodule Openflow.Action.NxClone do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_conjunction.ex b/lib/openflow/actions/nx_conjunction.ex index b9818d6..be33f2a 100644 --- a/lib/openflow/actions/nx_conjunction.ex +++ b/lib/openflow/actions/nx_conjunction.ex @@ -1,8 +1,8 @@ defmodule Openflow.Action.NxConjunction do defstruct( - clause: 0, + clause: 0, n_clauses: 0, - id: 0 + id: 0 ) @experimenter 0x00002320 @@ -11,14 +11,16 @@ defmodule Openflow.Action.NxConjunction do alias __MODULE__ def new(options) do - %NxConjunction{clause: options[:clause] || 0, - n_clauses: options[:n_clauses] || 0, - id: options[:id] || 0} + %NxConjunction{ + clause: options[:clause] || 0, + n_clauses: options[:n_clauses] || 0, + id: options[:id] || 0 + } 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>> + <<0xFFFF::16, 16::16, exp_body::bytes>> end def read(<<@experimenter::32, @nxast::16, clause::8, n_clauses::8, id::32>>) do diff --git a/lib/openflow/actions/nx_conntrack.ex b/lib/openflow/actions/nx_conntrack.ex index 4c4dafc..7844221 100644 --- a/lib/openflow/actions/nx_conntrack.ex +++ b/lib/openflow/actions/nx_conntrack.ex @@ -2,14 +2,14 @@ defmodule Openflow.Action.NxConntrack do import Bitwise defstruct( - flags: [], - zone_src: nil, - zone_imm: 0, - zone_offset: nil, - zone_n_bits: nil, + flags: [], + zone_src: nil, + zone_imm: 0, + zone_offset: nil, + zone_n_bits: nil, recirc_table: 255, - alg: 0, - exec: [] + alg: 0, + exec: [] ) @experimenter 0x00002320 @@ -18,61 +18,73 @@ defmodule Openflow.Action.NxConntrack do alias __MODULE__ def new(options \\ []) do - %NxConntrack{flags: options[:flags] || [], - zone_src: options[:zone_src], - zone_imm: options[:zone_imm] || 0, - zone_offset: options[:zone_offset], - zone_n_bits: options[:zone_n_bits], - recirc_table: options[:recirc_table] || 255, - alg: options[:alg] || 0, - exec: options[:exec] || []} + %NxConntrack{ + flags: options[:flags] || [], + zone_src: options[:zone_src], + zone_imm: options[:zone_imm] || 0, + zone_offset: options[:zone_offset], + zone_n_bits: options[:zone_n_bits], + recirc_table: options[:recirc_table] || 255, + alg: options[:alg] || 0, + exec: options[: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, + 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 + 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 + + {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) + + 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)>> + <<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 + 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, + flags: flags, recirc_table: recirc_table, - alg: alg, - exec: exec + alg: alg, + exec: exec } + case src_bin do <<0::32>> -> <> = ofs_nbits - %{ct|zone_imm: zone_imm} + %{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} + %{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 index 4faf769..2f2d1fc 100644 --- a/lib/openflow/actions/nx_controller.ex +++ b/lib/openflow/actions/nx_controller.ex @@ -11,19 +11,27 @@ defmodule Openflow.Action.NxController do alias __MODULE__ def new(options) do - %NxController{max_len: options[:max_len] || :no_buffer, - id: options[:id] || 0, - reason: options[:reason] || :action} + %NxController{ + max_len: options[:max_len] || :no_buffer, + id: options[:id] || 0, + reason: options[:reason] || :action + } 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>> + + 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 + 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} diff --git a/lib/openflow/actions/nx_controller2.ex b/lib/openflow/actions/nx_controller2.ex index 5b932e7..78fb9d5 100644 --- a/lib/openflow/actions/nx_controller2.ex +++ b/lib/openflow/actions/nx_controller2.ex @@ -1,10 +1,10 @@ defmodule Openflow.Action.NxController2 do defstruct( - max_len: :no_buffer, - id: 0, - reason: :action, + max_len: :no_buffer, + id: 0, + reason: :action, userdata: "", - pause: false + pause: false ) @experimenter 0x00002320 @@ -12,20 +12,22 @@ defmodule Openflow.Action.NxController2 do @prop_header_size 4 - @prop_max_len 0 - @prop_ctl_id 1 - @prop_reason 2 + @prop_max_len 0 + @prop_ctl_id 1 + @prop_reason 2 @prop_userdata 3 - @prop_pause 4 + @prop_pause 4 alias __MODULE__ def new(options) do - %NxController2{max_len: options[:max_len] || :no_buffer, - id: options[:id] || 0, - reason: options[:reason] || :action, - userdata: options[:userdata], - pause: options[:pause] || false} + %NxController2{ + max_len: options[:max_len] || :no_buffer, + id: options[:id] || 0, + reason: options[:reason] || :action, + userdata: options[:userdata], + pause: options[:pause] || false + } end def to_binary(%NxController2{} = ctl) do @@ -33,10 +35,10 @@ defmodule Openflow.Action.NxController2 do prop_keys = get_prop_key(ctl) props_bin = encode_prop("", prop_keys, ctl) exp_body = <> - exp_body_size = byte_size(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>> + <<0xFFFF::16, length::16, exp_body::bytes>> end def read(<<@experimenter::32, @nxast::16, 0::size(6)-unit(8), body::bytes>>) do @@ -48,64 +50,88 @@ defmodule Openflow.Action.NxController2 do 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) + |> 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 + + 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 + + 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 + <<@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 + <<@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 + <<@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 + + <> = + 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) diff --git a/lib/openflow/actions/nx_ct_clear.ex b/lib/openflow/actions/nx_ct_clear.ex index 32f0810..f58d3b3 100644 --- a/lib/openflow/actions/nx_ct_clear.ex +++ b/lib/openflow/actions/nx_ct_clear.ex @@ -12,7 +12,7 @@ defmodule Openflow.Action.NxCtClear do 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>> + <<0xFFFF::16, 16::16, exp_body::bytes>> end def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do diff --git a/lib/openflow/actions/nx_dec_mpls_ttl.ex b/lib/openflow/actions/nx_dec_mpls_ttl.ex index 8b2c0f1..6b5744d 100644 --- a/lib/openflow/actions/nx_dec_mpls_ttl.ex +++ b/lib/openflow/actions/nx_dec_mpls_ttl.ex @@ -12,7 +12,7 @@ defmodule Openflow.Action.NxDecMplsTtl do def to_binary(%NxDecMplsTtl{}) do exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>> - <<0xffff::16, 16::16, exp_body::bytes>> + <<0xFFFF::16, 16::16, exp_body::bytes>> end def read(<<@experimenter::32, @nxast::16, _::size(6)-unit(8)>>) do diff --git a/lib/openflow/actions/nx_dec_ttl.ex b/lib/openflow/actions/nx_dec_ttl.ex index 7d949b8..de6b088 100644 --- a/lib/openflow/actions/nx_dec_ttl.ex +++ b/lib/openflow/actions/nx_dec_ttl.ex @@ -12,7 +12,7 @@ defmodule Openflow.Action.NxDecTtl do def to_binary(%NxDecTtl{}) do exp_body = <<@experimenter::32, @nxast::16, 0::size(6)-unit(8)>> - <<0xffff::16, 16::16, exp_body::bytes>> + <<0xFFFF::16, 16::16, exp_body::bytes>> end def read(<<@experimenter::32, @nxast::16, _::16, _::size(4)-unit(8)>>) do diff --git a/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex b/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex index 6704e75..729b705 100644 --- a/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex +++ b/lib/openflow/actions/nx_dec_ttl_cnt_ids.ex @@ -12,14 +12,17 @@ defmodule Openflow.Action.NxDecTtlCntIds do def to_binary(%NxDecTtlCntIds{ids: ids}) do n_controllers = length(ids) - ids_bin = Enum.join((for id <- ids, do: <>), "") + 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) + + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_exit.ex b/lib/openflow/actions/nx_exit.ex index 842dd77..cc9786b 100644 --- a/lib/openflow/actions/nx_exit.ex +++ b/lib/openflow/actions/nx_exit.ex @@ -12,7 +12,7 @@ defmodule Openflow.Action.NxExit do def to_binary(%NxExit{}) do exp_body = <<@experimenter::32, @nxast::16, 0::48>> - <<0xffff::16, 16::16, exp_body::bytes>> + <<0xFFFF::16, 16::16, exp_body::bytes>> end def read(<<@experimenter::32, @nxast::16, 0::48>>) do diff --git a/lib/openflow/actions/nx_fin_timeout.ex b/lib/openflow/actions/nx_fin_timeout.ex index a94a4dd..8f8e005 100644 --- a/lib/openflow/actions/nx_fin_timeout.ex +++ b/lib/openflow/actions/nx_fin_timeout.ex @@ -10,13 +10,15 @@ defmodule Openflow.Action.NxFinTimeout do alias __MODULE__ def new(options) do - %NxFinTimeout{idle_timeout: options[:idle_timeout] || 0, - hard_timeout: options[:hard_timeout] || 0} + %NxFinTimeout{ + idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0 + } 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)>> + <<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 diff --git a/lib/openflow/actions/nx_flow_spec.ex b/lib/openflow/actions/nx_flow_spec.ex index 62a56b3..b865cc7 100644 --- a/lib/openflow/actions/nx_flow_spec.ex +++ b/lib/openflow/actions/nx_flow_spec.ex @@ -1,5 +1,4 @@ defmodule Openflow.Action.NxFlowSpec do - def read(flow_spec_bin) do do_read([], flow_spec_bin) end @@ -7,6 +6,7 @@ defmodule Openflow.Action.NxFlowSpec do 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 @@ -15,15 +15,17 @@ defmodule Openflow.Action.NxFlowSpec do 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) + do_read([flow_spec | acc], rest) end defp to_binary(acc, []), do: acc - defp to_binary(acc, [flow_spec|rest]) do + + defp to_binary(acc, [flow_spec | rest]) do codec = flow_spec.__struct__ - to_binary(<>, rest) + 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 index 5b35f20..dcdbf86 100644 --- a/lib/openflow/actions/nx_flow_spec_load.ex +++ b/lib/openflow/actions/nx_flow_spec_load.ex @@ -2,67 +2,80 @@ defmodule Openflow.Action.NxFlowSpecLoad do defstruct( src: nil, dst: nil, - n_bits: 0, + n_bits: 0, src_offset: 0, dst_offset: 0 ) - @learn_src_field 0 + @learn_src_field 0 @learn_src_immediate 1 - @learn_dst 1 + @learn_dst 1 alias __MODULE__ def new(options) do dst = options[:dst] n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst) - %NxFlowSpecLoad{src: options[:src], - dst: dst, - n_bits: n_bits, - src_offset: options[:src_offset] || 0, - dst_offset: options[:dst_offset] || 0} + + %NxFlowSpecLoad{ + src: options[:src], + dst: dst, + n_bits: n_bits, + src_offset: options[:src_offset] || 0, + dst_offset: options[:dst_offset] || 0 + } end def to_binary(%NxFlowSpecLoad{} = fsm) do %NxFlowSpecLoad{ - dst: dst_field, - n_bits: n_bits, + 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>> + <<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>> + <<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 + 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 = %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 + + <> = + 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 = %NxFlowSpecLoad{src: src, dst: dst, n_bits: n_bits, dst_offset: dst_ofs} {flow_spec, rest} end @@ -72,6 +85,7 @@ defmodule Openflow.Action.NxFlowSpecLoad 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} diff --git a/lib/openflow/actions/nx_flow_spec_match.ex b/lib/openflow/actions/nx_flow_spec_match.ex index c8e2e1c..feae23d 100644 --- a/lib/openflow/actions/nx_flow_spec_match.ex +++ b/lib/openflow/actions/nx_flow_spec_match.ex @@ -2,67 +2,80 @@ defmodule Openflow.Action.NxFlowSpecMatch do defstruct( src: nil, dst: nil, - n_bits: 0, + n_bits: 0, src_offset: 0, dst_offset: 0 ) - @learn_src_field 0 + @learn_src_field 0 @learn_src_immediate 1 - @learn_dst 0 + @learn_dst 0 alias __MODULE__ def new(options) do dst = options[:dst] n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(dst) - %NxFlowSpecMatch{src: options[:src], - dst: dst, - n_bits: n_bits, - src_offset: options[:src_offset] || 0, - dst_offset: options[:dst_offset] || 0} + + %NxFlowSpecMatch{ + src: options[:src], + dst: dst, + n_bits: n_bits, + src_offset: options[:src_offset] || 0, + dst_offset: options[:dst_offset] || 0 + } end def to_binary(%NxFlowSpecMatch{} = fsm) do %NxFlowSpecMatch{ - dst: dst_field, - n_bits: n_bits, + 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>> + <<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>> + <<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 + 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 = %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 + + <> = + 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 = %NxFlowSpecMatch{src: src, dst: dst, n_bits: n_bits, dst_offset: dst_ofs} {flow_spec, rest} end @@ -72,6 +85,7 @@ defmodule Openflow.Action.NxFlowSpecMatch 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} diff --git a/lib/openflow/actions/nx_flow_spec_output.ex b/lib/openflow/actions/nx_flow_spec_output.ex index 75ad422..7f0500b 100644 --- a/lib/openflow/actions/nx_flow_spec_output.ex +++ b/lib/openflow/actions/nx_flow_spec_output.ex @@ -5,32 +5,28 @@ defmodule Openflow.Action.NxFlowSpecOutput do src_offset: 0 ) - @learn_src_field 0 - @learn_dst 2 + @learn_src_field 0 + @learn_dst 2 alias __MODULE__ def new(options) do src = options[:src] n_bits = options[:n_bits] || Openflow.Match.Field.n_bits_of(src) - %NxFlowSpecOutput{n_bits: n_bits, - src: src, - src_offset: options[:src_offset] || 0} + %NxFlowSpecOutput{n_bits: n_bits, src: src, src_offset: options[:src_offset] || 0} end - def to_binary(%NxFlowSpecOutput{n_bits: n_bits, - src: src, - src_offset: src_ofs}) do + 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 + 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 = %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 index b57f380..04b6cf3 100644 --- a/lib/openflow/actions/nx_learn.ex +++ b/lib/openflow/actions/nx_learn.ex @@ -17,53 +17,60 @@ defmodule Openflow.Action.NxLearn do alias __MODULE__ def new(options) do - %NxLearn{idle_timeout: options[:idle_timeout] || 0, - hard_timeout: options[:hard_timeout] || 0, - priority: options[:priority] || 0, - cookie: options[:cookie] || 0, - flags: options[:flags] || [], - table_id: options[:table_id] || 0xff, - fin_idle_timeout: options[:fin_idle_timeout] || 0, - fin_hard_timeout: options[:fin_hard_timeout] || 0, - flow_specs: options[:flow_specs] || []} + %NxLearn{ + idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0, + priority: options[:priority] || 0, + cookie: options[:cookie] || 0, + flags: options[:flags] || [], + table_id: options[:table_id] || 0xFF, + fin_idle_timeout: options[:fin_idle_timeout] || 0, + fin_hard_timeout: options[:fin_hard_timeout] || 0, + flow_specs: options[: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 + 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) + + 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)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, body::bitstring>>) do - <> = body + <> = 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} + + %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 index fac6e77..ea3a49d 100644 --- a/lib/openflow/actions/nx_learn2.ex +++ b/lib/openflow/actions/nx_learn2.ex @@ -20,65 +20,77 @@ defmodule Openflow.Action.NxLearn2 do alias __MODULE__ def new(options) do - %NxLearn2{idle_timeout: options[:idle_timeout] || 0, - hard_timeout: options[:hard_timeout] || 0, - priority: options[:priority] || 0, - cookie: options[:cookie] || 0, - flags: options[:flags] || [], - table_id: options[:table_id] || 0xff, - fin_idle_timeout: options[:fin_idle_timeout] || 0, - fin_hard_timeout: options[:fin_hard_timeout] || 0, - limit: options[:limit] || 0, - result_dst_offset: options[:result_dst_offset] || 0, - result_dst: options[:result_dst], - flow_specs: options[:flow_specs] || []} + %NxLearn2{ + idle_timeout: options[:idle_timeout] || 0, + hard_timeout: options[:hard_timeout] || 0, + priority: options[:priority] || 0, + cookie: options[:cookie] || 0, + flags: options[:flags] || [], + table_id: options[:table_id] || 0xFF, + fin_idle_timeout: options[:fin_idle_timeout] || 0, + fin_hard_timeout: options[:fin_hard_timeout] || 0, + limit: options[:limit] || 0, + result_dst_offset: options[:result_dst_offset] || 0, + result_dst: options[:result_dst], + flow_specs: options[: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 + 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 + + 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) + + 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)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do - <> = body + <> = 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} + + 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 diff --git a/lib/openflow/actions/nx_multipath.ex b/lib/openflow/actions/nx_multipath.ex index 86c45c9..72975e0 100644 --- a/lib/openflow/actions/nx_multipath.ex +++ b/lib/openflow/actions/nx_multipath.ex @@ -3,13 +3,13 @@ defmodule Openflow.Action.NxMultipath do defstruct( hash_field: :eth_src, - basis: 0, - algorithm: :modulo_n, - max_link: 0, - argument: 0, - offset: 0, - n_bits: 0, - dst_field: nil + basis: 0, + algorithm: :modulo_n, + max_link: 0, + argument: 0, + offset: 0, + n_bits: 0, + dst_field: nil ) @experimenter 0x00002320 @@ -27,52 +27,62 @@ defmodule Openflow.Action.NxMultipath do 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} + + %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 + 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 = <> + ofs_nbits = ofs <<< 6 ||| n_bits - 1 + + body = + <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> - exp_body_size = byte_size(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, 0::size(padding_length)-unit(8)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do - <> = body + <> = 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} + + %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 index a8af26d..921d5f1 100644 --- a/lib/openflow/actions/nx_nat.ex +++ b/lib/openflow/actions/nx_nat.ex @@ -1,10 +1,10 @@ defmodule Openflow.Action.NxNat do defstruct( - flags: [], - ipv4_min: nil, - ipv4_max: nil, - ipv6_min: nil, - ipv6_max: nil, + flags: [], + ipv4_min: nil, + ipv4_max: nil, + ipv6_min: nil, + ipv6_max: nil, proto_min: nil, proto_max: nil ) @@ -22,30 +22,38 @@ defmodule Openflow.Action.NxNat do 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} + + %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) + + 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)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do @@ -59,33 +67,43 @@ defmodule Openflow.Action.NxNat do defp get_ranges(nat) do nat - |> Map.from_struct + |> 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) + |> 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 + + 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} -> + {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 + ) + "" -> 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) -> + proto when is_integer(proto) and proto in 1..0xFFFF -> encode_ranges(<>, rest, nat) + _ -> encode_ranges(<>, rest, nat) end @@ -93,26 +111,32 @@ defmodule Openflow.Action.NxNat do end defp decode_ranges(nat, [], _), do: nat - defp decode_ranges(nat, [range|ranges], bin) do + + 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) -> + <> when proto in 1..0xFFFF -> decode_ranges(struct(nat, %{range => proto}), ranges, rest) + rest -> decode_ranges(struct(nat, %{range => ""}), ranges, rest) end diff --git a/lib/openflow/actions/nx_note.ex b/lib/openflow/actions/nx_note.ex index 2e281fe..b96847d 100644 --- a/lib/openflow/actions/nx_note.ex +++ b/lib/openflow/actions/nx_note.ex @@ -13,10 +13,10 @@ defmodule Openflow.Action.NxNote do 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) + 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)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, note_bin::bytes>>) do diff --git a/lib/openflow/actions/nx_output_reg.ex b/lib/openflow/actions/nx_output_reg.ex index f94c130..852358a 100644 --- a/lib/openflow/actions/nx_output_reg.ex +++ b/lib/openflow/actions/nx_output_reg.ex @@ -2,10 +2,10 @@ defmodule Openflow.Action.NxOutputReg do import Bitwise defstruct( - n_bits: 0, - offset: 0, - src_field: nil, - max_len: :no_buffer + n_bits: 0, + offset: 0, + src_field: nil, + max_len: :no_buffer ) @experimenter 0x00002320 @@ -19,34 +19,25 @@ defmodule Openflow.Action.NxOutputReg do 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} + %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 + 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) + 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) + 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)>> + <<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} + %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 index d5c88c7..e254cf3 100644 --- a/lib/openflow/actions/nx_output_reg2.ex +++ b/lib/openflow/actions/nx_output_reg2.ex @@ -2,10 +2,10 @@ defmodule Openflow.Action.NxOutputReg2 do import Bitwise defstruct( - n_bits: 0, - offset: 0, - src_field: nil, - max_len: :no_buffer + n_bits: 0, + offset: 0, + src_field: nil, + max_len: :no_buffer ) @experimenter 0x00002320 @@ -24,28 +24,31 @@ defmodule Openflow.Action.NxOutputReg2 do 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) + 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) + 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)>> + <<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(<>) + + 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 + 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 index d8289b2..98b3c6e 100644 --- a/lib/openflow/actions/nx_output_trunc.ex +++ b/lib/openflow/actions/nx_output_trunc.ex @@ -18,10 +18,10 @@ defmodule Openflow.Action.NxOutputTrunc do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_pop_mpls.ex b/lib/openflow/actions/nx_pop_mpls.ex index 7aef5b0..f14f863 100644 --- a/lib/openflow/actions/nx_pop_mpls.ex +++ b/lib/openflow/actions/nx_pop_mpls.ex @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxPopMpls do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_pop_queue.ex b/lib/openflow/actions/nx_pop_queue.ex index bda8339..4821cd3 100644 --- a/lib/openflow/actions/nx_pop_queue.ex +++ b/lib/openflow/actions/nx_pop_queue.ex @@ -3,7 +3,7 @@ defmodule Openflow.Action.NxPopQueue do @experimenter 0x00002320 @nxast 5 - + alias __MODULE__ def new do @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxPopQueue do def to_binary(%NxPopQueue{}) do exp_body = <<@experimenter::32, @nxast::16, 0::48>> - exp_body_size = byte_size(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, 0::size(padding_length)-unit(8)>> + <<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 diff --git a/lib/openflow/actions/nx_push_mpls.ex b/lib/openflow/actions/nx_push_mpls.ex index 241d6b3..4db52b0 100644 --- a/lib/openflow/actions/nx_push_mpls.ex +++ b/lib/openflow/actions/nx_push_mpls.ex @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxPushMpls do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_reg_load.ex b/lib/openflow/actions/nx_reg_load.ex index ae5a6ac..1ee7da5 100644 --- a/lib/openflow/actions/nx_reg_load.ex +++ b/lib/openflow/actions/nx_reg_load.ex @@ -2,10 +2,10 @@ defmodule Openflow.Action.NxRegLoad do import Bitwise defstruct( - n_bits: 0, - offset: 0, - dst_field: nil, - value: nil + n_bits: 0, + offset: 0, + dst_field: nil, + value: nil ) @experimenter 0x00002320 @@ -19,36 +19,27 @@ defmodule Openflow.Action.NxRegLoad do 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} + %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 + 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) + ofs_nbits = ofs <<< 6 ||| n_bits - 1 body = <> exp_body = <<@experimenter::32, @nxast::16, body::bytes>> - exp_body_size = byte_size(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, 0::size(padding_length)-unit(8)>> + <<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} + %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 index 00d4cf6..0c1510e 100644 --- a/lib/openflow/actions/nx_reg_load2.ex +++ b/lib/openflow/actions/nx_reg_load2.ex @@ -1,7 +1,7 @@ defmodule Openflow.Action.NxRegLoad2 do defstruct( - dst_field: nil, - value: nil + dst_field: nil, + value: nil ) @experimenter 0x00002320 @@ -18,23 +18,27 @@ defmodule Openflow.Action.NxRegLoad2 do def to_binary(%NxRegLoad2{dst_field: dst_field, value: value}) do match_bin = [{dst_field, value}] - |> Openflow.Match.new - |> Openflow.Match.to_binary + |> 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) + + 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)>> + <<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) + {[{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 index f50a3f7..309fd38 100644 --- a/lib/openflow/actions/nx_reg_move.ex +++ b/lib/openflow/actions/nx_reg_move.ex @@ -1,10 +1,10 @@ defmodule Openflow.Action.NxRegMove do defstruct( - n_bits: 0, + n_bits: 0, src_offset: 0, dst_offset: 0, - src_field: nil, - dst_field: nil + src_field: nil, + dst_field: nil ) @experimenter 0x00002320 @@ -19,38 +19,49 @@ defmodule Openflow.Action.NxRegMove do 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} + + %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 + 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 = <> + + body = + <> + exp_body = <<@experimenter::32, @nxast::16, body::bytes>> - exp_body_size = byte_size(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, 0::size(padding_length)-unit(8)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, body::bytes>>) do - <> = body + <> = + 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} + + %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 index 4167a19..9ed1623 100644 --- a/lib/openflow/actions/nx_resubmit.ex +++ b/lib/openflow/actions/nx_resubmit.ex @@ -1,5 +1,5 @@ defmodule Openflow.Action.NxResubmit do - defstruct([in_port: :in_port]) + defstruct(in_port: :in_port) @experimenter 0x00002320 @nxast 1 @@ -13,10 +13,10 @@ defmodule Openflow.Action.NxResubmit do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_resubmit_table.ex b/lib/openflow/actions/nx_resubmit_table.ex index 663ad28..04bbba8 100644 --- a/lib/openflow/actions/nx_resubmit_table.ex +++ b/lib/openflow/actions/nx_resubmit_table.ex @@ -1,5 +1,5 @@ defmodule Openflow.Action.NxResubmitTable do - defstruct([in_port: :in_port, table_id: :all]) + defstruct(in_port: :in_port, table_id: :all) @experimenter 0x00002320 @nxast 14 @@ -9,6 +9,7 @@ defmodule Openflow.Action.NxResubmitTable do 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) @@ -19,13 +20,15 @@ defmodule Openflow.Action.NxResubmitTable 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) + 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)>> + <<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 + 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} diff --git a/lib/openflow/actions/nx_resubmit_table_ct.ex b/lib/openflow/actions/nx_resubmit_table_ct.ex index 01398e1..849640e 100644 --- a/lib/openflow/actions/nx_resubmit_table_ct.ex +++ b/lib/openflow/actions/nx_resubmit_table_ct.ex @@ -16,13 +16,15 @@ defmodule Openflow.Action.NxResubmitTableCt 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) + 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 + <<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 + 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} diff --git a/lib/openflow/actions/nx_sample.ex b/lib/openflow/actions/nx_sample.ex index b133e38..206c0ed 100644 --- a/lib/openflow/actions/nx_sample.ex +++ b/lib/openflow/actions/nx_sample.ex @@ -1,9 +1,9 @@ defmodule Openflow.Action.NxSample do defstruct( - probability: 0, + probability: 0, collector_set_id: 0, - obs_domain_id: 0, - obs_point_id: 0 + obs_domain_id: 0, + obs_point_id: 0 ) @experimenter 0x00002320 @@ -16,29 +16,40 @@ defmodule Openflow.Action.NxSample do 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} + + %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) + 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)>> + <<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} + 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 index fd15c5b..271a3c6 100644 --- a/lib/openflow/actions/nx_sample2.ex +++ b/lib/openflow/actions/nx_sample2.ex @@ -1,10 +1,10 @@ defmodule Openflow.Action.NxSample2 do defstruct( - probability: 0, + probability: 0, collector_set_id: 0, - obs_domain_id: 0, - obs_point_id: 0, - sampling_port: 0 + obs_domain_id: 0, + obs_point_id: 0, + sampling_port: 0 ) @experimenter 0x00002320 @@ -18,32 +18,43 @@ defmodule Openflow.Action.NxSample2 do 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} + + %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) + 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)>> + <<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} + 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 index f7e10ac..bb1c531 100644 --- a/lib/openflow/actions/nx_sample3.ex +++ b/lib/openflow/actions/nx_sample3.ex @@ -1,11 +1,11 @@ defmodule Openflow.Action.NxSample3 do defstruct( - probability: 0, + probability: 0, collector_set_id: 0, - obs_domain_id: 0, - obs_point_id: 0, - sampling_port: 0, - direction: :default + obs_domain_id: 0, + obs_point_id: 0, + sampling_port: 0, + direction: :default ) @experimenter 0x00002320 @@ -20,37 +20,50 @@ defmodule Openflow.Action.NxSample3 do 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} + + %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 + 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) + + 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)>> + <<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 + 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} + + %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 index 99436da..9fdcf04 100644 --- a/lib/openflow/actions/nx_set_mpls_label.ex +++ b/lib/openflow/actions/nx_set_mpls_label.ex @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxSetMplsLabel do def to_binary(%NxSetMplsLabel{label: label}) do exp_body = <<@experimenter::32, @nxast::16, 0::16, label::32>> - exp_body_size = byte_size(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, 0::size(padding_length)-unit(8)>> + <<0xFFFF::16, length::16, exp_body::bytes, 0::size(padding_length)-unit(8)>> end def read(<<@experimenter::32, @nxast::16, _::16, label::32>>) do diff --git a/lib/openflow/actions/nx_set_mpls_tc.ex b/lib/openflow/actions/nx_set_mpls_tc.ex index 16414f2..9392558 100644 --- a/lib/openflow/actions/nx_set_mpls_tc.ex +++ b/lib/openflow/actions/nx_set_mpls_tc.ex @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxSetMplsTc do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_set_mpls_ttl.ex b/lib/openflow/actions/nx_set_mpls_ttl.ex index 929398f..cd932af 100644 --- a/lib/openflow/actions/nx_set_mpls_ttl.ex +++ b/lib/openflow/actions/nx_set_mpls_ttl.ex @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxSetMplsTtl do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_set_queue.ex b/lib/openflow/actions/nx_set_queue.ex index d133276..845c494 100644 --- a/lib/openflow/actions/nx_set_queue.ex +++ b/lib/openflow/actions/nx_set_queue.ex @@ -1,5 +1,5 @@ defmodule Openflow.Action.NxSetQueue do - defstruct([queue_id: 0]) + defstruct(queue_id: 0) @experimenter 0x00002320 @nxast 4 @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxSetQueue do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_set_tunnel.ex b/lib/openflow/actions/nx_set_tunnel.ex index 0ed3d14..661114b 100644 --- a/lib/openflow/actions/nx_set_tunnel.ex +++ b/lib/openflow/actions/nx_set_tunnel.ex @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxSetTunnel do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_set_tunnel64.ex b/lib/openflow/actions/nx_set_tunnel64.ex index c70b230..d729689 100644 --- a/lib/openflow/actions/nx_set_tunnel64.ex +++ b/lib/openflow/actions/nx_set_tunnel64.ex @@ -1,5 +1,5 @@ defmodule Openflow.Action.NxSetTunnel64 do - defstruct([tunnel_id: 0]) + defstruct(tunnel_id: 0) @experimenter 0x00002320 @nxast 9 @@ -12,10 +12,10 @@ defmodule Openflow.Action.NxSetTunnel64 do 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/nx_stack_pop.ex b/lib/openflow/actions/nx_stack_pop.ex index c106678..479932c 100644 --- a/lib/openflow/actions/nx_stack_pop.ex +++ b/lib/openflow/actions/nx_stack_pop.ex @@ -1,8 +1,8 @@ defmodule Openflow.Action.NxStackPop do defstruct( - n_bits: 0, - offset: 0, - field: nil + n_bits: 0, + offset: 0, + field: nil ) @experimenter 0x00002320 @@ -20,16 +20,21 @@ defmodule Openflow.Action.NxStackPop do 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) + + 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)>> + <<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 + 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 diff --git a/lib/openflow/actions/nx_stack_push.ex b/lib/openflow/actions/nx_stack_push.ex index 21dc9c0..aa33e71 100644 --- a/lib/openflow/actions/nx_stack_push.ex +++ b/lib/openflow/actions/nx_stack_push.ex @@ -1,8 +1,8 @@ defmodule Openflow.Action.NxStackPush do defstruct( - n_bits: 0, - offset: 0, - field: nil + n_bits: 0, + offset: 0, + field: nil ) @experimenter 0x00002320 @@ -20,16 +20,21 @@ defmodule Openflow.Action.NxStackPush do 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) + + 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)>> + <<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 + 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 diff --git a/lib/openflow/actions/nx_write_metadata.ex b/lib/openflow/actions/nx_write_metadata.ex index 5450257..4396d6a 100644 --- a/lib/openflow/actions/nx_write_metadata.ex +++ b/lib/openflow/actions/nx_write_metadata.ex @@ -1,7 +1,7 @@ defmodule Openflow.Action.NxWriteMetadata do defstruct( metadata: 0, - metadata_mask: 0xffffffffffffffff + metadata_mask: 0xFFFFFFFFFFFFFFFF ) @experimenter 0x00002320 @@ -12,18 +12,21 @@ defmodule Openflow.Action.NxWriteMetadata do 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) + 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) + 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)>> + <<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 diff --git a/lib/openflow/actions/output.ex b/lib/openflow/actions/output.ex index 116dc96..070b123 100644 --- a/lib/openflow/actions/output.ex +++ b/lib/openflow/actions/output.ex @@ -11,6 +11,7 @@ defmodule Openflow.Action.Output do 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) diff --git a/lib/openflow/actions/push_pbb.ex b/lib/openflow/actions/push_pbb.ex index f509b70..4a7d0ae 100644 --- a/lib/openflow/actions/push_pbb.ex +++ b/lib/openflow/actions/push_pbb.ex @@ -1,5 +1,5 @@ defmodule Openflow.Action.PushPbb do - defstruct(ethertype: 0x88e7) + defstruct(ethertype: 0x88E7) alias __MODULE__ diff --git a/lib/openflow/actions/set_field.ex b/lib/openflow/actions/set_field.ex index a1b0c1d..79b4070 100644 --- a/lib/openflow/actions/set_field.ex +++ b/lib/openflow/actions/set_field.ex @@ -14,8 +14,8 @@ defmodule Openflow.Action.SetField do def to_binary(%SetField{field: {field, value}}) do match_bin = [{field, value}] - |> Openflow.Match.new - |> Openflow.Match.to_binary + |> 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) @@ -28,7 +28,7 @@ defmodule Openflow.Action.SetField 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) + {[field | _], _rest} = Openflow.Match.read(match_bin) %SetField{field: field} end end diff --git a/lib/openflow/barrier/reply.ex b/lib/openflow/barrier/reply.ex index a392f3d..59f7f68 100644 --- a/lib/openflow/barrier/reply.ex +++ b/lib/openflow/barrier/reply.ex @@ -1,9 +1,11 @@ defmodule Openflow.Barrier.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: 0 # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0 ) alias __MODULE__ diff --git a/lib/openflow/barrier/request.ex b/lib/openflow/barrier/request.ex index 6ca6437..399e533 100644 --- a/lib/openflow/barrier/request.ex +++ b/lib/openflow/barrier/request.ex @@ -1,9 +1,11 @@ defmodule Openflow.Barrier.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: 0 # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0 ) alias __MODULE__ diff --git a/lib/openflow/buckets.ex b/lib/openflow/buckets.ex index 806f3b6..005db35 100644 --- a/lib/openflow/buckets.ex +++ b/lib/openflow/buckets.ex @@ -1,9 +1,9 @@ defmodule Openflow.Bucket do defstruct( - weight: 0, - watch_port: 0, + weight: 0, + watch_port: 0, watch_group: 0, - actions: [] + actions: [] ) alias __MODULE__ @@ -29,26 +29,43 @@ defmodule Openflow.Bucket do # private functions defp do_read(acc, ""), do: Enum.reverse(acc) - defp do_read(acc, <>) do + + 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) + + 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 + + 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 = <> + + bucket_bin = + <> + to_binary(<>, rest) end end diff --git a/lib/openflow/echo/reply.ex b/lib/openflow/echo/reply.ex index 5955bf3..455ef64 100644 --- a/lib/openflow/echo/reply.ex +++ b/lib/openflow/echo/reply.ex @@ -1,10 +1,12 @@ defmodule Openflow.Echo.Reply do defstruct( - version: 4, - xid: 0, - data: "", - datapath_id: nil, # virtual field - aux_id: 0 # virtual field + version: 4, + xid: 0, + data: "", + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0 ) alias __MODULE__ diff --git a/lib/openflow/echo/request.ex b/lib/openflow/echo/request.ex index 248dede..f3a6af3 100644 --- a/lib/openflow/echo/request.ex +++ b/lib/openflow/echo/request.ex @@ -1,10 +1,12 @@ defmodule Openflow.Echo.Request do defstruct( - version: 4, - xid: 0, - data: "", - datapath_id: nil, # virtual field - aux_id: 0 # virtual field + version: 4, + xid: 0, + data: "", + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0 ) alias __MODULE__ @@ -23,4 +25,3 @@ defmodule Openflow.Echo.Request do data end end - diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex index b29f59d..7ea33b5 100644 --- a/lib/openflow/enums.ex +++ b/lib/openflow/enums.ex @@ -3,41 +3,39 @@ defmodule Openflow.Enums do @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.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.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} + {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 + onf_ext_message: 0x4F4E4600 ], - nicira_ext_message: [ # {Openflow.NxRole.Request, 10}, /* Openflow 1.3 support role request/reply */ # {Openflow.NxRole.Reply, 11}, @@ -45,534 +43,500 @@ defmodule Openflow.Enums do # {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.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.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} + {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}, + {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}, + {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}, + {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.NxFlow, 0}, + {Openflow.Multipart.NxAggregate, 1}, {Openflow.Multipart.NxFlowMonitor, 2}, {Openflow.Multipart.NxIPFIXBridge, 3}, - {Openflow.Multipart.NxIPFIXFlow, 4}, + {Openflow.Multipart.NxIPFIXFlow, 4} ], - hello_elem: [ - versionbitmap: 1 + 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, + 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 + experimenter: 0xFFFF ], - hello_failed: [ inconpatible: 0, - eperm: 1 + 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, + 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, + 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 + 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, + 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_experimeter: 5, + bad_exp_type: 6, + bad_len: 7, + eperm: 8 ], - bad_match: [ - bad_type: 0, - bad_len: 1, - bad_tag: 2, + 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 + 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, + unknown: 0, + table_full: 1, bad_table_id: 2, - overlap: 3, - eperm: 4, - bad_timeout: 5, - bad_command: 6, - bad_flags: 7 + 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, + 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 + 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_port: 0, + bad_hw_addr: 1, + bad_config: 2, bad_advertise: 3, - eperm: 4 + eperm: 4 ], - table_mod_failed: [ - bad_table: 0, + bad_table: 0, bad_config: 1, - eperm: 2 + eperm: 2 ], - queue_op_failed: [ - bad_port: 0, + bad_port: 0, bad_queue: 1, - eperm: 2 + eperm: 2 ], - switch_config_failed: [ bad_flags: 0, - bad_len: 1, - eperm: 2 + bad_len: 1, + eperm: 2 ], - role_request_failed: [ - stale: 0, - unsup: 1, + 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, + 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 + out_of_bands: 11 ], - table_features_failed: [ - bad_table: 0, + bad_table: 0, bad_metadata: 1, - bad_type: 2, - bad_len: 3, + bad_type: 2, + bad_len: 3, bad_argument: 4, - eperm: 5 + 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, + 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, + drop: 1 <<< 0, reasm: 1 <<< 1 ], - controller_max_len: [ - max: 0xffe5, - no_buffer: 0xffff + max: 0xFFE5, + no_buffer: 0xFFFF ], - experimenter_oxm_vendors: [ nicira_ext_match: 0x00002320, - hp_ext_match: 0x00002428, - onf_ext_match: 0x4f4e4600 + hp_ext_match: 0x00002428, + onf_ext_match: 0x4F4E4600 ], - match_type: [ standard: 0, - oxm: 1 + oxm: 1 ], - oxm_class: [ - nxm_0: 0x0000, - nxm_1: 0x0001, - openflow_basic: 0x8000, + nxm_0: 0x0000, + nxm_1: 0x0001, + openflow_basic: 0x8000, packet_register: 0x8001, - experimenter: 0xffff + 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_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, + 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, + 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 + 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, + 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, + 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, + 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 + 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 + none: 0x0000 ], - ipv6exthdr_flags: [ nonext: 1 <<< 0, - esp: 1 <<< 1, - auth: 1 <<< 2, - dest: 1 <<< 3, - frag: 1 <<< 4, + esp: 1 <<< 1, + auth: 1 <<< 2, + dest: 1 <<< 3, + frag: 1 <<< 4, router: 1 <<< 5, - hop: 1 <<< 6, - unrep: 1 <<< 7, - unseq: 1 <<< 8 + hop: 1 <<< 6, + unrep: 1 <<< 7, + unseq: 1 <<< 8 ], - tcp_flags: [ fin: 1 <<< 0, syn: 1 <<< 1, @@ -582,20 +546,26 @@ defmodule Openflow.Enums do urg: 1 <<< 5, ece: 1 <<< 6, cwr: 1 <<< 7, - ns: 1 <<< 8 + 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. + # Beginning of a new connection. + new: 1 <<< 0, + # Part of an existing connection. + est: 1 <<< 1, + # Related to an established connection. + rel: 1 <<< 2, + # Flow is in the reply direction. + rep: 1 <<< 3, + # Could not track connection. + inv: 1 <<< 4, + # Conntrack has occurred. + trk: 1 <<< 5, + # Packet's source address/port was mangled by NAT. + snat: 1 <<< 6, + # Packet's destination address/port was mangled by NAT. + dnat: 1 <<< 7 ], - packet_register: [ xreg0: 0, xreg1: 1, @@ -606,466 +576,414 @@ defmodule Openflow.Enums do xreg6: 6, xreg7: 7 ], - nicira_ext_match: [ - nsh_flags: 1, + 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 + nsh_np: 3, + nsh_spi: 4, + nsh_si: 5, + nsh_c1: 6, + nsh_c2: 7, + nsh_c3: 8, + nsh_c4: 9 ], - hp_ext_match: [ hp_udp_src_port_range: 0, hp_udp_dst_port_range: 1, hp_tcp_src_port_range: 2, hp_tcp_dst_port_range: 3, - hp_tcp_flags: 4, - hp_custom_1: 5, - hp_custom_2: 6, - hp_custom_3: 7, - hp_custom_4: 8 + hp_tcp_flags: 4, + hp_custom_1: 5, + hp_custom_2: 6, + hp_custom_3: 7, + hp_custom_4: 8 ], - hp_custom_match_type: [ l2_start: 1, l3_start: 2, l4_start: 3 ], - onf_ext_match: [ - onf_tcp_flags: 42, + onf_tcp_flags: 42, onf_actset_output: 43, - onf_pbb_uca: 2560, + onf_pbb_uca: 2560 ], - packet_in_reason: [ - no_match: 0, - action: 1, + no_match: 0, + action: 1, invalid_ttl: 2 ], - buffer_id: [ - no_buffer: 0xffffffff + no_buffer: 0xFFFFFFFF ], - port_config: [ - port_down: 1 <<< 0, - no_receive: 1 <<< 2, - no_forward: 1 <<< 5, + 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 + blocked: 1 <<< 1, + live: 1 <<< 2 ], - port_features: [ - {:"10mb_hd", 1 <<< 0}, - {:"10mb_fd", 1 <<< 1}, + {:"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}, + {:"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}, + {:"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 + 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 + 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, + no_match: 0, + action: 1, invalid_ttl: 2, - action_set: 3, - group: 4, - packet_out: 5 + action_set: 3, + group: 4, + packet_out: 5 ], - flow_mod_command: [ - add: 0, - modify: 1, + add: 0, + modify: 1, modify_strict: 2, - delete: 3, + delete: 3, delete_strict: 4 ], - flow_mod_flags: [ - send_flow_rem: 1 <<< 0, - check_overlap: 1 <<< 1, - reset_counts: 1 <<< 2, + send_flow_rem: 1 <<< 0, + check_overlap: 1 <<< 1, + reset_counts: 1 <<< 2, no_packet_counts: 1 <<< 3, - no_byte_counts: 1 <<< 4 + no_byte_counts: 1 <<< 4 ], - flow_removed_reason: [ idle_timeout: 0, hard_timeout: 1, - delete: 2, + delete: 2, group_delete: 3, meter_delete: 4, - eviction: 5 + eviction: 5 ], - port_reason: [ - add: 0, + 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, + add: 0, modify: 1, delete: 2 ], - - meter_id: [ - max: 0xffff0000, - slowpath: 0xfffffffd, - controller: 0xfffffffe, - all: 0xffffffff + 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, + 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}, + {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 + 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.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} + {Openflow.Action.Experimenter, 0xFFFF} ], - action_vendor: [ nicira_ext_action: 0x00002320, - onf_ext_action: 0x4f4e4600 + 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.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}, + {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, + modulo_n: 0, + hash_threshold: 1, highest_random_weight: 2, - iterative_hash: 3 + iterative_hash: 3 ], - nx_hash_fields: [ - eth_src: 0, - symmetric_l4: 1, - symmetric_l3l4: 2, + eth_src: 0, + symmetric_l4: 1, + symmetric_l3l4: 2, symmetric_l3l4_udp: 3, - nw_src: 4, - nw_dst: 5, + nw_src: 4, + nw_dst: 5 ], - nx_bd_algorithm: [ - active_backup: 0, + active_backup: 0, highest_random_weight: 1 ], - nx_learn_flag: [ - send_flow_rem: 1 <<< 0, + send_flow_rem: 1 <<< 0, delete_learned: 1 <<< 1, - write_result: 1 <<< 2 + write_result: 1 <<< 2 ], - nx_conntrack_flags: [ commit: 1 <<< 0, - force: 1 <<< 1 + force: 1 <<< 1 ], - nx_nat_flags: [ - src: 1 <<< 0, - dst: 1 <<< 1, - persistent: 1 <<< 2, - protocol_hash: 1 <<< 3, + 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, + 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, + max_len: 0, controller_id: 1, - reason: 2, - userdata: 3, - pause: 4 + reason: 2, + userdata: 3, + pause: 4 ], - nx_action_sample_direction: [ default: 0, ingress: 1, - egress: 2 + egress: 2 ], - nx_flow_spec_type: [ - {Openflow.Action.NxFlowSpecMatch, 0}, - {Openflow.Action.NxFlowSpecLoad, 1}, + {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} + {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 + equal: 1, + master: 2, + slave: 3 ], - nx_role: [ - other: 0, + other: 0, master: 1, - slave: 2 + slave: 2 ], - packet_in_format: [ - standard: 0, - nxt_packet_in: 1, + standard: 0, + nxt_packet_in: 1, nxt_packet_in2: 2 ], - flow_format: [ openflow10: 0, - nxm: 1 + 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, + 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, + bridge: 0x8000, + stack: 0x8001, + mirrors: 0x8002, conntracked: 0x8003, - table_id: 0x8004, - cookie: 0x8005, - actions: 0x8006, - action_set: 0x8007 + 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, + add: 1 <<< 1, + delete: 1 <<< 2, + modify: 1 <<< 3, actions: 1 <<< 4, - own: 1 <<< 5 + own: 1 <<< 5 ], - flow_update_event: [ - added: 0, - deleted: 1, + added: 0, + deleted: 1, modified: 2, - abbrev: 3 + abbrev: 3 ], - tlv_table_mod_command: [ - add: 0, + add: 0, delete: 1, - clear: 2 + clear: 2 ], - table_feature_prop_type: [ - instructions: 0, - instructions_miss: 1, - next_tables: 2, - next_tables_miss: 3, - write_actions: 4, - write_actions_miss: 5, - apply_actions: 6, - apply_actions_miss: 7, - match: 8, - wildcards: 10, - write_setfield: 12, + instructions: 0, + instructions_miss: 1, + next_tables: 2, + next_tables_miss: 3, + write_actions: 4, + write_actions_miss: 5, + apply_actions: 6, + apply_actions_miss: 7, + match: 8, + wildcards: 10, + write_setfield: 12, write_setfield_miss: 13, - apply_setfield: 14, + apply_setfield: 14, apply_setfield_miss: 15, - experimenter: 0xfffe, - experimenter_miss: 0xffff + experimenter: 0xFFFE, + experimenter_miss: 0xFFFF ] ] @@ -1105,7 +1023,7 @@ defmodule Openflow.Enums do def unquote(to_int_fn_name)(unquote(key)), do: unquote(value) end - def unquote(to_int_fn_name)(_), do: throw(:bad_enum) + def unquote(to_int_fn_name)(_), do: throw(:bad_enum) for {key, value} <- enum_def do def unquote(to_atom_fn_name)(unquote(value)), do: unquote(key) diff --git a/lib/openflow/error_msg.ex b/lib/openflow/error_msg.ex index 4a6f354..2279230 100644 --- a/lib/openflow/error_msg.ex +++ b/lib/openflow/error_msg.ex @@ -2,33 +2,38 @@ defmodule Openflow.ErrorMsg do @moduledoc "OpenFlow Error codec module" defstruct( - version: 4, - xid: 0, - type: nil, - code: nil, - data: "", - exp_type: nil, + version: 4, + xid: 0, + type: nil, + code: nil, + data: "", + exp_type: nil, experimenter: nil, - datapath_id: nil, # virtual field - aux_id: 0 # virtual field + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0 ) 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) + 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, + type: error_type, + exp_type: exp_type, experimenter: experimenter, - data: data + 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, @@ -36,16 +41,17 @@ defmodule Openflow.ErrorMsg do } end - def to_binary(%ErrorMsg{type: :experimenter, - exp_type: exp_type, - experimenter: experimenter, - data: data}) do + 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 + + 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) <> diff --git a/lib/openflow/experimenter.ex b/lib/openflow/experimenter.ex index bfead7a..1d540f0 100644 --- a/lib/openflow/experimenter.ex +++ b/lib/openflow/experimenter.ex @@ -1,12 +1,12 @@ defmodule Openflow.Experimenter do defstruct( - version: 4, - xid: 0, + version: 4, + xid: 0, datapath_id: "", - aux_id: 0, - exp_id: 0, - exp_type: 0, - data: "" + aux_id: 0, + exp_id: 0, + exp_type: 0, + data: "" ) alias __MODULE__ @@ -14,9 +14,11 @@ defmodule Openflow.Experimenter do 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, "")} + %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 @@ -27,10 +29,12 @@ defmodule Openflow.Experimenter 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) -> + + 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 diff --git a/lib/openflow/features/reply.ex b/lib/openflow/features/reply.ex index b22b384..e104ba8 100644 --- a/lib/openflow/features/reply.ex +++ b/lib/openflow/features/reply.ex @@ -1,22 +1,24 @@ defmodule Openflow.Features.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: "", - n_buffers: 0, - n_tables: 0, - aux_id: 0, - capabilities: [] + 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 + 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, @@ -31,7 +33,8 @@ defmodule Openflow.Features.Reply do n_buffers: n_buf, n_tables: n_tab, aux_id: aux_id, - capabilities: flags}) do + capabilities: flags + }) do dpid_int = String.to_integer(datapath_id, 16) flags_int = Openflow.Enums.flags_to_int(flags, :switch_capabilities) <> diff --git a/lib/openflow/features/request.ex b/lib/openflow/features/request.ex index d23aee1..83aadbb 100644 --- a/lib/openflow/features/request.ex +++ b/lib/openflow/features/request.ex @@ -1,9 +1,11 @@ defmodule Openflow.Features.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: 0 # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0 ) alias __MODULE__ diff --git a/lib/openflow/flow_mod.ex b/lib/openflow/flow_mod.ex index 338b0c7..1c1d972 100644 --- a/lib/openflow/flow_mod.ex +++ b/lib/openflow/flow_mod.ex @@ -3,7 +3,7 @@ defmodule Openflow.FlowMod do version: 4, xid: 0, datapath_id: "", - aux_id: 0, + aux_id: 0, cookie: 0, cookie_mask: 0, table_id: 0, @@ -35,26 +35,31 @@ defmodule Openflow.FlowMod do 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) + 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} + + %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 + 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) @@ -64,35 +69,41 @@ defmodule Openflow.FlowMod do {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} + + %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 + %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) @@ -101,9 +112,9 @@ defmodule Openflow.FlowMod do 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 index 3b3fe68..eea87d3 100644 --- a/lib/openflow/flow_removed.ex +++ b/lib/openflow/flow_removed.ex @@ -1,42 +1,46 @@ 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, + 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: [] + idle_timeout: 0, + hard_timeout: 0, + packet_count: 0, + byte_count: 0, + match: [] ) alias __MODULE__ def ofp_type, do: 11 - def read(<>) do + 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} + + %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 index 58b8f45..2f2e1a7 100644 --- a/lib/openflow/get_async/reply.ex +++ b/lib/openflow/get_async/reply.ex @@ -1,40 +1,46 @@ 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, + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0, + 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 + 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} + 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 - <> + 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 index 7433daf..3be0048 100644 --- a/lib/openflow/get_async/request.ex +++ b/lib/openflow/get_async/request.ex @@ -1,9 +1,11 @@ defmodule Openflow.GetAsync.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: 0 # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0 ) alias __MODULE__ diff --git a/lib/openflow/get_config/reply.ex b/lib/openflow/get_config/reply.ex index db1870d..55f08ff 100644 --- a/lib/openflow/get_config/reply.ex +++ b/lib/openflow/get_config/reply.ex @@ -1,10 +1,11 @@ defmodule Openflow.GetConfig.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: "", - aux_id: 0, - flags: [], # default = "normal" is no special handling + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + # default = "normal" is no special handling + flags: [], miss_send_len: 128 ) diff --git a/lib/openflow/get_config/request.ex b/lib/openflow/get_config/request.ex index c1ec4d9..e9e0af4 100644 --- a/lib/openflow/get_config/request.ex +++ b/lib/openflow/get_config/request.ex @@ -1,9 +1,11 @@ defmodule Openflow.GetConfig.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: 0 # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0 ) alias __MODULE__ diff --git a/lib/openflow/group_mod.ex b/lib/openflow/group_mod.ex index 276e9af..f8f2a54 100644 --- a/lib/openflow/group_mod.ex +++ b/lib/openflow/group_mod.ex @@ -3,7 +3,7 @@ defmodule Openflow.GroupMod do version: 4, xid: 0, datapath_id: "", - aux_id: 0, + aux_id: 0, command: :add, type: :all, group_id: 0, @@ -19,10 +19,7 @@ defmodule Openflow.GroupMod do 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} + %GroupMod{command: command, type: type, group_id: group_id, buckets: buckets} end def read(<>) do @@ -30,16 +27,10 @@ defmodule Openflow.GroupMod do 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} + %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 + 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) diff --git a/lib/openflow/hello.ex b/lib/openflow/hello.ex index a3f4a75..463e32e 100644 --- a/lib/openflow/hello.ex +++ b/lib/openflow/hello.ex @@ -9,32 +9,32 @@ defmodule Openflow.Hello do alias __MODULE__ - def ofp_type, - do: 0 + 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) + Enum.any?(versionbitmaps, fn versions -> 4 in versions end) end - def read(binary), - do: %Hello{elements: decode([], binary)} + def read(binary), do: %Hello{elements: decode([], binary)} - def to_binary(%Hello{elements: elements}), - do: encode([], elements) + 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 @@ -50,15 +50,13 @@ defmodule Openflow.Hello do end end - defp encode(acc, []), - do: to_string(acc) - defp encode(acc, [h|rest]), - do: encode([encode_hello_elem(h)|acc], rest) + 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 + 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) @@ -66,11 +64,13 @@ defmodule Openflow.Hello do 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) @@ -80,24 +80,27 @@ defmodule Openflow.Hello do defp encode_bitmap(list) do size = list - |> Enum.max + |> 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, + 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) + 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) + + 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 index c33d631..b64b1c7 100644 --- a/lib/openflow/instruction.ex +++ b/lib/openflow/instruction.ex @@ -1,5 +1,4 @@ defmodule Openflow.Instruction do - def read(instruction_bin) do do_read([], instruction_bin) end @@ -7,6 +6,7 @@ defmodule Openflow.Instruction do def to_binary(instructions) when is_list(instructions) do to_binary(<<>>, instructions) end + def to_binary(instruction) do to_binary([instruction]) end @@ -14,15 +14,17 @@ defmodule Openflow.Instruction do # 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) + do_read([codec.read(instruction_bin) | acc], rest) end defp to_binary(acc, []), do: acc - defp to_binary(acc, [instruction|rest]) do + + defp to_binary(acc, [instruction | rest]) do codec = instruction.__struct__ - to_binary(<>, rest) + to_binary(<>, rest) end end diff --git a/lib/openflow/instructions/experimenter.ex b/lib/openflow/instructions/experimenter.ex index c0c624d..f4cc3f2 100644 --- a/lib/openflow/instructions/experimenter.ex +++ b/lib/openflow/instructions/experimenter.ex @@ -9,10 +9,10 @@ defmodule Openflow.Instruction.Experimenter do 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>> + <<0xFFFF::16, length::16, exp_id::32, data::bytes>> end - def read(<<0xffff::16, _::16, exp_id::32, data::bytes>>) do + 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/write_metadata.ex b/lib/openflow/instructions/write_metadata.ex index 034e8d7..5b0df98 100644 --- a/lib/openflow/instructions/write_metadata.ex +++ b/lib/openflow/instructions/write_metadata.ex @@ -1,11 +1,11 @@ defmodule Openflow.Instruction.WriteMetadata do - defstruct(metadata: 0, metadata_mask: 0xffffffffffffffff) + 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) + metadata_mask = Keyword.get(options, :metadata_mask, 0xFFFFFFFFFFFFFFFF) %WriteMetadata{metadata: metadata, metadata_mask: metadata_mask} end diff --git a/lib/openflow/match.ex b/lib/openflow/match.ex index 0ca8f98..3cbf258 100644 --- a/lib/openflow/match.ex +++ b/lib/openflow/match.ex @@ -1,11 +1,11 @@ defmodule Openflow.Match do - @match_size 8 + @match_size 8 @header_size 4 - defstruct([ - type: :oxm, + defstruct( + type: :oxm, fields: [] - ]) + ) alias __MODULE__ @@ -13,6 +13,7 @@ defmodule Openflow.Match do oxm_fields = fields |> keyword_to_oxm_fields([]) + %Match{type: :oxm, fields: oxm_fields} end @@ -20,7 +21,10 @@ defmodule Openflow.Match do <<1::16, no_pad_len::16, binary1::binary>> = binary padding_length = Openflow.Utils.pad_length(no_pad_len, 8) match_field_len = no_pad_len - @header_size - <> = binary1 + + <> = binary1 + {decode_fields(match_fields, []), rest} end @@ -33,14 +37,17 @@ defmodule Openflow.Match do end def codec_header(oxm_field0) when is_atom(oxm_field0) do - oxm_field = case has_mask(oxm_field0) do - 1 -> - string = to_string(oxm_field0) - "masked_" <> field = string - String.to_atom(field) - 0 -> - oxm_field0 - end + oxm_field = + case has_mask(oxm_field0) do + 1 -> + string = to_string(oxm_field0) + "masked_" <> field = string + String.to_atom(field) + + 0 -> + oxm_field0 + end + 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) @@ -48,8 +55,9 @@ defmodule Openflow.Match do oxm_length = div(Openflow.Match.Field.n_bits_of(oxm_field), 8) has_mask = has_mask(oxm_field0) <> + experimenter when experimenter in [:nicira_ext_match, :onf_ext_match, :hp_ext_match] -> - oxm_class_int = 0xffff + 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) @@ -57,41 +65,63 @@ defmodule Openflow.Match do <> end end + def codec_header(<>) do oxm_class = Openflow.Enums.to_atom(oxm_class_int, :oxm_class) + case oxm_has_mask do - 0 -> Openflow.Enums.to_atom(oxm_field_int, oxm_class) + 0 -> + Openflow.Enums.to_atom(oxm_field_int, oxm_class) + 1 -> field_str = oxm_field_int |> Openflow.Enums.to_atom(oxm_class) |> to_string + String.to_atom("masked_" <> field_str) end end - def codec_header(<<0xffff::16, oxm_field_int::7, oxm_has_mask::1, _oxm_length::8, experimenter_int::32>>) do + + 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) + case oxm_has_mask do - 0 -> Openflow.Enums.to_atom(oxm_field_int, experimenter) + 0 -> + Openflow.Enums.to_atom(oxm_field_int, experimenter) + 1 -> field_str = oxm_field_int |> Openflow.Enums.to_atom(experimenter) |> to_string + String.to_atom("masked_" <> field_str) end 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 + 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 + + 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 @@ -99,16 +129,21 @@ defmodule Openflow.Match do 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]) + 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 + + 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]) + decode_fields(rest, [{field_name, value} | acc]) end + defp decode_fields(<>, acc) do field_len = div(length, 2) <> = binary @@ -116,37 +151,43 @@ defmodule Openflow.Match do 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]) + 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]) + decode_fields(rest, [{field_name, value} | acc]) end defp encode_fields([], acc), do: acc - defp encode_fields([field|fields], acc) do - encode_fields(fields, <>) + + 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 + 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>> + 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 + 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>> + <<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) @@ -154,6 +195,7 @@ defmodule Openflow.Match do 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) @@ -163,8 +205,9 @@ defmodule Openflow.Match do 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]) + + 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 @@ -173,6 +216,7 @@ defmodule Openflow.Match do 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) @@ -184,6 +228,7 @@ defmodule Openflow.Match do oxm_field |> to_string |> String.match?(~r/^masked_/) + if has_mask? do 1 else diff --git a/lib/openflow/match/field.ex b/lib/openflow/match/field.ex index 8a6e411..3cabbd4 100644 --- a/lib/openflow/match/field.ex +++ b/lib/openflow/match/field.ex @@ -1,23 +1,26 @@ 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 + + value = + if bit_size < n_bits do + head_pad_len = n_bits - bit_size + <<0::size(head_pad_len), value0::bytes>> else - value0 + 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 - end + formatting(value, type, format) end + def codec(value, type) do {type, format} = format_of(type) formatting(value, type, format) @@ -29,251 +32,251 @@ defmodule Openflow.Match.Field do |> 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({: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 + 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_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 + 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(: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 + 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(: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 + 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 + 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_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 + 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 # HP Ext Match def vendor_of(:hp_udp_src_port_range), do: :hp_ext_match @@ -281,269 +284,269 @@ defmodule Openflow.Match.Field do def vendor_of(:hp_tcp_src_port_range), do: :hp_ext_match def vendor_of(:hp_tcp_dst_port_range), do: :hp_ext_match def vendor_of(:hp_tcp_flags), do: :hp_ext_match - def vendor_of(:hp_custom_1), do: :hp_ext_match - def vendor_of(:hp_custom_2), do: :hp_ext_match - def vendor_of(:hp_custom_3), do: :hp_ext_match - def vendor_of(:hp_custom_4), do: :hp_ext_match + def vendor_of(:hp_custom_1), do: :hp_ext_match + def vendor_of(:hp_custom_2), do: :hp_ext_match + def vendor_of(:hp_custom_3), do: :hp_ext_match + def vendor_of(:hp_custom_4), do: :hp_ext_match # ONF Ext Match - def vendor_of(:onf_tcp_flags), do: :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 + 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} + 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} + 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} + 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} + 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} + 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} # HP Ext Match def format_of(:hp_udp_src_port_range), do: {:be32, :decimal} def format_of(:hp_udp_dst_port_range), do: {:be32, :decimal} def format_of(:hp_tcp_src_port_range), do: {:be32, :decimal} def format_of(:hp_tcp_dst_port_range), do: {:be32, :decimal} - def format_of(:hp_tcp_flags), do: {:be16, :tcp_flags} - def format_of(:hp_custom_1), do: {:dynamic, :bytes} - def format_of(:hp_custom_2), do: {:dynamic, :bytes} - def format_of(:hp_custom_3), do: {:dynamic, :bytes} - def format_of(:hp_custom_4), do: {:dynamic, :bytes} + def format_of(:hp_tcp_flags), do: {:be16, :tcp_flags} + def format_of(:hp_custom_1), do: {:dynamic, :bytes} + def format_of(:hp_custom_2), do: {:dynamic, :bytes} + def format_of(:hp_custom_3), do: {:dynamic, :bytes} + def format_of(:hp_custom_4), do: {:dynamic, :bytes} # ONF Ext Match - def format_of(:onf_tcp_flags), do: {:be16, :tcp_flags} + 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} + def format_of(:onf_pbb_uca), do: {:u8, :decimal} # Formatting = decimal def formatting(<>, :u8, :decimal), do: value @@ -577,17 +580,22 @@ defmodule Openflow.Match.Field 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>> + def formatting(value, :mac, :ethernet), do: <> # 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 + 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 + + def formatting({a1, a2, a3, a4, a5, a6, a7, a8}, :be128, :ipv6) do <> end @@ -599,13 +607,15 @@ defmodule Openflow.Match.Field do :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 + :bad_enum -> value end + <> end @@ -617,6 +627,7 @@ defmodule Openflow.Match.Field do :bad_enum -> value end end + def formatting(value, :be32, :openflow13_port) do port_no = try do @@ -624,6 +635,7 @@ defmodule Openflow.Match.Field do catch :bad_enum -> value end + <> end @@ -631,24 +643,27 @@ defmodule Openflow.Match.Field do 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 diff --git a/lib/openflow/meter_band.ex b/lib/openflow/meter_band.ex index cbc4805..ab8a8b8 100644 --- a/lib/openflow/meter_band.ex +++ b/lib/openflow/meter_band.ex @@ -1,5 +1,4 @@ defmodule Openflow.MeterBand do - def read(meter_band_bin) do do_read([], meter_band_bin) end @@ -7,6 +6,7 @@ defmodule Openflow.MeterBand do 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 @@ -14,15 +14,17 @@ defmodule Openflow.MeterBand do # 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) + do_read([codec.read(meter_band_bin) | acc], rest) end defp to_binary(acc, []), do: acc - defp to_binary(acc, [meter_band|rest]) do + + defp to_binary(acc, [meter_band | rest]) do codec = meter_band.__struct__ - to_binary(<>, rest) + to_binary(<>, rest) end end diff --git a/lib/openflow/meter_band/drop.ex b/lib/openflow/meter_band/drop.ex index 6336652..bacd01d 100644 --- a/lib/openflow/meter_band/drop.ex +++ b/lib/openflow/meter_band/drop.ex @@ -1,6 +1,6 @@ defmodule Openflow.MeterBand.Drop do defstruct( - rate: 0, + rate: 0, burst_size: 0 ) diff --git a/lib/openflow/meter_band/experimenter.ex b/lib/openflow/meter_band/experimenter.ex index 8a73171..1fab8e6 100644 --- a/lib/openflow/meter_band/experimenter.ex +++ b/lib/openflow/meter_band/experimenter.ex @@ -1,7 +1,7 @@ defmodule Openflow.MeterBand.Experimenter do defstruct( - rate: 0, - burst_size: 0, + rate: 0, + burst_size: 0, experimenter: 0 ) @@ -14,11 +14,11 @@ defmodule Openflow.MeterBand.Experimenter do %Experimenter{rate: rate, burst_size: burst_size, experimenter: experimenter} end - def read(<<0xffff::16, _::16, rate::32, burst_size::32, experimenter::32>>) do + 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>> + <<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 index 6e0f4bf..1bb3f93 100644 --- a/lib/openflow/meter_band/remark.ex +++ b/lib/openflow/meter_band/remark.ex @@ -1,6 +1,6 @@ defmodule Openflow.MeterBand.Remark do defstruct( - rate: 0, + rate: 0, burst_size: 0, prec_level: 0 ) diff --git a/lib/openflow/meter_mod.ex b/lib/openflow/meter_mod.ex index 24aab63..05e47b3 100644 --- a/lib/openflow/meter_mod.ex +++ b/lib/openflow/meter_mod.ex @@ -3,7 +3,7 @@ defmodule Openflow.MeterMod do version: 4, xid: 0, datapath_id: "", - aux_id: 0, + aux_id: 0, command: :add, flags: [], meter_id: 0, diff --git a/lib/openflow/multipart/aggregate/reply.ex b/lib/openflow/multipart/aggregate/reply.ex index 7bb7beb..7b710c2 100644 --- a/lib/openflow/multipart/aggregate/reply.ex +++ b/lib/openflow/multipart/aggregate/reply.ex @@ -1,13 +1,14 @@ defmodule Openflow.Multipart.Aggregate.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], packet_count: 0, - byte_count: 0, - flow_count: 0 + byte_count: 0, + flow_count: 0 ) alias __MODULE__ diff --git a/lib/openflow/multipart/aggregate/request.ex b/lib/openflow/multipart/aggregate/request.ex index e7fa041..89f55e3 100644 --- a/lib/openflow/multipart/aggregate/request.ex +++ b/lib/openflow/multipart/aggregate/request.ex @@ -1,15 +1,16 @@ 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: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + flags: [], + table_id: :all, + out_port: :any, + out_group: :any, + cookie: 0, + cookie_mask: 0, + match: [] ) alias __MODULE__ @@ -23,44 +24,55 @@ defmodule Openflow.Multipart.Aggregate.Request do 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} + + %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 + 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} + + %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 + 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 = <> + + body_bin = + <> + header_bin = Openflow.Multipart.Request.header(msg) <> end diff --git a/lib/openflow/multipart/desc/reply.ex b/lib/openflow/multipart/desc/reply.ex index 7794428..a995cf2 100644 --- a/lib/openflow/multipart/desc/reply.ex +++ b/lib/openflow/multipart/desc/reply.ex @@ -1,31 +1,36 @@ defmodule Openflow.Multipart.Desc.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - mfr_desc: "", - hw_desc: "", - sw_desc: "", - serial_num: "", - dp_desc: "" + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + mfr_desc: "", + hw_desc: "", + sw_desc: "", + serial_num: "", + dp_desc: "" ) alias __MODULE__ - @desc_str_len 256 + @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)} + 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 index ffd55e2..4d9bef7 100644 --- a/lib/openflow/multipart/desc/request.ex +++ b/lib/openflow/multipart/desc/request.ex @@ -1,8 +1,9 @@ defmodule Openflow.Multipart.Desc.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [] ) diff --git a/lib/openflow/multipart/flow/reply.ex b/lib/openflow/multipart/flow/reply.ex index 41941f0..135a222 100644 --- a/lib/openflow/multipart/flow/reply.ex +++ b/lib/openflow/multipart/flow/reply.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.Flow.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - flows: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + flows: [] ) alias __MODULE__ @@ -22,29 +23,32 @@ defmodule Openflow.Multipart.Flow.Reply do end def append_body(%Reply{flows: flows} = message, %Reply{flags: [:more], flows: continue}) do - %{message|flows: [continue|flows]} + %{message | flows: [continue | flows]} end + def append_body(%Reply{flows: flows} = message, %Reply{flags: [], flows: continue}) do - new_flows = [continue|flows] - |> Enum.reverse - |> List.flatten - %{message|flows: new_flows} + new_flows = + [continue | flows] + |> Enum.reverse() + |> List.flatten() + + %{message | flows: new_flows} end end defmodule Openflow.Multipart.FlowStats do defstruct( - table_id: 0, - duration_sec: 0, + 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: [], + priority: 0, + idle_timeout: 0, + hard_timeout: 0, + flags: 0, + cookie: 0, + packet_count: 0, + byte_count: 0, + match: [], instructions: [] ) @@ -57,30 +61,35 @@ defmodule Openflow.Multipart.FlowStats do # 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) + 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 + 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} + + %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 index 53e8146..dc14e43 100644 --- a/lib/openflow/multipart/flow/request.ex +++ b/lib/openflow/multipart/flow/request.ex @@ -1,15 +1,16 @@ 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: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + flags: [], + table_id: :all, + out_port: :any, + out_group: :any, + cookie: 0, + cookie_mask: 0, + match: [] ) alias __MODULE__ @@ -22,45 +23,56 @@ defmodule Openflow.Multipart.Flow.Request do 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, Openflow.Match.new) - %Request{table_id: table_id, - out_port: out_port, - out_group: out_group, - cookie: cookie, - cookie_mask: cookie_mask, - match: match} + match = Keyword.get(options, :match, Openflow.Match.new()) + + %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 + 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} + + %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 + 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 = <> + + body_bin = + <> + header_bin = Openflow.Multipart.Request.header(msg) <> end diff --git a/lib/openflow/multipart/group/reply.ex b/lib/openflow/multipart/group/reply.ex index fc434a0..5e4b86f 100644 --- a/lib/openflow/multipart/group/reply.ex +++ b/lib/openflow/multipart/group/reply.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.Group.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - groups: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + groups: [] ) alias __MODULE__ @@ -22,25 +23,28 @@ defmodule Openflow.Multipart.Group.Reply do end def append_body(%Reply{groups: groups} = message, %Reply{flags: [:more], groups: continue}) do - %{message|groups: [continue|groups]} + %{message | groups: [continue | groups]} end + def append_body(%Reply{groups: groups} = message, %Reply{flags: [], groups: continue}) do - new_groups = [continue|groups] - |> Enum.reverse - |> List.flatten - %{message|groups: new_groups} + new_groups = + [continue | groups] + |> Enum.reverse() + |> List.flatten() + + %{message | groups: new_groups} end end defmodule Openflow.Multipart.Group do defstruct( - group_id: 0, - ref_count: 0, - packet_count: 0, - byte_count: 0, - duration_sec: 0, + group_id: 0, + ref_count: 0, + packet_count: 0, + byte_count: 0, + duration_sec: 0, duration_nsec: 0, - bucket_stats: [] + bucket_stats: [] ) @ofp_group_stats_size 40 @@ -54,27 +58,32 @@ defmodule Openflow.Multipart.Group do # private functions defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do <> = binary - do_read([codec(group_bin)|acc], rest) + do_read([codec(group_bin) | acc], rest) end - defp codec(<>) do + 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} + + 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 index 288123a..bc7130c 100644 --- a/lib/openflow/multipart/group/request.ex +++ b/lib/openflow/multipart/group/request.ex @@ -1,10 +1,11 @@ defmodule Openflow.Multipart.Group.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [], - group_id: :all + group_id: :all ) alias __MODULE__ diff --git a/lib/openflow/multipart/group_desc/reply.ex b/lib/openflow/multipart/group_desc/reply.ex index fea5684..8e8d424 100644 --- a/lib/openflow/multipart/group_desc/reply.ex +++ b/lib/openflow/multipart/group_desc/reply.ex @@ -1,10 +1,11 @@ defmodule Openflow.Multipart.GroupDesc.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - flags: [], - groups: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + flags: [], + groups: [] ) alias __MODULE__ @@ -21,21 +22,24 @@ defmodule Openflow.Multipart.GroupDesc.Reply do end def append_body(%Reply{groups: groups} = message, %Reply{flags: [:more], groups: continue}) do - %{message|groups: [continue|groups]} + %{message | groups: [continue | groups]} end + def append_body(%Reply{groups: groups} = message, %Reply{flags: [], groups: continue}) do - new_groups = [continue|groups] - |> Enum.reverse - |> List.flatten - %{message|groups: new_groups} + new_groups = + [continue | groups] + |> Enum.reverse() + |> List.flatten() + + %{message | groups: new_groups} end end defmodule Openflow.Multipart.GroupDescStats do defstruct( - type: :all, - group_id: 0, - buckets: [] + type: :all, + group_id: 0, + buckets: [] ) alias __MODULE__ @@ -49,9 +53,10 @@ defmodule Openflow.Multipart.GroupDescStats do # 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) + do_read([codec(group_stats_bin) | acc], rest) end defp codec(<>) do diff --git a/lib/openflow/multipart/group_desc/request.ex b/lib/openflow/multipart/group_desc/request.ex index 6f7f390..7fda473 100644 --- a/lib/openflow/multipart/group_desc/request.ex +++ b/lib/openflow/multipart/group_desc/request.ex @@ -1,8 +1,9 @@ defmodule Openflow.Multipart.GroupDesc.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [] ) diff --git a/lib/openflow/multipart/group_features/reply.ex b/lib/openflow/multipart/group_features/reply.ex index 495ad83..a43667b 100644 --- a/lib/openflow/multipart/group_features/reply.ex +++ b/lib/openflow/multipart/group_features/reply.ex @@ -1,45 +1,47 @@ defmodule Openflow.Multipart.GroupFeatures.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - types: 0, + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + types: 0, capabilities: [], - max_groups_for_all: 0, - max_groups_for_select: 0, - max_groups_for_indirect: 0, + 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 + 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 + 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} + + %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 index c51278b..b44c0dd 100644 --- a/lib/openflow/multipart/group_features/request.ex +++ b/lib/openflow/multipart/group_features/request.ex @@ -1,8 +1,9 @@ defmodule Openflow.Multipart.GroupFeatures.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [] ) diff --git a/lib/openflow/multipart/meter/reply.ex b/lib/openflow/multipart/meter/reply.ex index 2aede6e..7fe9656 100644 --- a/lib/openflow/multipart/meter/reply.ex +++ b/lib/openflow/multipart/meter/reply.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.Meter.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - meters: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + meters: [] ) alias __MODULE__ @@ -18,25 +19,28 @@ defmodule Openflow.Multipart.Meter.Reply do end def append_body(%Reply{meters: meters} = message, %Reply{flags: [:more], meters: continue}) do - %{message|meters: [continue|meters]} + %{message | meters: [continue | meters]} end + def append_body(%Reply{meters: meters} = message, %Reply{flags: [], meters: continue}) do - new_meters = [continue|meters] - |> Enum.reverse - |> List.flatten - %{message|meters: new_meters} + new_meters = + [continue | meters] + |> Enum.reverse() + |> List.flatten() + + %{message | meters: new_meters} end end defmodule Openflow.Multipart.Meter do defstruct( - meter_id: 0, - flow_count: 0, + meter_id: 0, + flow_count: 0, packet_in_count: 0, - byte_in_count: 0, - duration_sec: 0, - duration_nsec: 0, - band_stats: [] + byte_in_count: 0, + duration_sec: 0, + duration_nsec: 0, + band_stats: [] ) @ofp_meter_stats_size 40 @@ -50,25 +54,32 @@ defmodule Openflow.Multipart.Meter do # 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) + do_read([codec(meter_bin) | acc], rest) end - defp codec(<>) do + 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} + + 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 index d9f1944..084b2fb 100644 --- a/lib/openflow/multipart/meter/request.ex +++ b/lib/openflow/multipart/meter/request.ex @@ -1,10 +1,11 @@ defmodule Openflow.Multipart.Meter.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [], - meter_id: :all + meter_id: :all ) alias __MODULE__ diff --git a/lib/openflow/multipart/meter_config/request.ex b/lib/openflow/multipart/meter_config/request.ex index ced8c02..afd7dd1 100644 --- a/lib/openflow/multipart/meter_config/request.ex +++ b/lib/openflow/multipart/meter_config/request.ex @@ -1,10 +1,11 @@ defmodule Openflow.Multipart.MeterConfig.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [], - meter_id: :all + meter_id: :all ) alias __MODULE__ diff --git a/lib/openflow/multipart/port_desc/reply.ex b/lib/openflow/multipart/port_desc/reply.ex index 73e0573..6025dab 100644 --- a/lib/openflow/multipart/port_desc/reply.ex +++ b/lib/openflow/multipart/port_desc/reply.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.PortDesc.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - ports: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + ports: [] ) alias __MODULE__ @@ -17,17 +18,20 @@ defmodule Openflow.Multipart.PortDesc.Reply do end def read(<>) do - ports = for (<>), do: Openflow.Port.read(port_bin) + ports = for <>, do: Openflow.Port.read(port_bin) %Reply{ports: Enum.reverse(ports)} end def append_body(%Reply{ports: ports} = message, %Reply{flags: [:more], ports: continue}) do - %{message|ports: [continue|ports]} + %{message | ports: [continue | ports]} end + def append_body(%Reply{ports: ports} = message, %Reply{flags: [], ports: continue}) do - new_ports = [continue|ports] - |> Enum.reverse - |> List.flatten - %{message|ports: new_ports} + new_ports = + [continue | ports] + |> Enum.reverse() + |> List.flatten() + + %{message | ports: new_ports} end end diff --git a/lib/openflow/multipart/port_desc/request.ex b/lib/openflow/multipart/port_desc/request.ex index be0abde..e6bfe44 100644 --- a/lib/openflow/multipart/port_desc/request.ex +++ b/lib/openflow/multipart/port_desc/request.ex @@ -1,8 +1,9 @@ defmodule Openflow.Multipart.PortDesc.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [] ) diff --git a/lib/openflow/multipart/port_stats/reply.ex b/lib/openflow/multipart/port_stats/reply.ex index e57ed32..880c639 100644 --- a/lib/openflow/multipart/port_stats/reply.ex +++ b/lib/openflow/multipart/port_stats/reply.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.PortStats.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - ports: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + ports: [] ) alias __MODULE__ @@ -22,32 +23,35 @@ defmodule Openflow.Multipart.PortStats.Reply do end def append_body(%Reply{ports: ports} = message, %Reply{flags: [:more], ports: continue}) do - %{message|ports: [continue|ports]} + %{message | ports: [continue | ports]} end + def append_body(%Reply{ports: ports} = message, %Reply{flags: [], ports: continue}) do - new_ports = [continue|ports] - |> Enum.reverse - |> List.flatten - %{message|ports: new_ports} + new_ports = + [continue | ports] + |> Enum.reverse() + |> List.flatten() + + %{message | ports: new_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, + 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 ) @@ -60,30 +64,33 @@ defmodule Openflow.Multipart.PortStats do # private functions defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do - do_read([codec(port_stats_bin)|acc], rest) + 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} + 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 index e432be6..6b91648 100644 --- a/lib/openflow/multipart/port_stats/request.ex +++ b/lib/openflow/multipart/port_stats/request.ex @@ -1,8 +1,9 @@ defmodule Openflow.Multipart.PortStats.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [], port_number: :any ) diff --git a/lib/openflow/multipart/queue/reply.ex b/lib/openflow/multipart/queue/reply.ex index 38afbbc..a420ba1 100644 --- a/lib/openflow/multipart/queue/reply.ex +++ b/lib/openflow/multipart/queue/reply.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.Queue.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - queues: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + queues: [] ) alias __MODULE__ @@ -22,24 +23,27 @@ defmodule Openflow.Multipart.Queue.Reply do end def append_body(%Reply{queues: queues} = message, %Reply{flags: [:more], queues: continue}) do - %{message|queues: [continue|queues]} + %{message | queues: [continue | queues]} end + def append_body(%Reply{queues: queues} = message, %Reply{flags: [], queues: continue}) do - new_queues = [continue|queues] - |> Enum.reverse - |> List.flatten - %{message|queues: new_queues} + new_queues = + [continue | queues] + |> Enum.reverse() + |> List.flatten() + + %{message | queues: new_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, + port_number: 0, + queue_id: 0, + tx_bytes: 0, + tx_packets: 0, + tx_errors: 0, + duration_sec: 0, duration_nsec: 0 ) @@ -52,19 +56,23 @@ defmodule Openflow.Multipart.Queue do # private functions defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do - do_read([codec(queue_bin)|acc], rest) + 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} + 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 index 654a407..7cab958 100644 --- a/lib/openflow/multipart/queue/request.ex +++ b/lib/openflow/multipart/queue/request.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.Queue.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [], port_number: :any, - queue_id: :all + queue_id: :all ) alias __MODULE__ diff --git a/lib/openflow/multipart/reply.ex b/lib/openflow/multipart/reply.ex index ad446cc..18893b0 100644 --- a/lib/openflow/multipart/reply.ex +++ b/lib/openflow/multipart/reply.ex @@ -5,15 +5,17 @@ defmodule Openflow.Multipart.Reply 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} + %{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 diff --git a/lib/openflow/multipart/request.ex b/lib/openflow/multipart/request.ex index 4f6f454..43d6e78 100644 --- a/lib/openflow/multipart/request.ex +++ b/lib/openflow/multipart/request.ex @@ -5,15 +5,17 @@ defmodule Openflow.Multipart.Request 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} + %{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 diff --git a/lib/openflow/multipart/table/reply.ex b/lib/openflow/multipart/table/reply.ex index 457de7c..f83406f 100644 --- a/lib/openflow/multipart/table/reply.ex +++ b/lib/openflow/multipart/table/reply.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.Table.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - tables: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + tables: [] ) alias __MODULE__ @@ -18,21 +19,24 @@ defmodule Openflow.Multipart.Table.Reply do end def append_body(%Reply{tables: tables} = message, %Reply{flags: [:more], tables: continue}) do - %{message|tables: [continue|tables]} + %{message | tables: [continue | tables]} end + def append_body(%Reply{tables: tables} = message, %Reply{flags: [], tables: continue}) do - new_tables = [continue|tables] - |> Enum.reverse - |> List.flatten - %{message|tables: new_tables} + new_tables = + [continue | tables] + |> Enum.reverse() + |> List.flatten() + + %{message | tables: new_tables} end end defmodule Openflow.Multipart.TableStats do defstruct( - table_id: 0, - active_count: 0, - lookup_count: 0, + table_id: 0, + active_count: 0, + lookup_count: 0, matched_count: 0 ) @@ -45,12 +49,20 @@ defmodule Openflow.Multipart.TableStats do # private functions defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <>) do - do_read([codec(table_stats_bin)|acc], rest) + 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} + 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 index 8607068..39938c2 100644 --- a/lib/openflow/multipart/table/request.ex +++ b/lib/openflow/multipart/table/request.ex @@ -1,8 +1,9 @@ defmodule Openflow.Multipart.Table.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, flags: [] ) diff --git a/lib/openflow/multipart/table_features/body.ex b/lib/openflow/multipart/table_features/body.ex index c4903fc..3399a06 100644 --- a/lib/openflow/multipart/table_features/body.ex +++ b/lib/openflow/multipart/table_features/body.ex @@ -1,46 +1,44 @@ defmodule Openflow.Multipart.TableFeatures.Body do - defstruct [ - table_id: 0, - name: "", - metadata_match: 0, - metadata_write: 0, - config: [], - max_entries: 0, - instructions: nil, - instructions_miss: nil, - next_tables: nil, - next_tables_miss: nil, - write_actions: nil, - write_actions_miss: nil, - apply_actions: nil, - apply_actions_miss: nil, - match: nil, - wildcards: nil, - write_setfield: nil, - write_setfield_miss: nil, - apply_setfield: nil, - apply_setfield_miss: nil - ] + defstruct table_id: 0, + name: "", + metadata_match: 0, + metadata_write: 0, + config: [], + max_entries: 0, + instructions: nil, + instructions_miss: nil, + next_tables: nil, + next_tables_miss: nil, + write_actions: nil, + write_actions_miss: nil, + apply_actions: nil, + apply_actions_miss: nil, + match: nil, + wildcards: nil, + write_setfield: nil, + write_setfield_miss: nil, + apply_setfield: nil, + apply_setfield_miss: nil alias __MODULE__ - @max_table_name_len 32 - @prop_header_length 4 + @max_table_name_len 32 + @prop_header_length 4 @table_features_length 64 - @instructions 0 - @instructions_miss 1 - @next_tables 2 - @next_tables_miss 3 - @write_actions 4 - @write_actions_miss 5 - @apply_actions 6 - @apply_actions_miss 7 - @match 8 - @wildcards 10 - @write_setfield 12 + @instructions 0 + @instructions_miss 1 + @next_tables 2 + @next_tables_miss 3 + @write_actions 4 + @write_actions_miss 5 + @apply_actions 6 + @apply_actions_miss 7 + @match 8 + @wildcards 10 + @write_setfield 12 @write_setfield_miss 13 - @apply_setfield 14 + @apply_setfield 14 @apply_setfield_miss 15 @prop_keys [ @@ -62,25 +60,25 @@ defmodule Openflow.Multipart.TableFeatures.Body do def new(options) do %Body{ - table_id: options[:table_id] || 0, - name: options[:name] || "", - metadata_match: options[:metadata_match] || 0, - metadata_write: options[:metadata_write] || 0, - config: options[:config] || [], - max_entries: options[:max_entries] || 0, - instructions: options[:instructions], - instructions_miss: options[:instructions_miss], - next_tables: options[:next_tables], - next_tables_miss: options[:next_tables_miss], - write_actions: options[:write_actions], - write_actions_miss: options[:write_actions_miss], - apply_actions: options[:apply_actions], - apply_actions_miss: options[:apply_actions_miss], - match: options[:match], - wildcards: options[:wildcards], - write_setfield: options[:write_setfield], + table_id: options[:table_id] || 0, + name: options[:name] || "", + metadata_match: options[:metadata_match] || 0, + metadata_write: options[:metadata_write] || 0, + config: options[:config] || [], + max_entries: options[:max_entries] || 0, + instructions: options[:instructions], + instructions_miss: options[:instructions_miss], + next_tables: options[:next_tables], + next_tables_miss: options[:next_tables_miss], + write_actions: options[:write_actions], + write_actions_miss: options[:write_actions_miss], + apply_actions: options[:apply_actions], + apply_actions_miss: options[:apply_actions_miss], + match: options[:match], + wildcards: options[:wildcards], + write_setfield: options[:write_setfield], write_setfield_miss: options[:write_setfield_miss], - apply_setfield: options[:apply_setfield], + apply_setfield: options[:apply_setfield], apply_setfield_miss: options[:apply_setfield_miss] } end @@ -96,102 +94,120 @@ defmodule Openflow.Multipart.TableFeatures.Body do # private functions defp do_to_binary(acc, []), do: acc - defp do_to_binary(acc, [table|rest]) do - do_to_binary(<>, rest) + + defp do_to_binary(acc, [table | rest]) do + do_to_binary(<>, rest) end defp do_read(acc, ""), do: Enum.reverse(acc) + defp do_read(acc, <> = binary) do <> = binary - do_read([decode(features_bin)|acc], rest) + do_read([decode(features_bin) | acc], rest) end - defp decode(<<_length::16, table_id::8, _::size(5)-unit(8), - name_bin::size(@max_table_name_len)-bytes, - metadata_match::64, metadata_write::64, - config_int::32, max_entries::32, props_bin::bytes>>) do + defp decode( + <<_length::16, table_id::8, _::size(5)-unit(8), + name_bin::size(@max_table_name_len)-bytes, metadata_match::64, metadata_write::64, + config_int::32, max_entries::32, props_bin::bytes>> + ) do name = Openflow.Utils.decode_string(name_bin) config = Openflow.Enums.int_to_flags(config_int, :table_config) - body = %Body{table_id: table_id, - name: name, - metadata_match: metadata_match, - metadata_write: metadata_write, - config: config, - max_entries: max_entries} + + body = %Body{ + table_id: table_id, + name: name, + metadata_match: metadata_match, + metadata_write: metadata_write, + config: config, + max_entries: max_entries + } + decode_props(body, props_bin) end defp encode(table) do - filter_fn = fn(key) -> not is_nil(Map.get(table, key)) end + filter_fn = fn key -> not is_nil(Map.get(table, key)) end keys = Enum.filter(@prop_keys, filter_fn) props_bin = encode_props("", table, keys) length = @table_features_length + byte_size(props_bin) - %Body{table_id: table_id, - name: name, - metadata_match: metadata_match, - metadata_write: metadata_write, - config: config, - max_entries: max_entries} = table + + %Body{ + table_id: table_id, + name: name, + metadata_match: metadata_match, + metadata_write: metadata_write, + config: config, + max_entries: max_entries + } = table + config_int = Openflow.Enums.flags_to_int(config, :table_config) name_bin = Openflow.Utils.encode_string(name, @max_table_name_len) - <> + + <> end defp decode_props(body, ""), do: body + defp decode_props(body, <>) - when type_int == @instructions or type_int == @instructions_miss do + when type_int == @instructions or type_int == @instructions_miss do pad_length = Openflow.Utils.pad_length(length, 8) value_length = length - @prop_header_length - <> = tail + + <> = + tail + instructions = decode_instructions([], instructions_bin) type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) + body |> struct(%{type => instructions}) |> decode_props(rest) end + defp decode_props(body, <>) - when type_int == @next_tables or type_int == @next_tables_miss do + when type_int == @next_tables or type_int == @next_tables_miss do pad_length = Openflow.Utils.pad_length(length, 8) value_length = length - @prop_header_length <> = tail next_tables = for <>, do: table_id type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) + body |> struct(%{type => next_tables}) |> decode_props(rest) end + defp decode_props(body, <>) - when type_int == @write_actions or - type_int == @write_actions_miss or - type_int == @apply_actions or - type_int == @apply_actions_miss do + when type_int == @write_actions or type_int == @write_actions_miss or + type_int == @apply_actions or type_int == @apply_actions_miss do pad_length = Openflow.Utils.pad_length(length, 8) value_length = length - @prop_header_length <> = tail actions = decode_actions([], actions_bin) type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) + body |> struct(%{type => actions}) |> decode_props(rest) end + defp decode_props(body, <>) - when type_int == @match or - type_int == @wildcards or - type_int == @write_setfield or - type_int == @write_setfield_miss or - type_int == @apply_setfield or - type_int == @apply_setfield_miss do + when type_int == @match or type_int == @wildcards or type_int == @write_setfield or + type_int == @write_setfield_miss or type_int == @apply_setfield or + type_int == @apply_setfield_miss do pad_length = Openflow.Utils.pad_length(length, 8) value_length = length - @prop_header_length <> = tail matches = decode_matches([], matches_bin) type = Openflow.Enums.to_atom(type_int, :table_feature_prop_type) + body |> struct(%{type => matches}) |> decode_props(rest) end + defp decode_props(body, <<_type_int::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) value_length = length - @prop_header_length @@ -200,8 +216,9 @@ defmodule Openflow.Multipart.TableFeatures.Body do end defp encode_props(acc, _table, []), do: acc - defp encode_props(acc, table, [type|rest]) - when type == :instructions or type == :instructions_miss do + + defp encode_props(acc, table, [type | rest]) + when type == :instructions or type == :instructions_miss do type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) instructions_bin = encode_instructions("", Map.get(table, type)) length = @prop_header_length + byte_size(instructions_bin) @@ -209,8 +226,9 @@ defmodule Openflow.Multipart.TableFeatures.Body do body = <> encode_props(<>, table, rest) end - defp encode_props(acc, table, [type|rest]) - when type == :next_tables or type == :next_tables_miss do + + defp encode_props(acc, table, [type | rest]) + when type == :next_tables or type == :next_tables_miss do type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) next_tables_bin = to_string(Map.get(table, type)) length = @prop_header_length + byte_size(next_tables_bin) @@ -218,11 +236,10 @@ defmodule Openflow.Multipart.TableFeatures.Body do body = <> encode_props(<>, table, rest) end - defp encode_props(acc, table, [type|rest]) - when (type == :write_actions or - type == :write_actions_miss or - type == :apply_actions or - type == :apply_actions_miss) do + + defp encode_props(acc, table, [type | rest]) + when type == :write_actions or type == :write_actions_miss or type == :apply_actions or + type == :apply_actions_miss do type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) actions_bin = encode_actions("", Map.get(table, type)) length = @prop_header_length + byte_size(actions_bin) @@ -230,13 +247,11 @@ defmodule Openflow.Multipart.TableFeatures.Body do body = <> encode_props(<>, table, rest) end - defp encode_props(acc, table, [type|rest]) - when (type == :match or - type == :wildcards or - type == :write_setfield or - type == :write_setfield_miss or - type == :apply_setfield or - type == :apply_setfield_miss) do + + defp encode_props(acc, table, [type | rest]) + when type == :match or type == :wildcards or type == :write_setfield or + type == :write_setfield_miss or type == :apply_setfield or + type == :apply_setfield_miss do type_int = Openflow.Enums.to_int(type, :table_feature_prop_type) matches_bin = encode_matches("", Map.get(table, type)) length = @prop_header_length + byte_size(matches_bin) @@ -246,51 +261,61 @@ defmodule Openflow.Multipart.TableFeatures.Body do end defp decode_instructions(acc, ""), do: Enum.reverse(acc) - defp decode_instructions(acc, <<0xffff::16, _::16, exp_id::32, rest::bytes>>) do - decode_instructions([Openflow.Instruction.Experimenter.new(exp_id)|acc], rest) + + defp decode_instructions(acc, <<0xFFFF::16, _::16, exp_id::32, rest::bytes>>) do + decode_instructions([Openflow.Instruction.Experimenter.new(exp_id) | acc], rest) end + defp decode_instructions(acc, <>) do instruction = Openflow.Enums.to_atom(type_int, :instruction_type) - decode_instructions([instruction|acc], rest) + decode_instructions([instruction | acc], rest) end defp encode_instructions(acc, []), do: acc - defp encode_instructions(acc, [%Openflow.Instruction.Experimenter{exp_id: exp_id}|rest]) do - encode_instructions(<>, rest) + + defp encode_instructions(acc, [%Openflow.Instruction.Experimenter{exp_id: exp_id} | rest]) do + encode_instructions(<>, rest) end - defp encode_instructions(acc, [type|rest]) do + + defp encode_instructions(acc, [type | rest]) do type_int = Openflow.Enums.to_int(type, :instruction_type) encode_instructions(<>, rest) end defp decode_actions(acc, ""), do: Enum.reverse(acc) - defp decode_actions(acc, <<0xffff::16, _::16, exp_id::32, rest::bytes>>) do - decode_actions([Openflow.Action.Experimenter.new(exp_id)|acc], rest) + + defp decode_actions(acc, <<0xFFFF::16, _::16, exp_id::32, rest::bytes>>) do + decode_actions([Openflow.Action.Experimenter.new(exp_id) | acc], rest) end + defp decode_actions(acc, <>) do action = Openflow.Enums.to_atom(type_int, :action_type) - decode_actions([action|acc], rest) + decode_actions([action | acc], rest) end defp encode_actions(acc, []), do: acc - defp encode_actions(acc, [%Openflow.Action.Experimenter{exp_id: exp_id}|rest]) do - encode_actions(<>, rest) + + defp encode_actions(acc, [%Openflow.Action.Experimenter{exp_id: exp_id} | rest]) do + encode_actions(<>, rest) end - defp encode_actions(acc, [type|rest]) do + + defp encode_actions(acc, [type | rest]) do type_int = Openflow.Enums.to_int(type, :action_type) encode_actions(<>, rest) end defp decode_matches(acc, ""), do: Enum.reverse(acc) + defp decode_matches(acc, binary) do length = Openflow.Match.header_size(binary) <> = binary field = Openflow.Match.codec_header(header_bin) - decode_matches([field|acc], rest) + decode_matches([field | acc], rest) end defp encode_matches(acc, []), do: acc - defp encode_matches(acc, [field|rest]) do + + defp encode_matches(acc, [field | rest]) do header_bin = Openflow.Match.codec_header(field) encode_matches(<>, rest) end diff --git a/lib/openflow/multipart/table_features/reply.ex b/lib/openflow/multipart/table_features/reply.ex index 0d3c834..f44f4fa 100644 --- a/lib/openflow/multipart/table_features/reply.ex +++ b/lib/openflow/multipart/table_features/reply.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.TableFeatures.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - tables: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + tables: [] ) alias __MODULE__ @@ -30,12 +31,15 @@ defmodule Openflow.Multipart.TableFeatures.Reply do end def append_body(%Reply{tables: tables} = message, %Reply{flags: [:more], tables: continue}) do - %{message|tables: [continue|tables]} + %{message | tables: [continue | tables]} end + def append_body(%Reply{tables: tables} = message, %Reply{flags: [], tables: continue}) do - new_tables = [continue|tables] - |> Enum.reverse - |> List.flatten - %{message|tables: new_tables} + new_tables = + [continue | tables] + |> Enum.reverse() + |> List.flatten() + + %{message | tables: new_tables} end end diff --git a/lib/openflow/multipart/table_features/request.ex b/lib/openflow/multipart/table_features/request.ex index 3259c5d..b4b9fad 100644 --- a/lib/openflow/multipart/table_features/request.ex +++ b/lib/openflow/multipart/table_features/request.ex @@ -1,11 +1,12 @@ defmodule Openflow.Multipart.TableFeatures.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: nil, - flags: [], - tables: [] + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + aux_id: nil, + flags: [], + tables: [] ) alias __MODULE__ @@ -30,12 +31,15 @@ defmodule Openflow.Multipart.TableFeatures.Request do end def append_body(%Request{tables: tables} = message, %Request{flags: [:more], tables: continue}) do - %{message|tables: [continue|tables]} + %{message | tables: [continue | tables]} end + def append_body(%Request{tables: tables} = message, %Request{flags: [], tables: continue}) do - new_tables = [continue|tables] - |> Enum.reverse - |> List.flatten - %{message|tables: new_tables} + new_tables = + [continue | tables] + |> Enum.reverse() + |> List.flatten() + + %{message | tables: new_tables} end end diff --git a/lib/openflow/nx_packet_in2.ex b/lib/openflow/nx_packet_in2.ex index dfa8b34..77955be 100644 --- a/lib/openflow/nx_packet_in2.ex +++ b/lib/openflow/nx_packet_in2.ex @@ -1,26 +1,26 @@ defmodule Openflow.NxPacketIn2 do defstruct( - version: 4, - xid: 0, - datapath_id: "", - aux_id: 0, + 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, + packet: nil, + full_len: nil, + buffer_id: nil, + table_id: nil, + cookie: nil, + reason: nil, + metadata: nil, + userdata: nil, # continuation properties: - continuation_bridge: "", - continuation_stack: [], + continuation_bridge: "", + continuation_stack: [], continuation_conntracked: false, - continuation_table_id: nil, - continuation_cookie: nil, - continuation_actions: [], - continuation_action_set: nil + continuation_table_id: nil, + continuation_cookie: nil, + continuation_actions: [], + continuation_action_set: nil ) alias __MODULE__ @@ -28,24 +28,24 @@ defmodule Openflow.NxPacketIn2 do @experimenter 0x00002320 @nx_type 30 - @packet 0 - @full_len 1 - @buffer_id 2 - @table_id 3 - @cookie 4 - @reason 5 - @metadata 6 - @userdata 7 + @packet 0 + @full_len 1 + @buffer_id 2 + @table_id 3 + @cookie 4 + @reason 5 + @metadata 6 + @userdata 7 @continuation 8 - @nxcpt_bridge 0x8000 - @nxcpt_stack 0x8001 - @nxcpt_mirrors 0x8002 + @nxcpt_bridge 0x8000 + @nxcpt_stack 0x8001 + @nxcpt_mirrors 0x8002 @nxcpt_conntracked 0x8003 - @nxcpt_table_id 0x8004 - @nxcpt_cookie 0x8005 - @nxcpt_actions 0x8006 - @nxcpt_action_set 0x8007 + @nxcpt_table_id 0x8004 + @nxcpt_cookie 0x8005 + @nxcpt_actions 0x8006 + @nxcpt_action_set 0x8007 @prop_header_length 4 @@ -59,52 +59,66 @@ defmodule Openflow.NxPacketIn2 do ## private functions defp decode_props(pktin, ""), do: pktin + defp decode_props(pktin, <<@packet::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) packet_length = length - @prop_header_length <> = tail - decode_props(%{pktin|packet: packet}, rest) + decode_props(%{pktin | packet: packet}, rest) end + defp decode_props(pktin, <<@full_len::16, _length::16, full_len::32, rest::bytes>>) do - decode_props(%{pktin|full_len: full_len}, rest) + decode_props(%{pktin | full_len: full_len}, rest) end + defp decode_props(pktin, <<@buffer_id::16, _length::16, buffer_id::32, rest::bytes>>) do - decode_props(%{pktin|buffer_id: buffer_id}, rest) + decode_props(%{pktin | buffer_id: buffer_id}, rest) end + defp decode_props(pktin, <<@table_id::16, _length::16, table_id::8, _::24, rest::bytes>>) do - decode_props(%{pktin|table_id: table_id}, rest) + decode_props(%{pktin | table_id: table_id}, rest) end + defp decode_props(pktin, <<@cookie::16, _length::16, _::32, cookie::64, rest::bytes>>) do - decode_props(%{pktin|cookie: cookie}, rest) + decode_props(%{pktin | cookie: cookie}, rest) end + defp decode_props(pktin, <<@reason::16, _length::16, reason_int::8, _::24, rest::bytes>>) do reason = Openflow.Enums.to_atom(reason_int, :packet_in_reason) - decode_props(%{pktin|reason: reason}, rest) + decode_props(%{pktin | reason: reason}, rest) end + defp decode_props(pktin, <<@metadata::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) match_field_length = length - @prop_header_length - <> = tail + + <> = + tail + match_len = 4 + byte_size(match_fields_bin) padding = Openflow.Utils.pad_length(match_len, 8) - match_bin = (<<1::16, match_len::16, match_fields_bin::bytes, 0::size(padding)-unit(8)>>) + match_bin = <<1::16, match_len::16, match_fields_bin::bytes, 0::size(padding)-unit(8)>> {fields, _rest} = Openflow.Match.read(match_bin) - decode_props(%{pktin|metadata: fields}, rest) + decode_props(%{pktin | metadata: fields}, rest) end + defp decode_props(pktin, <<@userdata::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) userdata_length = length - @prop_header_length <> = tail - decode_props(%{pktin|userdata: userdata}, rest) + decode_props(%{pktin | userdata: userdata}, rest) end + defp decode_props(pktin, <<@continuation::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) data_length = length - @prop_header_length - 4 <<_pad::32, data::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail + pktin |> decode_continuations(data) |> decode_props(rest) end + defp decode_props(pktin, <<_::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) data_length = length - @prop_header_length @@ -113,52 +127,64 @@ defmodule Openflow.NxPacketIn2 do end defp decode_continuations(pktin, ""), do: pktin + defp decode_continuations(pktin, <<@nxcpt_bridge::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) data_length = length - @prop_header_length <> = tail - decode_continuations(%{pktin|continuation_bridge: bridge}, rest) + decode_continuations(%{pktin | continuation_bridge: bridge}, rest) end + defp decode_continuations(pktin, <<@nxcpt_stack::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) data_length = (length - @prop_header_length) * 8 <> = tail - decode_continuations(%{pktin|continuation_stack: pktin.continuation_stack ++ [stack]}, rest) + decode_continuations(%{pktin | continuation_stack: pktin.continuation_stack ++ [stack]}, rest) end + defp decode_continuations(pktin, <<@nxcpt_mirrors::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) data_length = length - @prop_header_length <> = tail - decode_continuations(%{pktin|continuation_mirrors: mirrors}, rest) + decode_continuations(%{pktin | continuation_mirrors: mirrors}, rest) end + defp decode_continuations(pktin, <<@nxcpt_conntracked::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) data_length = length - @prop_header_length <<_::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail - decode_continuations(%{pktin|continuation_conntracked: true}, rest) + decode_continuations(%{pktin | continuation_conntracked: true}, rest) end + defp decode_continuations(pktin, <<@nxcpt_table_id::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) <> = tail - decode_continuations(%{pktin|continuation_table_id: table_id}, rest) + decode_continuations(%{pktin | continuation_table_id: table_id}, rest) end + defp decode_continuations(pktin, <<@nxcpt_cookie::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) <> = tail - decode_continuations(%{pktin|continuation_cookie: cookie}, rest) + decode_continuations(%{pktin | continuation_cookie: cookie}, rest) end + defp decode_continuations(pktin, <<@nxcpt_actions::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) data_length = length - @prop_header_length - 4 - <<_pad::32, actions::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = tail - decode_continuations(%{pktin|continuation_actions: Openflow.Action.read(actions)}, rest) + + <<_pad::32, actions::size(data_length)-bytes, _::size(pad_length)-unit(8), rest::bytes>> = + tail + + decode_continuations(%{pktin | continuation_actions: Openflow.Action.read(actions)}, rest) end + defp decode_continuations(pktin, <<@nxcpt_action_set::16, length::16, tail::bytes>>) do pad_length = Openflow.Utils.pad_length(length, 8) data_length = length - @prop_header_length <> = tail - decode_continuations(%{pktin|continuation_action_set: action_set}, rest) + decode_continuations(%{pktin | continuation_action_set: action_set}, rest) end + defp decode_continuations(pktin, _) do decode_continuations(pktin, "") end diff --git a/lib/openflow/nx_packet_in_format.ex b/lib/openflow/nx_packet_in_format.ex index c5f2dd5..e42b570 100644 --- a/lib/openflow/nx_packet_in_format.ex +++ b/lib/openflow/nx_packet_in_format.ex @@ -1,9 +1,9 @@ defmodule Openflow.NxSetPacketInFormat do defstruct( - version: 4, - xid: 0, + version: 4, + xid: 0, datapath_id: "", - aux_id: 0, + aux_id: 0, format: :standard ) diff --git a/lib/openflow/nx_set_controller_id.ex b/lib/openflow/nx_set_controller_id.ex index de9c95e..f249960 100644 --- a/lib/openflow/nx_set_controller_id.ex +++ b/lib/openflow/nx_set_controller_id.ex @@ -1,8 +1,8 @@ defmodule Openflow.NxSetControllerId do defstruct( version: 4, - xid: 0, - id: 0 + xid: 0, + id: 0 ) @experimenter 0x00002320 diff --git a/lib/openflow/packet_in.ex b/lib/openflow/packet_in.ex index 415aa92..2d436e8 100644 --- a/lib/openflow/packet_in.ex +++ b/lib/openflow/packet_in.ex @@ -3,23 +3,25 @@ defmodule Openflow.PacketIn do 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: "" + 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 + 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) @@ -27,34 +29,41 @@ defmodule Openflow.PacketIn do <<_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} + + %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 + %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 - <> + [{: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 index dcadd43..42ca76b 100644 --- a/lib/openflow/packet_out.ex +++ b/lib/openflow/packet_out.ex @@ -3,11 +3,11 @@ defmodule Openflow.PacketOut do version: 4, xid: 0, datapath_id: "", - aux_id: 0, - buffer_id: :no_buffer, - in_port: :controller, - actions: [], - data: "" + aux_id: 0, + buffer_id: :no_buffer, + in_port: :controller, + actions: [], + data: "" ) alias __MODULE__ @@ -19,32 +19,26 @@ defmodule Openflow.PacketOut do 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} + %PacketOut{buffer_id: buffer_id, in_port: in_port, actions: actions, data: data} end - def read(<>) do + 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} + %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 + %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 diff --git a/lib/openflow/port.ex b/lib/openflow/port.ex index 1f71180..099b0bc 100644 --- a/lib/openflow/port.ex +++ b/lib/openflow/port.ex @@ -2,27 +2,27 @@ defmodule Openflow.Port do defstruct( number: 0, hw_addr: "000000000000", - name: "", + name: "", config: [], - state: [], - current_features: [], + state: [], + current_features: [], advertised_features: [], - supported_features: [], - peer_features: [], + supported_features: [], + peer_features: [], current_speed: :"10mb_hd", - max_speed: :"10mb_hd" + max_speed: :"10mb_hd" ) @ofp_max_port_name_len 16 alias __MODULE__ - def read(<>) do + 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) @@ -32,16 +32,19 @@ defmodule Openflow.Port do 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} + + %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 index 01506f5..2a8ff57 100644 --- a/lib/openflow/port_mod.ex +++ b/lib/openflow/port_mod.ex @@ -1,14 +1,14 @@ defmodule Openflow.PortMod do defstruct( - version: 4, - xid: 0, + version: 4, + xid: 0, datapath_id: "", - aux_id: 0, - number: 0, - hw_addr: "000000000000", - config: [], - mask: [], - advertise: [] + aux_id: 0, + number: 0, + hw_addr: "000000000000", + config: [], + mask: [], + advertise: [] ) alias __MODULE__ @@ -24,9 +24,10 @@ defmodule Openflow.PortMod do %PortMod{number: port_no, hw_addr: hw_addr, config: config, mask: mask, advertise: advertise} end - def read(<>) do + 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) @@ -35,14 +36,20 @@ defmodule Openflow.PortMod do %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 + 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>> + hw_addr_bin = <> 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 index 83ea182..9b92554 100644 --- a/lib/openflow/port_status.ex +++ b/lib/openflow/port_status.ex @@ -1,11 +1,11 @@ defmodule Openflow.PortStatus do defstruct( - version: 4, - xid: 0, + version: 4, + xid: 0, datapath_id: "", - aux_id: 0, - reason: :add, - port: nil + aux_id: 0, + reason: :add, + port: nil ) alias __MODULE__ diff --git a/lib/openflow/role/reply.ex b/lib/openflow/role/reply.ex index 9e81329..0480a0f 100644 --- a/lib/openflow/role/reply.ex +++ b/lib/openflow/role/reply.ex @@ -1,9 +1,11 @@ defmodule Openflow.Role.Reply do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: 0, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0, role: :nochange, generation_id: 0 ) diff --git a/lib/openflow/role/request.ex b/lib/openflow/role/request.ex index e2ed43e..fcb66f1 100644 --- a/lib/openflow/role/request.ex +++ b/lib/openflow/role/request.ex @@ -1,9 +1,11 @@ defmodule Openflow.Role.Request do defstruct( - version: 4, - xid: 0, - datapath_id: nil, # virtual field - aux_id: 0, # virtual field + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0, role: :nochange, generation_id: 0 ) diff --git a/lib/openflow/set_async.ex b/lib/openflow/set_async.ex index fb245d8..717d644 100644 --- a/lib/openflow/set_async.ex +++ b/lib/openflow/set_async.ex @@ -1,40 +1,46 @@ 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, + version: 4, + xid: 0, + # virtual field + datapath_id: nil, + # virtual field + aux_id: 0, + 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 + 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} + 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 - <> + 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 index 0a08ff5..18f3a8c 100644 --- a/lib/openflow/set_config.ex +++ b/lib/openflow/set_config.ex @@ -1,10 +1,11 @@ defmodule Openflow.SetConfig do defstruct( - version: 4, - xid: 0, - datapath_id: "", - aux_id: 0, - flags: [], # default = "normal" is no special handling + version: 4, + xid: 0, + datapath_id: "", + aux_id: 0, + # default = "normal" is no special handling + flags: [], miss_send_len: 128 ) diff --git a/lib/openflow/table_mod.ex b/lib/openflow/table_mod.ex index d06f45b..2ea364a 100644 --- a/lib/openflow/table_mod.ex +++ b/lib/openflow/table_mod.ex @@ -1,11 +1,11 @@ defmodule Openflow.TableMod do defstruct( - version: 4, - xid: 0, + version: 4, + xid: 0, datapath_id: "", - aux_id: 0, - table_id: 0, - config: 0 + aux_id: 0, + table_id: 0, + config: 0 ) alias __MODULE__ diff --git a/lib/openflow/utils.ex b/lib/openflow/utils.ex index afe8cf8..79456f0 100644 --- a/lib/openflow/utils.ex +++ b/lib/openflow/utils.ex @@ -1,16 +1,19 @@ 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) + 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 + + 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 + + def flags_to_int(acc0, [flag | rest], enum) do acc = acc0 ||| Keyword.get(enum, flag, 0) flags_to_int(acc, rest, enum) end @@ -18,14 +21,14 @@ defmodule Openflow.Utils do def to_hex_string(binary), do: datapath_id(binary) def padding(length, padding) do - case (padding - rem(length, padding)) do + case padding - rem(length, padding) do ^padding -> 0 - pad_len -> pad_len + pad_len -> pad_len end end def pad_length(length, width) do - rem((width - rem(length, width)), width) + rem(width - rem(length, width), width) end def decode_string(binary) do @@ -43,6 +46,7 @@ defmodule Openflow.Utils do :bad_enum -> int end end + def get_enum(int, type) do try do Openflow.Enums.to_int(int, type) @@ -57,7 +61,7 @@ defmodule Openflow.Utils do binary |> split_to_hex_string |> Enum.join("") - |> String.downcase + |> String.downcase() end defp split_to_hex_string(binary) do @@ -67,7 +71,7 @@ defmodule Openflow.Utils do defp integer_to_hex(int) do case Integer.to_string(int, 16) do <> -> <<48, d>> - dd -> dd + dd -> dd end end end diff --git a/lib/ovsdb/open_vswitch.ex b/lib/ovsdb/open_vswitch.ex index 67baaa9..43b15ca 100644 --- a/lib/ovsdb/open_vswitch.ex +++ b/lib/ovsdb/open_vswitch.ex @@ -2,21 +2,19 @@ defmodule OVSDB.OpenvSwitch do use GenServer defmodule State do - defstruct [ - server: nil, - client_pid: nil, - monitor_pid: nil, - ovs_uuid: nil - ] + defstruct server: nil, + client_pid: nil, + monitor_pid: nil, + ovs_uuid: nil end @database "Open_vSwitch" @open_vswitch "Open_vSwitch" - @interface "Interface" - @port "Port" - @controller "Controller" - @bridge "Bridge" + @interface "Interface" + @port "Port" + @controller "Controller" + @bridge "Bridge" def start_link(server) do GenServer.start_link(__MODULE__, [server]) @@ -28,13 +26,15 @@ defmodule OVSDB.OpenvSwitch do def add_br(pid, bridge, options \\ []) do br_uuids = GenServer.call(pid, {:sync_get, @open_vswitch, "bridges"}) + add_br_options = [ - bridge: bridge, + bridge: bridge, controller: options[:controller] || "tcp:127.0.0.1:6653", - protocol: options[:protocol] || "OpenFlow13", - fail_mode: options[:fail_mode] || "standalone", - br_uuids: br_uuids + protocol: options[:protocol] || "OpenFlow13", + fail_mode: options[:fail_mode] || "standalone", + br_uuids: br_uuids ] + GenServer.call(pid, {:add_br, add_br_options}) end @@ -44,92 +44,120 @@ defmodule OVSDB.OpenvSwitch do new_bridges = case GenServer.call(pid, {:sync_get, @open_vswitch, "bridges"}) do ["set", bridges] -> %{"bridges" => ["set", bridges -- [uuid]]} - ^uuid -> %{"bridges" => ["set", []]} - curr_bridges -> %{"bridges" => curr_bridges} + ^uuid -> %{"bridges" => ["set", []]} + curr_bridges -> %{"bridges" => curr_bridges} end + GenServer.call(pid, {:del_br, new_bridges}) + :not_found -> {:error, :not_found} end end def init([server]) do - state = server - |> String.to_charlist - |> init_client + state = + server + |> String.to_charlist() + |> init_client + {:ok, state} end def handle_call({:sync_get, table, col_name}, _from, state) do - [%{"rows" => [%{^col_name => values}|_]}] = + [%{"rows" => [%{^col_name => values} | _]}] = [col_name] |> :eovsdb_op.select(table, []) |> xact(state.client_pid) + {:reply, values, state} end + def handle_call({:add_br, options}, _from, state) do %State{client_pid: pid, ovs_uuid: ovs} = state br_iface = %{name: options[:bridge], type: :internal} br_port = %{name: options[:bridge], interfaces: ["named-uuid", "brinterface"]} controller = %{target: options[:controller]} + new_bridge = %{ - name: options[:bridge], - ports: ["named-uuid", "brport"], + name: options[:bridge], + ports: ["named-uuid", "brport"], controller: ["named-uuid", "brcontroller"], - fail_mode: options[:fail_mode], - protocols: options[:protocol] + fail_mode: options[:fail_mode], + protocols: options[:protocol] } + named_uuid = ["named-uuid", "bridge"] + new_bridges = case options[:br_uuids] do - ["set", []] -> %{bridges: named_uuid} - ["set", bridges] -> %{bridges: ["set", bridges ++ [named_uuid]]} + ["set", []] -> %{bridges: named_uuid} + ["set", bridges] -> %{bridges: ["set", bridges ++ [named_uuid]]} ["uuid", _] = bridge -> %{bridges: ["set", [bridge] ++ [named_uuid]]} end + next_config = [{"next_cfg", "+=", 1}] eq_ovs_uuid = [{"_uuid", "==", ovs}] - replies = xact([ - :eovsdb_op.insert(@interface, br_iface, "brinterface"), - :eovsdb_op.insert(@port, br_port, "brport"), - :eovsdb_op.insert(@controller, controller, "brcontroller"), - :eovsdb_op.insert(@bridge, new_bridge, "bridge"), - :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), - :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) - ], pid) + + replies = + xact( + [ + :eovsdb_op.insert(@interface, br_iface, "brinterface"), + :eovsdb_op.insert(@port, br_port, "brport"), + :eovsdb_op.insert(@controller, controller, "brcontroller"), + :eovsdb_op.insert(@bridge, new_bridge, "bridge"), + :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), + :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) + ], + pid + ) + {:reply, replies, state} end + def handle_call({:del_br, new_bridges}, _from, state) do %State{client_pid: pid, ovs_uuid: ovs} = state eq_ovs_uuid = [{"_uuid", "==", ovs}] next_config = [{"next_cfg", "+=", 1}] - replies = xact([ - :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), - :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) - ], pid) + + replies = + xact( + [ + :eovsdb_op.update(@open_vswitch, eq_ovs_uuid, new_bridges), + :eovsdb_op.mutate(@open_vswitch, eq_ovs_uuid, next_config) + ], + pid + ) + {:reply, replies, state} end + def handle_call({:find_by_name, table, name}, _from, state) do %State{client_pid: pid} = state query = :eovsdb_op.select('*', table, [{"name", "==", name}]) - reply = case xact(query, pid) do - [%{"rows" => []}] -> :not_found - [%{"rows" => [row]}] -> row - end + + reply = + case xact(query, pid) do + [%{"rows" => []}] -> :not_found + [%{"rows" => [row]}] -> row + end + {:reply, reply, state} end def handle_cast({:async_get, "_uuid"}, state) do - [%{"rows" => [%{"_uuid" => values}|_]}] = + [%{"rows" => [%{"_uuid" => values} | _]}] = ["_uuid"] |> :eovsdb_op.select(@open_vswitch, []) |> xact(state.client_pid) - {:noreply, %{state|ovs_uuid: values}} + + {:noreply, %{state | ovs_uuid: values}} end # private functions defp init_client(server) do - {:ok, pid} = :eovsdb_client.connect(server, [database: @database]) + {:ok, pid} = :eovsdb_client.connect(server, database: @database) :eovsdb_client.regist_schema(pid) :ok = GenServer.cast(self(), {:async_get, "_uuid"}) %State{server: server, client_pid: pid} @@ -139,6 +167,7 @@ defmodule OVSDB.OpenvSwitch do {:ok, res} = :eovsdb_client.transaction(pid, query) res end + defp xact(query, pid) when is_map(query) do xact([query], pid) end diff --git a/lib/tres/application.ex b/lib/tres/application.ex index 5af2b67..f5ecd20 100644 --- a/lib/tres/application.ex +++ b/lib/tres/application.ex @@ -8,12 +8,16 @@ defmodule Tres.Application do def start(_type, _args) do import Supervisor.Spec - {cb_mod, _cb_args} = Tres.Utils.get_callback_module - children = [worker(Registry, [[keys: :unique, name: SwitchRegistry]], id: SwitchRegistry), - supervisor(Tres.MessageHandlerSup, [cb_mod], id: MessageHandlerSup), - supervisor(OVSDB, [], id: OVSDB)] + {cb_mod, _cb_args} = Tres.Utils.get_callback_module() + + children = [ + worker(Registry, [[keys: :unique, name: SwitchRegistry]], id: SwitchRegistry), + supervisor(Tres.MessageHandlerSup, [cb_mod], id: MessageHandlerSup), + supervisor(OVSDB, [], id: OVSDB) + ] + opts = [strategy: :one_for_one, name: Tres.Supervisor] - {:ok, _connection_pid} = Tres.Utils.start_openflow_listener + {:ok, _connection_pid} = Tres.Utils.start_openflow_listener() Supervisor.start_link(children, opts) end end diff --git a/lib/tres/example_handler.ex b/lib/tres/example_handler.ex index ded3129..5e9a7b7 100644 --- a/lib/tres/example_handler.ex +++ b/lib/tres/example_handler.ex @@ -5,11 +5,9 @@ defmodule Tres.ExampleHandler do import Logger defmodule State do - defstruct [ - datapath_id: nil, - aux_id: nil, - conn_ref: nil - ] + defstruct datapath_id: nil, + aux_id: nil, + conn_ref: nil end def start_link(datapath, args) do @@ -17,10 +15,11 @@ defmodule Tres.ExampleHandler do end def init([{datapath_id, aux_id}, _args]) do - info("[#{__MODULE__}] Switch Ready: " - <> "datapath_id: #{datapath_id} " - <> "aux_id: #{aux_id} " - <> "on #{inspect(self())}") + info( + "[#{__MODULE__}] Switch Ready: " <> + "datapath_id: #{datapath_id} " <> "aux_id: #{aux_id} " <> "on #{inspect(self())}" + ) + _ = send_desc_stats_request(datapath_id) _ = send_port_desc_stats_request(datapath_id) state = %State{datapath_id: datapath_id, aux_id: aux_id} @@ -31,18 +30,24 @@ defmodule Tres.ExampleHandler do handle_port_desc_stats_reply(desc, datapath_id) {:noreply, state} end + def handle_info(%Desc.Reply{datapath_id: datapath_id} = desc, state) do handle_desc_stats_reply(desc, datapath_id) {:noreply, state} end + def handle_info({:switch_disconnected, reason}, state) do - :ok = warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id} by #{reason}") + :ok = + warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id} by #{reason}") + {:stop, :normal, state} end + def handle_info({:switch_hang, _datapath_id}, state) do :ok = warn("[#{__MODULE__}] Switch possible hang: datapath_id: #{state.datapath_id}") {:noreply, state} end + # `Catch all` function is required. def handle_info(info, state) do :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}: #{state.datapath_id}") @@ -52,35 +57,32 @@ defmodule Tres.ExampleHandler do # private functions defp send_desc_stats_request(datapath_id) do - Desc.Request.new + Desc.Request.new() |> send_message(datapath_id) end defp send_port_desc_stats_request(datapath_id) do - PortDesc.Request.new + PortDesc.Request.new() |> send_message(datapath_id) end defp handle_desc_stats_reply(desc, datapath_id) do info( - "[#{__MODULE__}] Switch Desc: " - <> "mfr = #{desc.mfr_desc} " - <> "hw = #{desc.hw_desc} " - <> "sw = #{desc.sw_desc} " - <> "for #{datapath_id}" + "[#{__MODULE__}] Switch Desc: " <> + "mfr = #{desc.mfr_desc} " <> + "hw = #{desc.hw_desc} " <> "sw = #{desc.sw_desc} " <> "for #{datapath_id}" ) end defp handle_port_desc_stats_reply(port_desc, datapath_id) do for port <- port_desc.ports do info( - "[#{__MODULE__}] Switch has port: " - <> "number = #{port.number} " - <> "hw_addr = #{port.hw_addr} " - <> "name = #{port.name} " - <> "config = #{inspect(port.config)} " - <> "current_speed = #{port.current_speed} " - <> "on #{datapath_id}" + "[#{__MODULE__}] Switch has port: " <> + "number = #{port.number} " <> + "hw_addr = #{port.hw_addr} " <> + "name = #{port.name} " <> + "config = #{inspect(port.config)} " <> + "current_speed = #{port.current_speed} " <> "on #{datapath_id}" ) end end diff --git a/lib/tres/message_handler_sup.ex b/lib/tres/message_handler_sup.ex index 9c250f6..b847bff 100644 --- a/lib/tres/message_handler_sup.ex +++ b/lib/tres/message_handler_sup.ex @@ -11,7 +11,7 @@ defmodule Tres.MessageHandlerSup do end def start_child({dpid, aux_id}) do - {_cb_mod, cb_args} = Tres.Utils.get_callback_module + {_cb_mod, cb_args} = Tres.Utils.get_callback_module() Supervisor.start_child(__MODULE__, [{dpid, aux_id}, cb_args]) end end diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index 1445288..87d6a00 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -3,7 +3,7 @@ defmodule Tres.MessageHelper do quote location: :keep do defp send_flow_mod_add(datapath_id, options \\ []) do flow_mod = %Openflow.FlowMod{ - cookie: options[:cookie] || 0, + cookie: options[:cookie] || 0, priority: options[:priority] || 0, table_id: options[:table_id] || 0, command: :add, @@ -11,41 +11,46 @@ defmodule Tres.MessageHelper do hard_timeout: options[:hard_timeout] || 0, buffer_id: :no_buffer, out_port: :any, - out_group: :any, + out_group: :any, flags: options[:flags] || [], - match: options[:match] || Openflow.Match.new, - instructions: options[:instructions] || [], + match: options[:match] || Openflow.Match.new(), + instructions: options[:instructions] || [] } + send_message(flow_mod, datapath_id) end defp send_flow_mod_modify(datapath_id, options \\ []) do command = Tres.Utils.flow_command(:modify, options) + flow_mod = %Openflow.FlowMod{ - cookie: options[:cookie] || 0, + cookie: options[:cookie] || 0, table_id: options[:table_id] || 0, command: command, idle_timeout: options[:idle_timeout] || 0, hard_timeout: options[:hard_timeout] || 0, out_port: :any, out_group: :any, - match: options[:match] || Openflow.Match.new, - instructions: options[:instructions] || [], + match: options[:match] || Openflow.Match.new(), + instructions: options[:instructions] || [] } + send_message(flow_mod, datapath_id) end defp send_flow_mod_delete(datapath_id, options \\ []) do command = Tres.Utils.flow_command(:delete, options) + flow_mod = %Openflow.FlowMod{ - cookie: options[:cookie] || 0, + cookie: options[:cookie] || 0, cookie_mask: options[:cookie_mask] || 0, table_id: options[:table_id] || :all, command: command, out_port: options[:out_port] || :any, out_group: options[:out_group] || :any, - match: options[:match] || Openflow.Match.new + match: options[:match] || Openflow.Match.new() } + send_message(flow_mod, datapath_id) end @@ -56,16 +61,19 @@ defmodule Tres.MessageHelper do actions: options[:actions] || [], data: options[:data] || "" } + send_message(packet_out, datapath_id) end defp send_group_mod_add(datapath_id, options \\ []) do - group_mod = Openflow.GroupMod.new( - command: :add, - type: options[:type] || :all, - group_id: options[:group_id] || 0, - buckets: options[:buckets] || [] - ) + group_mod = + Openflow.GroupMod.new( + command: :add, + type: options[:type] || :all, + group_id: options[:group_id] || 0, + buckets: options[:buckets] || [] + ) + send_message(group_mod, datapath_id) end @@ -75,12 +83,14 @@ defmodule Tres.MessageHelper do end defp send_group_mod_modify(datapath_id, options) do - group_mod = Openflow.GroupMod.new( - command: :modify, - type: options[:type] || :all, - group_id: options[:group_id] || 0, - buckets: options[:buckets] || [] - ) + group_mod = + Openflow.GroupMod.new( + command: :modify, + type: options[:type] || :all, + group_id: options[:group_id] || 0, + buckets: options[:buckets] || [] + ) + send_message(group_mod, datapath_id) end end diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 78c3350..b486182 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -4,21 +4,21 @@ defmodule Tres.SecureChannel do import Logger alias :tres_xact_kv, as: XACT_KV - alias :queue, as: Queue + alias :queue, as: Queue alias Tres.SecureChannelState, as: State alias Tres.SwitchRegistry alias Tres.MessageHandlerSup @process_flags [ - trap_exit: true, + trap_exit: true, message_queue_data: :on_heap ] @supported_version 4 - @hello_handshake_timeout 1000 + @hello_handshake_timeout 1000 @features_handshake_timeout 1000 - @ping_timeout 5000 + @ping_timeout 5000 # @transaction_timeout 5000 @ping_interval 5000 @@ -35,47 +35,73 @@ defmodule Tres.SecureChannel do def init([ref, socket, transport, _opts]) do state_data = init_secure_channel(ref, socket, transport) - debug("[#{__MODULE__}] TCP connected to Switch on" - <> " #{state_data.ip_addr}:#{state_data.port}" - <> " on #{inspect(self())}") + + debug( + "[#{__MODULE__}] TCP connected to Switch on" <> + " #{state_data.ip_addr}:#{state_data.port}" <> " on #{inspect(self())}" + ) + :gen_statem.enter_loop(__MODULE__, [debug: [:debug]], :INIT, state_data, []) end # TCP handler - def handle_event(:info, {:tcp, socket, packet}, state, - %State{socket: socket, transport: transport} = state_data) do - transport.setopts(socket, [active: :once]) + def handle_event( + :info, + {:tcp, socket, packet}, + state, + %State{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, - %State{socket: socket} = state_data) do + + def handle_event(:info, {:tcp_closed, socket}, _state, %State{socket: socket} = state_data) do close_connection(:tcp_closed, state_data) end - def handle_event(:info, {:tcp_error, socket, reason}, _state, - %State{socket: socket} = state_data) do + + def handle_event( + :info, + {:tcp_error, socket, reason}, + _state, + %State{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 + + 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 + + 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, - %State{datapath_id: datapath_id, aux_id: aux_id, xact_kv_ref: kv_ref}) do + def terminate(reason, state, %State{ + datapath_id: datapath_id, + aux_id: aux_id, + xact_kv_ref: kv_ref + }) do warn("[#{__MODULE__}] termiate: #{inspect(reason)} state = #{inspect(state)}") true = XACT_KV.drop(kv_ref) :ok = SwitchRegistry.unregister({datapath_id, aux_id}) @@ -85,8 +111,8 @@ defmodule Tres.SecureChannel do defp init_secure_channel(ref, socket, transport) do init_process(ref) - :ok = transport.setopts(socket, [active: :once]) - kv_ref = XACT_KV.create + :ok = transport.setopts(socket, active: :once) + kv_ref = XACT_KV.create() State.new(ref: ref, socket: socket, transport: transport, xact_kv_ref: kv_ref) end @@ -100,52 +126,76 @@ defmodule Tres.SecureChannel do %State{datapath_id: dpid, aux_id: aux_id} = state_data {:ok, pid} = MessageHandlerSup.start_child({dpid, aux_id}) ref = Process.monitor(pid) - %{state_data|handler_pid: pid, handler_ref: ref} + %{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}") + 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)}") + 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}") + 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 - debug("[#{__MODULE__}] Switch connected "<> - "datapath_id: #{features.datapath_id}"<> - " auxiliary_id: #{features.aux_id}") + + defp handle_CONNECTING( + :internal, + {:openflow, %Openflow.Features.Reply{} = features}, + state_data + ) do + debug( + "[#{__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__)}") + 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 @@ -156,46 +206,61 @@ defmodule Tres.SecureChannel do start_periodic_idle_check() {:keep_state, new_state_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}}, - %State{ping_xid: xid} = state_data) do + + defp handle_CONNECTED( + :internal, + {:openflow, %Openflow.Echo.Reply{xid: xid}}, + %State{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, %Openflow.Barrier.Reply{} = barrier}, state_data) do {new_state_data, next_actions} = process_xact(barrier, state_data) {:keep_state, new_state_data, next_actions} end + defp handle_CONNECTED(:internal, {:openflow, message}, state_data) do %State{datapath_id: dpid, aux_id: aux_id} = state_data - new_message = %{message|datapath_id: dpid, aux_id: aux_id} + new_message = %{message | datapath_id: dpid, aux_id: aux_id} + state_data.xact_kv_ref |> XACT_KV.is_exists(message.xid) |> handle_message(new_message, state_data) + :keep_state_and_data end + defp handle_CONNECTED(:internal, {:send_message, message}, state_data) do xactional_send_message(message, state_data) :keep_state_and_data end + defp handle_CONNECTED(:cast, {:send_message, message} = action, state_data) do - new_action_queue = if XACT_KV.is_empty(state_data.xact_kv_ref) do - xactional_send_message(message, state_data) - state_data.action_queue - else - Queue.in(action, state_data.action_queue) - end - {:keep_state, %{state_data|action_queue: new_action_queue}} + new_action_queue = + if XACT_KV.is_empty(state_data.xact_kv_ref) do + xactional_send_message(message, state_data) + state_data.action_queue + else + Queue.in(action, state_data.action_queue) + end + + {:keep_state, %{state_data | action_queue: new_action_queue}} end # WATING state @@ -206,21 +271,25 @@ defmodule Tres.SecureChannel do 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 %State{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data - send(handler_pid, %{message|datapath_id: dpid, aux_id: aux_id}) + send(handler_pid, %{message | datapath_id: dpid, aux_id: aux_id}) {:next_state, :CONNECTING, state_data} end + defp handle_WATING(type, message, state_data) - when type == :cast or type == :call do + when type == :cast or type == :call do debug("[#{__MODULE__}] Postponed: #{inspect(message)}, now WATING") {:keep_state, state_data, [{:postpone, true}]} end @@ -228,17 +297,24 @@ defmodule Tres.SecureChannel do defp handle_packet("", state_data, _state, actions) do {:keep_state, state_data, Enum.reverse(actions)} end + defp handle_packet(packet, %State{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}") + 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]) + 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) + handle_packet("", %{state_data | buffer: binary}, state, actions) + {:error, _reason} -> handle_packet("", state_data, state, actions) end @@ -246,20 +322,24 @@ defmodule Tres.SecureChannel do defp handle_message(_in_xact = true, message, state_data) do case XACT_KV.get(state_data.xact_kv_ref, message.xid) do - [{:xact_entry, _xid, prev_message, _orig}|_] -> + [{:xact_entry, _xid, prev_message, _orig} | _] -> new_message = Openflow.append_body(prev_message, message) XACT_KV.update(state_data.xact_kv_ref, message.xid, new_message) + _ -> XACT_KV.delete(state_data.xact_kv_ref, message.xid) end end + defp handle_message(_in_xact = false, message, state_data), do: send(state_data.handler_pid, message) defp process_xact(%Openflow.Barrier.Reply{xid: xid}, state_data) do - :ok = state_data.xact_kv_ref - |> XACT_KV.get(xid) - |> Enum.each(&process_xact_entry(&1, state_data)) + :ok = + state_data.xact_kv_ref + |> XACT_KV.get(xid) + |> Enum.each(&process_xact_entry(&1, state_data)) + pop_action_queue(state_data) end @@ -273,23 +353,26 @@ defmodule Tres.SecureChannel do case Queue.out(queue) do {:empty, action_queue} -> {[], action_queue} + {{:value, next_action}, action_queue} -> {[{:next_event, :internal, next_action}], action_queue} end - {%{state_data|action_queue: new_queue}, next_actions} + + {%{state_data | action_queue: new_queue}, next_actions} 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}} + {:keep_state, %{state_data | timer_ref: ref}} end defp handle_hello_handshake_1(hello, state_data) do maybe_cancel_timer(state_data.timer_ref) State.set_transaction_id(state_data.xid, hello.xid) + if Openflow.Hello.supported_version?(hello) do - {:next_state, :CONNECTING, %{state_data|timer_ref: nil}} + {:next_state, :CONNECTING, %{state_data | timer_ref: nil}} else close_connection(:failed_version_negotiation, state_data) end @@ -299,45 +382,49 @@ defmodule Tres.SecureChannel do new_xid = State.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}} + {: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 + 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) + 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: SwitchRegistry.monitor(datapath_id) - defp monitor_connection(_datapath_id, _aux_id), - do: nil + + defp monitor_connection(_datapath_id, _aux_id), do: nil defp send_hello(state_data) do @supported_version - |> Openflow.Hello.new + |> Openflow.Hello.new() |> send_message(state_data) end defp send_features(xid, state_data) do - %{Openflow.Features.Request.new|xid: xid} + %{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} + %{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} + %{Openflow.Echo.Request.new(data) | xid: xid} |> send_message(state_data) end @@ -347,14 +434,15 @@ defmodule Tres.SecureChannel do defp maybe_ping(state_data) do case should_be_ping?(state_data) do - true -> send_ping(state_data) + true -> send_ping(state_data) false -> state_data end end defp should_be_ping?(%State{last_received: last_received, aux_id: 0}) do - :timer.now_diff(:os.timestamp(), last_received) > (1000 * @ping_interval) + :timer.now_diff(:os.timestamp(), last_received) > 1000 * @ping_interval end + defp should_be_ping?(_) do false end @@ -363,32 +451,36 @@ defmodule Tres.SecureChannel do xid = State.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} + %{state_data | ping_timer_ref: ping_ref, ping_xid: xid} end defp handle_ping_timeout(%State{ping_fail_count: fail_count} = state_data, :CONNECTING) - when fail_count > @ping_fail_max_count do + when fail_count > @ping_fail_max_count do {:next_state, :WAITING, state_data} end + defp handle_ping_timeout(%State{ping_fail_count: fail_count} = state_data, :WAITING) - when fail_count > @ping_fail_max_count do + 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}} + {:keep_state, %{state_data | ping_timer_ref: nil, ping_xid: nil}} end defp xactional_send_message(message, state_data) do xid = State.increment_transaction_id(state_data.xid) + messages = [ - %{message|xid: xid}, - %{Openflow.Barrier.Request.new|xid: xid} + %{message | xid: xid}, + %{Openflow.Barrier.Request.new() | xid: xid} ] + XACT_KV.insert(state_data.xact_kv_ref, xid, message) send_message(messages, state_data) end @@ -396,83 +488,101 @@ defmodule Tres.SecureChannel do defp send_message(message, %State{socket: socket, transport: transport}) do if is_list(message) do for message <- message, - do: debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") + do: + debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") else debug("[#{__MODULE__}] Sending: #{inspect(message.__struct__)}(xid: #{message.xid})") end + 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}, - %State{main_monitor_ref: mon_ref} = state_data) do + defp handle_signal( + {:DOWN, mon_ref, :process, _main_pid, reason}, + %State{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}, - %State{handler_ref: handler_ref} = state_data) do + + defp handle_signal( + {:DOWN, handler_ref, :process, _main_pid, reason}, + %State{handler_ref: handler_ref} = state_data + ) do close_connection({:handler_down, reason}, state_data) end - defp handle_signal({:'EXIT', _pid, reason}, state_data) do + + 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}} + {: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}} + {: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}} + {:stop, :normal, %{state_data | socket: nil}} end + defp close_connection(:handler_error = disconnected_reason, state_data) do warn("[#{__MODULE__}] connection terminated: Got handler error") %State{handler_pid: handler_pid} = state_data send(handler_pid, {:switch_disconnected, disconnected_reason}) - {:stop, :normal, %{state_data|socket: nil}} + {:stop, :normal, %{state_data | socket: nil}} end + defp close_connection(:ping_failed = disconnected_reason, state_data) do warn("[#{__MODULE__}] connection terminated: Exceeded to max_ping_fail_count") %State{handler_pid: handler_pid} = state_data send(handler_pid, {:switch_disconnected, disconnected_reason}) - {:stop, :normal, %{state_data|socket: nil}} + {:stop, :normal, %{state_data | socket: nil}} end + defp close_connection({:main_closed = disconnected_reason, reason}, state_data) do warn("[#{__MODULE__}] connection terminated: Main connection down by #{reason}") %State{handler_pid: handler_pid} = state_data send(handler_pid, {:switch_disconnected, disconnected_reason}) - {:stop, :normal, %{state_data|socket: nil}} + {:stop, :normal, %{state_data | socket: nil}} end + defp close_connection({:handler_down = disconnected_reason, reason}, state_data) do warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}") %State{handler_pid: handler_pid} = state_data send(handler_pid, {:switch_disconnected, disconnected_reason}) - {:stop, :normal, %{state_data|socket: nil}} + {:stop, :normal, %{state_data | socket: nil}} end + defp close_connection({:trap_detected = disconnected_reason, reason}, state_data) do warn("[#{__MODULE__}] connection terminated: Trapped by #{reason}") %State{handler_pid: handler_pid} = state_data send(handler_pid, {:switch_disconnected, disconnected_reason}) - {:stop, :normal, %{state_data|socket: nil}} + {:stop, :normal, %{state_data | socket: nil}} end + defp close_connection(:tcp_closed = disconnected_reason, state_data) do warn("[#{__MODULE__}] connection terminated: TCP Closed by peer") %State{handler_pid: handler_pid} = state_data send(handler_pid, {:switch_disconnected, disconnected_reason}) - {:stop, :normal, %{state_data|socket: nil}} + {:stop, :normal, %{state_data | socket: nil}} end + defp close_connection({:tcp_error, reason} = disconnected_reason, state_data) do warn("[#{__MODULE__}] connection terminated: TCP Error occured: #{reason}") %State{handler_pid: handler_pid} = state_data send(handler_pid, {:switch_disconnected, disconnected_reason}) - {:stop, :normal, %{state_data|socket: nil}} + {:stop, :normal, %{state_data | socket: nil}} end end diff --git a/lib/tres/secure_channel_state.ex b/lib/tres/secure_channel_state.ex index 71f04ff..4d1c29b 100644 --- a/lib/tres/secure_channel_state.ex +++ b/lib/tres/secure_channel_state.ex @@ -1,24 +1,24 @@ 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, - xact_kv_ref: nil, - action_queue: :queue.new + 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, + xact_kv_ref: nil, + action_queue: :queue.new() ) alias __MODULE__ @@ -30,27 +30,28 @@ defmodule Tres.SecureChannelState do transport = Keyword.get(options, :transport) {:ok, {ip_addr, port}} = :inet.peername(socket) {:ok, xid_agent} = Agent.start_link(fn -> 0 end) - kv_ref = XACT_KV.create + kv_ref = XACT_KV.create() + %SecureChannelState{ - ref: ref, - socket: socket, + ref: ref, + socket: socket, transport: transport, - ip_addr: :inet.ntoa(ip_addr), - port: port, - xid: xid_agent, + ip_addr: :inet.ntoa(ip_addr), + port: port, + xid: xid_agent, xact_kv_ref: kv_ref } end def increment_transaction_id(xid_agent) do - Agent.get_and_update(xid_agent, &({&1 + 1, &1 + 1})) + 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) + Agent.update(xid_agent, fn _ -> xid end) end def get_transaction_id(xid_agent) do - Agent.get(xid_agent, &(&1)) + Agent.get(xid_agent, & &1) end end diff --git a/lib/tres/switch_registry.ex b/lib/tres/switch_registry.ex index 1145ee3..40653b6 100644 --- a/lib/tres/switch_registry.ex +++ b/lib/tres/switch_registry.ex @@ -13,6 +13,7 @@ defmodule Tres.SwitchRegistry do [] -> nil end end + def lookup_pid(datapath_id) do lookup_pid({datapath_id, 0}) end @@ -20,6 +21,7 @@ defmodule Tres.SwitchRegistry do 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 @@ -27,7 +29,7 @@ defmodule Tres.SwitchRegistry do def monitor(datapath_id) do datapath_id |> lookup_pid - |> Process.monitor + |> Process.monitor() end # private function diff --git a/lib/tres/utils.ex b/lib/tres/utils.ex index b99f3a1..e215ff0 100644 --- a/lib/tres/utils.ex +++ b/lib/tres/utils.ex @@ -3,8 +3,8 @@ defmodule Tres.Utils do @connection_manager Tres.SecureChannel @default_max_connections 10 - @default_num_acceptors 10 - @default_openflow_port 6633 + @default_num_acceptors 10 + @default_openflow_port 6633 def get_callback_module do cb_mod = get_config(:callback_module, Tres.ExampleHandler) @@ -32,7 +32,7 @@ defmodule Tres.Utils do def is_multipart?(message) do message.__struct__ - |> Module.split + |> Module.split() |> Enum.at(1) |> String.match?(~r/Multipart/) end @@ -44,6 +44,7 @@ defmodule Tres.Utils do :delete end end + def flow_command(:modify, options) do if Keyword.get(options, :strict, false) do :modify_strict diff --git a/mix.exs b/mix.exs index ffbf3d9..3f60fca 100644 --- a/mix.exs +++ b/mix.exs @@ -2,25 +2,28 @@ defmodule Tres.Mixfile do use Mix.Project def project do - [app: :tres, - version: "0.1.0", - elixir: "~> 1.5", - start_permanent: Mix.env == :prod, - compilers: [:erlang] ++ Mix.compilers, - deps: deps(), - aliases: [test: "test --no-start"]] + [ + app: :tres, + version: "0.1.0", + elixir: "~> 1.5", + start_permanent: Mix.env() == :prod, + compilers: [:erlang] ++ Mix.compilers(), + deps: deps(), + aliases: [test: "test --no-start"] + ] end # Run "mix help compile.app" to learn about applications. def application do - [extra_applications: [:logger, :ranch, :eovsdb], - mod: {Tres.Application, []}] + [extra_applications: [:logger, :ranch, :eovsdb], mod: {Tres.Application, []}] end # Run "mix help deps" to learn about dependencies. defp deps do - [{:ranch, "~> 1.4.0"}, - {:eovsdb, github: "shun159/eovsdb", branch: "master"}, - {:epcap, github: "msantos/epcap", branch: "master", only: :test}] + [ + {:ranch, "~> 1.4.0"}, + {:eovsdb, github: "shun159/eovsdb", branch: "master"}, + {:epcap, github: "msantos/epcap", branch: "master", only: :test} + ] end end diff --git a/test/flay.ex b/test/flay.ex index a6fbccf..2c26cf5 100644 --- a/test/flay.ex +++ b/test/flay.ex @@ -5,13 +5,11 @@ defmodule Flay do import Logger defmodule State do - defstruct [ - datapath_id: nil, - tester_pid: nil, - conn_ref: nil, - reply_to: nil, - default_profile: nil, - ] + defstruct datapath_id: nil, + tester_pid: nil, + conn_ref: nil, + reply_to: nil, + default_profile: nil end def start_link(datapath, args) do @@ -26,32 +24,37 @@ defmodule Flay do end def handle_call(:port_desc_stats, from, state) do - send_message(PortDesc.Request.new, state.datapath_id) - {:noreply, %{state|reply_to: from}} + send_message(PortDesc.Request.new(), state.datapath_id) + {:noreply, %{state | reply_to: from}} end + def handle_call(:flow_stats, from, state) do - send_message(Flow.Request.new, state.datapath_id) - {:noreply, %{state|reply_to: from}} + send_message(Flow.Request.new(), state.datapath_id) + {:noreply, %{state | reply_to: from}} end def handle_cast(:desc_stats, state) do - send_message(Desc.Request.new, state.datapath_id) + send_message(Desc.Request.new(), state.datapath_id) {:noreply, state} end + def handle_cast({:register_pid, tester_pid}, state) do - {:noreply, %{state|tester_pid: tester_pid}} + {:noreply, %{state | tester_pid: tester_pid}} end + def handle_cast({:flow_install, flow_opts, tester_pid}, state) do send_flow_mod_add(state.datapath_id, flow_opts) flow_opts_to_ofp_print(flow_opts) - {:noreply, %{state|tester_pid: tester_pid}} + {:noreply, %{state | tester_pid: tester_pid}} end + def handle_cast(:flow_del, state) do send_flow_mod_delete(state.datapath_id) {:noreply, state} end + def handle_cast({:flow_del, cookie}, state) do - send_flow_mod_delete(state.datapath_id, cookie: cookie, cookie_mask: 0xffffffffffffffff) + send_flow_mod_delete(state.datapath_id, cookie: cookie, cookie_mask: 0xFFFFFFFFFFFFFFFF) {:noreply, state} end @@ -59,31 +62,36 @@ defmodule Flay do send(state.tester_pid, error) {:noreply, state} end + def handle_info(%PacketIn{} = pktin, state) do send(state.tester_pid, pktin) {:noreply, state} end + def handle_info(%TableFeatures.Reply{} = table, state) do - {:noreply, %{state|default_profile: table}} + {:noreply, %{state | default_profile: table}} end + def handle_info(%PortDesc.Reply{} = desc, state) do GenServer.reply(state.reply_to, desc) {:noreply, state} end + def handle_info(%Desc.Reply{} = desc, state) do info( - "[#{__MODULE__}] Switch Desc: " - <> "mfr = #{desc.mfr_desc} " - <> "hw = #{desc.hw_desc} " - <> "sw = #{desc.sw_desc} " + "[#{__MODULE__}] Switch Desc: " <> + "mfr = #{desc.mfr_desc} " <> "hw = #{desc.hw_desc} " <> "sw = #{desc.sw_desc} " ) + init_bridge(state.datapath_id, desc) {:noreply, state} end + def handle_info(%Flow.Reply{} = desc, state) do GenServer.reply(state.reply_to, desc) - {:noreply, %{state|reply_to: nil}} + {:noreply, %{state | reply_to: nil}} end + # `Catch all` function is required. def handle_info(info, state) do :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}") @@ -94,11 +102,11 @@ defmodule Flay do defp flow_opts_to_ofp_print(flow_opts) do flow_opts - |> FlowMod.new - |> Openflow.to_binary + |> FlowMod.new() + |> Openflow.to_binary() |> binary_to_space_delimited_hex |> ofp_print_cmd - |> Logger.info + |> Logger.info() end defp ofp_print_cmd(print_args) do @@ -110,7 +118,7 @@ defmodule Flay do binary |> split_to_hex_string |> Enum.join(" ") - |> String.downcase + |> String.downcase() end defp split_to_hex_string(binary) do @@ -120,24 +128,26 @@ defmodule Flay do defp integer_to_hex(int) do case Integer.to_string(int, 16) do <> -> <<48, d>> - dd -> dd + dd -> dd end end defp init_controller([datapath_id, tester_pid]) do conn_ref = SwitchRegistry.monitor(datapath_id) + %State{ datapath_id: datapath_id, - tester_pid: tester_pid, - conn_ref: conn_ref + tester_pid: tester_pid, + conn_ref: conn_ref } end defp init_bridge(datapath_id, %Desc.Reply{mfr_desc: "Aruba"}) do :ok = info("Transform flow table pipeline") + tables = [ TableFeatures.Body.new( - table_id: 0, + table_id: 0, name: "classifier", max_entries: 50, config: [:table_miss_mask], @@ -161,7 +171,7 @@ defmodule Flay do :ip_proto, :ipv4_src, :udp_dst, - :tcp_dst, + :tcp_dst ], instructions: [ Openflow.Instruction.GotoTable, @@ -178,11 +188,11 @@ defmodule Flay do :vlan_vid ], next_tables: [ - 1, - ], + 1 + ] ), TableFeatures.Body.new( - table_id: 1, + table_id: 1, name: "admission_control", max_entries: 50, config: [:table_miss_mask], @@ -223,13 +233,16 @@ defmodule Flay do :vlan_vid, :ipv4_src, :ipv4_dst - ], + ] ) ] + TableFeatures.Request.new(tables) |> send_message(datapath_id) + send_flow_mod_delete(datapath_id, table_id: :all) end + defp init_bridge(_datapath_id, _mfr) do :ok = info("Flow pipeline profile is not defined") :ok diff --git a/test/flog_test.exs b/test/flog_test.exs index 3037cc7..dc31453 100644 --- a/test/flog_test.exs +++ b/test/flog_test.exs @@ -4,32 +4,38 @@ defmodule FlogTest do @listen_port 6653 - @vlan_trunk_port "veth0" # FIXME: - @access_port1 "veth3" # FIXME: - @access_port2 "veth4" # FIXME: + # FIXME: + @vlan_trunk_port "veth0" + # FIXME: + @access_port1 "veth3" + # FIXME: + @access_port2 "veth4" - @vlan_trunk_port_sniffer "veth1" # FIXME: - @access_port1_sniffer "veth2" # FIXME: - @access_port2_sniffer "veth5" # FIXME: + # FIXME: + @vlan_trunk_port_sniffer "veth1" + # FIXME: + @access_port1_sniffer "veth2" + # FIXME: + @access_port2_sniffer "veth5" - @bootnet_vid 0x1000 ||| 5 - @user_vid 0x1000 ||| 123 - @vlan_present {0x1000, 0x1000} - @mcast {"010000000000", "010000000000"} - @sdl_vmac "000000000001" - @bcast "ffffffffffff" - @mac "000102030405" - @auth_ipv4_address {192,168,5,4} - @captive_ipv4_address {192,168,5,5} - @quad_0_ip {0,0,0,0} - @bcast_ip {255,255,255,255} - @client_ip {192,168,5,10} - @client_farm_ip {192,168,255,1} - @gateway_ip {192,168,5,4} - @farm_gw_ip {192,168,255,254} + @bootnet_vid 0x1000 ||| 5 + @user_vid 0x1000 ||| 123 + @vlan_present {0x1000, 0x1000} + @mcast {"010000000000", "010000000000"} + @sdl_vmac "000000000001" + @bcast "ffffffffffff" + @mac "000102030405" + @auth_ipv4_address {192, 168, 5, 4} + @captive_ipv4_address {192, 168, 5, 5} + @quad_0_ip {0, 0, 0, 0} + @bcast_ip {255, 255, 255, 255} + @client_ip {192, 168, 5, 10} + @client_farm_ip {192, 168, 255, 1} + @gateway_ip {192, 168, 5, 4} + @farm_gw_ip {192, 168, 255, 254} @farm_gw_mac "00000000000f" - @internet {8,8,8,8} - @trusted_macs [ + @internet {8, 8, 8, 8} + @trusted_macs [ "0800274d3297", "0800274d3298", "0800274d3299" @@ -49,12 +55,13 @@ defmodule FlogTest do setup_applications() wait_for_connected() ports = get_ports_desc() - vlan_trunk = Enum.find(ports, fn(port) -> port.name == @vlan_trunk_port end) - port = Enum.find(ports, fn(port) -> port.name == @access_port1 end) - port2 = Enum.find(ports, fn(port) -> port.name == @access_port2 end) - cookie = 0x1000000000000001 + vlan_trunk = Enum.find(ports, fn port -> port.name == @vlan_trunk_port end) + port = Enum.find(ports, fn port -> port.name == @access_port1 end) + port2 = Enum.find(ports, fn port -> port.name == @access_port2 end) + cookie = 0x1000000000000001 cookie2 = 0x2000000000000001 timeout = 32_678 + options = [ vlan_trunk: vlan_trunk, port: port, @@ -63,18 +70,20 @@ defmodule FlogTest do cookie2: cookie2, timeout: timeout ] + {:ok, options} end - describe("switch:merged_handler:table=0,priority=0,cookie=0x8000000000000000,actions=drop") do + describe "switch:merged_handler:table=0,priority=0,cookie=0x8000000000000000,actions=drop" do test "Install Flow" do options = [ cookie: 0x8000000000000000, table_id: 0, priority: 0 ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -85,10 +94,12 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) payload = <<0::size(16)-unit(8)>> + packet = [ ether(dhost: dhost, shost: shost, type: 0x0806), arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] + Pf.inject!(pid1, packet, payload) refute_receive {@vlan_trunk_port_sniffer, ^packet}, 1000 Pf.stop(pid1) @@ -96,26 +107,31 @@ defmodule FlogTest do end end - describe("switch:merged_handler:" <> - "table=0,priority=200,cookie=0x4000000000000000,in_port=%d,dl_dst=%s,dl_vlan=0x0000/0x1fff,dl_type=%s," <> - "actions=controller") do + describe "switch:merged_handler:" <> + "table=0,priority=200,cookie=0x4000000000000000,in_port=%d,dl_dst=%s,dl_vlan=0x0000/0x1fff,dl_type=%s," <> + "actions=controller" do test "Install Flow", state do - match = Openflow.Match.new( - in_port: state.vlan_trunk.number, - eth_dst: @bcast, - vlan_vid: 0x0000, - eth_type: 0x88cc - ) + match = + Openflow.Match.new( + in_port: state.vlan_trunk.number, + eth_dst: @bcast, + vlan_vid: 0x0000, + eth_type: 0x88CC + ) + action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) - options = - [cookie: 0x4000000000000000, - table_id: 0, - priority: 200, - match: match, - instructions: [ins]] + + options = [ + cookie: 0x4000000000000000, + table_id: 0, + priority: 200, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 {:ok, pid1} = Pf.start_link(@access_port1_sniffer) @@ -123,12 +139,18 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(state.vlan_trunk.hw_addr, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + packet = [ - ether(dhost: dhost, shost: shost, type: 0x88cc), - lldp(pdus: [chassis_id(value: "hogehoge"), - port_id(subtype: :mac_address, value: shost), - ttl(value: 128)]) + ether(dhost: dhost, shost: shost, type: 0x88CC), + lldp( + pdus: [ + chassis_id(value: "hogehoge"), + port_id(subtype: :mac_address, value: shost), + ttl(value: 128) + ] + ) ] + Pf.inject!(pid2, packet) in_port = state.vlan_trunk.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 @@ -139,37 +161,36 @@ defmodule FlogTest do end end - describe("switch:merged_handler:" <> - "table=0,priority=201,cookie=0x4000000000000000,in_port=%d,dl_src=%s,actions=drop") do + describe "switch:merged_handler:" <> + "table=0,priority=201,cookie=0x4000000000000000,in_port=%d,dl_src=%s,actions=drop" do test "Install Flow", state do for port <- [state.vlan_trunk, state.port] do match = Openflow.Match.new(in_port: port.number, eth_src: port.hw_addr) - options = - [cookie: 0x4000000000000000, - table_id: 0, - priority: 201, - match: match] + options = [cookie: 0x4000000000000000, table_id: 0, priority: 201, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end end - describe("switch:uplink_escalation_flow:" <> - "table=0,priority=10,cookie=0x1000000000000000,arp,actions=controller") do + describe "switch:uplink_escalation_flow:" <> + "table=0,priority=10,cookie=0x1000000000000000,arp,actions=controller" do test "Install Flow", state do match = Openflow.Match.new(eth_type: 0x0806) action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) - options = - [cookie: 0x1000000000000000, - table_id: 0, - priority: 10, - match: match, - instructions: [ins]] + + options = [ + cookie: 0x1000000000000000, + table_id: 0, + priority: 10, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 {:ok, pid1} = Pf.start_link(@access_port1_sniffer) @@ -178,10 +199,12 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) payload = <<0::size(16)-unit(8)>> + packet = [ ether(dhost: dhost, shost: shost, type: 0x0806), arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] + Pf.inject!(pid1, packet, payload) in_port = state.port.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 @@ -191,20 +214,23 @@ defmodule FlogTest do end end - describe("switch:uplink_escalation_flow:" <> - "table=0,priority=10,cookie=0x1000000000000000,udp,udp_dst=67,actions=controller") do + describe "switch:uplink_escalation_flow:" <> + "table=0,priority=10,cookie=0x1000000000000000,udp,udp_dst=67,actions=controller" do test "Install Flow", state do match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) - action = Openflow.Action.Output.new(:controller) + action = Openflow.Action.Output.new(:controller) ins = Openflow.Instruction.ApplyActions.new(action) - options = - [cookie: 0x1000000000000000, - table_id: 0, - priority: 10, - match: match, - instructions: [ins]] + + options = [ + cookie: 0x1000000000000000, + table_id: 0, + priority: 10, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 {:ok, pid1} = Pf.start_link(@access_port1_sniffer) @@ -218,11 +244,13 @@ defmodule FlogTest do udp_header = udp(sport: 68, dport: 67, ulen: length) ipv4_header = ipv4(saddr: @quad_0_ip, daddr: @bcast_ip, p: 17, len: 20 + length) udp_sum = :pkt.makesum([ipv4_header, udp_header, payload]) + packet = [ ether(dhost: dhost, shost: shost, type: 0x0800), ipv4_header, udp(udp_header, sum: udp_sum) ] + Pf.inject!(pid1, packet, <>) in_port = state.port.number assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 @@ -232,17 +260,13 @@ defmodule FlogTest do end end - describe("switch:uplink_escalation_flow:" <> - "table=0,priority=11,cookie=0x1000000000000000,in_port={trunk_port},actions=drop") do + describe "switch:uplink_escalation_flow:" <> + "table=0,priority=11,cookie=0x1000000000000000,in_port={trunk_port},actions=drop" do test "Install Flow", state do match = Openflow.Match.new(in_port: state.vlan_trunk.number) - options = - [cookie: 0x1000000000000000, - table_id: 0, - priority: 11, - match: match] + options = [cookie: 0x1000000000000000, table_id: 0, priority: 11, match: match] :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -253,10 +277,12 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) payload = <<0::size(16)-unit(8)>> + packet = [ ether(dhost: dhost, shost: shost, type: 0x0806), arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] + Pf.inject!(pid2, packet, payload) refute_receive {@access_port1_sniffer, ^packet}, 1000 Pf.stop(pid1) @@ -264,32 +290,38 @@ defmodule FlogTest do end end - describe("associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "dl_src={mac},in_port={port_no}," <> - "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,output:{vlan_trunk_port}") do + describe "associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "dl_src={mac},in_port={port_no}," <> + "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,output:{vlan_trunk_port}" do test "Install Flow", state do - match = Openflow.Match.new( - in_port: state.port.number, - eth_src: @mac - ) + match = + Openflow.Match.new( + in_port: state.port.number, + eth_src: @mac + ) + actions = [ - Openflow.Action.PushVlan.new, + Openflow.Action.PushVlan.new(), Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}), - Openflow.Action.Output.new(state.vlan_trunk.number), + Openflow.Action.Output.new(state.vlan_trunk.number) ] + ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [cookie: state.cookie, - table_id: 0, - priority: 20, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: [ins]] + + options = [ + cookie: state.cookie, + table_id: 0, + priority: 20, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -299,13 +331,18 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - packet = [ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip)] + + packet = [ + ether(dhost: dhost, shost: shost, type: 0x0806), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) + ] + expect = [ ether(dhost: dhost, shost: shost, type: 0x8100), {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] + Pf.inject!(pid1, packet) assert_receive {@vlan_trunk_port_sniffer, ^expect}, 3000 Pf.stop(pid1) @@ -313,33 +350,39 @@ defmodule FlogTest do end end - describe("associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "in_port={vlan_trunk_port},dl_vlan={vlan},dl_dst={mcast}," <> - "actions=strip_vlan,{outputs}") do + describe "associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "in_port={vlan_trunk_port},dl_vlan={vlan},dl_dst={mcast}," <> + "actions=strip_vlan,{outputs}" do test "Install Flow", state do - match = Openflow.Match.new( - in_port: state.vlan_trunk.number, - vlan_vid: @bootnet_vid, - eth_dst: @mcast - ) + match = + Openflow.Match.new( + in_port: state.vlan_trunk.number, + vlan_vid: @bootnet_vid, + eth_dst: @mcast + ) + actions = [ - Openflow.Action.PopVlan.new, + Openflow.Action.PopVlan.new(), Openflow.Action.Output.new(state.port.number), - Openflow.Action.Output.new(state.port2.number), + Openflow.Action.Output.new(state.port2.number) ] + ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [cookie: state.cookie, - table_id: 0, - priority: 50, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: [ins]] + + options = [ + cookie: state.cookie, + table_id: 0, + priority: 50, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -350,15 +393,18 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) + packet = [ ether(dhost: dhost, shost: shost, type: 0x8100), {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] + expect = [ ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip), + arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) ] + Pf.inject!(pid2, packet) assert_receive {@access_port1_sniffer, ^expect}, 3000 assert_receive {@access_port2_sniffer, ^expect}, 3000 @@ -368,32 +414,38 @@ defmodule FlogTest do end end - describe("associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "in_port={vlan_trunk_port},dl_vlan={vlan},dl_dst={mac}," <> - "actions=strip_vlan,output:{port_no}") do + describe "associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "in_port={vlan_trunk_port},dl_vlan={vlan},dl_dst={mac}," <> + "actions=strip_vlan,output:{port_no}" do test "Install Flow", state do - match = Openflow.Match.new( - in_port: state.vlan_trunk.number, - vlan_vid: @bootnet_vid, - eth_dst: @mac - ) + match = + Openflow.Match.new( + in_port: state.vlan_trunk.number, + vlan_vid: @bootnet_vid, + eth_dst: @mac + ) + actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(state.port.number), + Openflow.Action.PopVlan.new(), + Openflow.Action.Output.new(state.port.number) ] + ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [cookie: state.cookie, - table_id: 0, - priority: 50, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: [ins]] + + options = [ + cookie: state.cookie, + table_id: 0, + priority: 50, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -403,15 +455,18 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) dhost = Openflow.Match.Field.codec(@mac, :eth_dst) + packet = [ ether(dhost: dhost, shost: shost, type: 0x8100), {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 2, sha: shost, tip: @client_ip, sip: @gateway_ip), + arp(op: 2, sha: shost, tip: @client_ip, sip: @gateway_ip) ] + expect = [ ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 2, sha: shost, tip: @client_ip, sip: @gateway_ip), + arp(op: 2, sha: shost, tip: @client_ip, sip: @gateway_ip) ] + Pf.inject!(pid2, packet) assert_receive {@access_port1_sniffer, ^expect}, 3000 Pf.stop(pid1) @@ -419,25 +474,27 @@ defmodule FlogTest do end end - describe("associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "dl_src={mac},in_port={vlan_trunk_port}," <> - "actions=drop") do + describe "associate:register_bootstrap_rule:" <> + "send_flow_rem," <> "dl_src={mac},in_port={vlan_trunk_port}," <> "actions=drop" do test "Install Flow", state do - match = Openflow.Match.new( - in_port: state.vlan_trunk.number, - eth_src: @mac - ) - options = - [cookie: state.cookie, - table_id: 0, - priority: 19, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match] + match = + Openflow.Match.new( + in_port: state.vlan_trunk.number, + eth_src: @mac + ) + + options = [ + cookie: state.cookie, + table_id: 0, + priority: 19, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -448,10 +505,12 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(@mac, :eth_src) dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) payload = <<0::size(16)-unit(8)>> + packet = [ ether(dhost: dhost, shost: shost, type: 0x0806), arp(op: 2, sha: shost, sip: @client_ip, tip: @gateway_ip) ] + Pf.inject!(pid2, packet, payload) refute_receive {@access_port1_sniffer, ^packet}, 3000 Pf.stop(pid1) @@ -459,34 +518,40 @@ defmodule FlogTest do end end - describe("associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "in_port={vlan_trunk_port},dl_vlan={vlan},dl_src={trusted},dl_dst={mac}," <> - "actions=strip_vlan,output:{port_no}") do + describe "associate:register_bootstrap_rule:" <> + "send_flow_rem," <> + "in_port={vlan_trunk_port},dl_vlan={vlan},dl_src={trusted},dl_dst={mac}," <> + "actions=strip_vlan,output:{port_no}" do test "Install Flow", state do for trusted <- @trusted_macs do - match = Openflow.Match.new( - in_port: state.vlan_trunk.number, - vlan_vid: @bootnet_vid, - eth_src: trusted, - eth_dst: @mac - ) + match = + Openflow.Match.new( + in_port: state.vlan_trunk.number, + vlan_vid: @bootnet_vid, + eth_src: trusted, + eth_dst: @mac + ) + actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(state.port.number), + Openflow.Action.PopVlan.new(), + Openflow.Action.Output.new(state.port.number) ] + ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [cookie: state.cookie, - table_id: 0, - priority: 50, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: [ins]] + + options = [ + cookie: state.cookie, + table_id: 0, + priority: 50, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end @@ -499,15 +564,18 @@ defmodule FlogTest do shost = Openflow.Match.Field.codec(trusted, :eth_src) dhost = Openflow.Match.Field.codec(@mac, :eth_dst) payload = <<0::size(16)-unit(8)>> + packet = [ ether(dhost: dhost, shost: shost, type: 0x8100), {:"802.1q", 0, 0, 5, 0x0806}, arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) ] + expect = [ ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip), + arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) ] + Pf.inject!(pid2, packet, payload) assert_receive {@access_port1_sniffer, ^expect}, 3000 Pf.stop(pid1) @@ -516,101 +584,115 @@ defmodule FlogTest do end end - describe("associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "dl_src={trusted}," <> - "actions=drop") do + describe "associate:register_bootstrap_rule:" <> + "send_flow_rem," <> "dl_src={trusted}," <> "actions=drop" do test "Install Flow", state do for trusted <- @trusted_macs do match = Openflow.Match.new(eth_src: trusted) - options = - [cookie: state.cookie, - table_id: 0, - priority: 29, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match] + + options = [ + cookie: state.cookie, + table_id: 0, + priority: 29, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end end - describe("associate:register_usernet_rule:" <> - "in_port={vlan_trunk_port},dl_vlan={vlan}," <> - "actions=goto_table:1") do + describe "associate:register_usernet_rule:" <> + "in_port={vlan_trunk_port},dl_vlan={vlan}," <> "actions=goto_table:1" do setup [:flow_del_by_cookie] test "Install Flow", state do match = Openflow.Match.new(in_port: state.vlan_trunk.number, vlan_vid: @user_vid) ins = [Openflow.Instruction.GotoTable.new(1)] - options = - [cookie: state.cookie2, - table_id: 0, - priority: 50, - match: match, - instructions: ins] + + options = [ + cookie: state.cookie2, + table_id: 0, + priority: 50, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end - describe("associate:register_usernet_rule:" <> - "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem, dl_src={mac},in_port={port_no}," <> - "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,goto_table:1") do + describe "associate:register_usernet_rule:" <> + "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem, dl_src={mac},in_port={port_no}," <> + "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,goto_table:1" do test "Install Flow", state do match = Openflow.Match.new(in_port: state.port.number, eth_src: @mac) + actions = [ - Openflow.Action.PushVlan.new, + Openflow.Action.PushVlan.new(), Openflow.Action.SetField.new({:vlan_vid, @user_vid}) ] + ins = [ Openflow.Instruction.ApplyActions.new(actions), Openflow.Instruction.GotoTable.new(1) ] - options = - [cookie: state.cookie2, - table_id: 0, - priority: 20, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: ins] + + options = [ + cookie: state.cookie2, + table_id: 0, + priority: 20, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end - describe("associate:register_usernet_rule:" <> - "vlan_vid=0x1000/0x1000,tcp,tcp_dst=443,nw_dst={auth_ipv4_address}," <> - "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vlan_trunk_port}") do + describe "associate:register_usernet_rule:" <> + "vlan_vid=0x1000/0x1000,tcp,tcp_dst=443,nw_dst={auth_ipv4_address}," <> + "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vlan_trunk_port}" do test "Install Flow", state do - match = Openflow.Match.new( - vlan_vid: @vlan_present, - eth_type: 0x0800, - ip_proto: 6, - ipv4_dst: @auth_ipv4_address, - tcp_dst: 443 - ) + match = + Openflow.Match.new( + vlan_vid: @vlan_present, + eth_type: 0x0800, + ip_proto: 6, + ipv4_dst: @auth_ipv4_address, + tcp_dst: 443 + ) + actions = [ - Openflow.Action.PopVlan.new, + Openflow.Action.PopVlan.new(), Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(state.vlan_trunk.number), + Openflow.Action.Output.new(state.vlan_trunk.number) ] + ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [cookie: state.cookie2, - table_id: 1, - priority: 30, - match: match, - instructions: [ins]] + + options = [ + cookie: state.cookie2, + table_id: 1, + priority: 30, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -622,15 +704,18 @@ defmodule FlogTest do dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) payload = "" - tcp_header = tcp( - sport: 53_688, - dport: 443, - seqno: 1_488_352_223, - off: 10, - syn: 1, - win: 29_200, - opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> - ) + + tcp_header = + tcp( + sport: 53_688, + dport: 443, + seqno: 1_488_352_223, + off: 10, + syn: 1, + win: 29_200, + opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> + ) + ipv4_header = ipv4(saddr: @client_ip, daddr: @auth_ipv4_address, p: 6, len: 60) tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) @@ -643,31 +728,37 @@ defmodule FlogTest do end end - describe("associate:register_usernet_rule:" <> - "vlan_vid=0x1000/0x1000,tcp,tcp_dst=80,nw_dst={captive_ipv4_address}," <> - "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vlan_trunk_port}") do + describe "associate:register_usernet_rule:" <> + "vlan_vid=0x1000/0x1000,tcp,tcp_dst=80,nw_dst={captive_ipv4_address}," <> + "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vlan_trunk_port}" do test "Install Flow", state do - match = Openflow.Match.new( - vlan_vid: @vlan_present, - eth_type: 0x0800, - ip_proto: 6, - ipv4_dst: @captive_ipv4_address, - tcp_dst: 80 - ) + match = + Openflow.Match.new( + vlan_vid: @vlan_present, + eth_type: 0x0800, + ip_proto: 6, + ipv4_dst: @captive_ipv4_address, + tcp_dst: 80 + ) + actions = [ - Openflow.Action.PopVlan.new, + Openflow.Action.PopVlan.new(), Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(state.vlan_trunk.number), + Openflow.Action.Output.new(state.vlan_trunk.number) ] + ins = Openflow.Instruction.ApplyActions.new(actions) - options = - [cookie: state.cookie2, - table_id: 1, - priority: 30, - match: match, - instructions: [ins]] + + options = [ + cookie: state.cookie2, + table_id: 1, + priority: 30, + match: match, + instructions: [ins] + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -679,15 +770,18 @@ defmodule FlogTest do dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) payload = "" - tcp_header = tcp( - sport: 53_688, - dport: 80, - seqno: 1_488_352_223, - off: 10, - syn: 1, - win: 29_200, - opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> - ) + + tcp_header = + tcp( + sport: 53_688, + dport: 80, + seqno: 1_488_352_223, + off: 10, + syn: 1, + win: 29_200, + opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> + ) + ipv4_header = ipv4(saddr: @client_ip, daddr: @captive_ipv4_address, p: 6, len: 60) tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) @@ -700,67 +794,74 @@ defmodule FlogTest do end end - describe("associate:register_usernet_rule:" <> - "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,dl_src={mac},in_port={vlan_trunk_port}," <> - "actions=drop") do + describe "associate:register_usernet_rule:" <> + "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,dl_src={mac},in_port={vlan_trunk_port}," <> + "actions=drop" do test "Install Flow", state do match = Openflow.Match.new(in_port: state.vlan_trunk.number, eth_src: @mac) - options = - [cookie: state.cookie2, - table_id: 0, - priority: 19, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match] + + options = [ + cookie: state.cookie2, + table_id: 0, + priority: 19, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end - describe("associate:register_usernet_rule:" <> - "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,dl_src={mac}," <> - "actions=controller") do + describe "associate:register_usernet_rule:" <> + "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,dl_src={mac}," <> + "actions=controller" do test "Install Flow", state do match = Openflow.Match.new(eth_src: @mac) actions = [Openflow.Action.Output.new(:controller)] ins = [Openflow.Instruction.ApplyActions.new(actions)] - options = - [cookie: state.cookie2, - table_id: 0, - priority: 18, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: ins] + + options = [ + cookie: state.cookie2, + table_id: 0, + priority: 18, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end - describe("associate:register_usernet_rule:" <> - "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,vlan_vid=0x1000/0x1000,dl_dst={mac}," <> - "actions=strip_vlan,output:{port_no}") do + describe "associate:register_usernet_rule:" <> + "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,vlan_vid=0x1000/0x1000,dl_dst={mac}," <> + "actions=strip_vlan,output:{port_no}" do test "Install Flow", state do match = Openflow.Match.new(vlan_vid: @vlan_present, eth_dst: @mac) - actions = [ - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(state.port.number)] + actions = [Openflow.Action.PopVlan.new(), Openflow.Action.Output.new(state.port.number)] ins = [Openflow.Instruction.ApplyActions.new(actions)] - options = - [cookie: state.cookie2, - table_id: 1, - priority: 50, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: ins] + + options = [ + cookie: state.cookie2, + table_id: 1, + priority: 50, + flags: [:send_flow_rem], + idle_timeout: state.timeout, + hard_timeout: state.timeout, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -783,26 +884,31 @@ defmodule FlogTest do end end - describe("associate:register_usernet_rule:" <> - "dl_vlan={vlan},dl_dst={mcast}," <> - "actions=output:{vlan_trunk_port},strip_vlan,{outputs}") do + describe "associate:register_usernet_rule:" <> + "dl_vlan={vlan},dl_dst={mcast}," <> + "actions=output:{vlan_trunk_port},strip_vlan,{outputs}" do test "Install Flow", state do match = Openflow.Match.new(vlan_vid: @user_vid, eth_dst: @mcast) + actions = [ Openflow.Action.Output.new(state.vlan_trunk.number), - Openflow.Action.PopVlan.new, + Openflow.Action.PopVlan.new(), Openflow.Action.Output.new(state.port.number), Openflow.Action.Output.new(state.port2.number) ] + ins = [Openflow.Instruction.ApplyActions.new(actions)] - options = - [cookie: state.cookie2, - table_id: 1, - priority: 60, - match: match, - instructions: ins] + + options = [ + cookie: state.cookie2, + table_id: 1, + priority: 60, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -840,10 +946,12 @@ defmodule FlogTest do arp_header = arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip) packet = [ether_header, arp_header] Pf.inject!(pid1, packet) + expects = [ {@vlan_trunk_port_sniffer, [vlan_ether_header, vlan_header, arp_header]}, {@access_port2_sniffer, [ether_header, arp_header]} ] + :ok = assert_receives(expects) Pf.stop(pid1) Pf.stop(pid2) @@ -851,39 +959,46 @@ defmodule FlogTest do end end - describe("associate:register_usernet_rule:" <> - "dl_vlan={vlan}," <> - "actions=output:{vlan_trunk_port},strip_vlan,{outputs}") do + describe "associate:register_usernet_rule:" <> + "dl_vlan={vlan}," <> "actions=output:{vlan_trunk_port},strip_vlan,{outputs}" do test "Install Flow", state do match = Openflow.Match.new(vlan_vid: @user_vid) + actions = [ Openflow.Action.Output.new(state.vlan_trunk.number), - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(state.port.number)] + Openflow.Action.PopVlan.new(), + Openflow.Action.Output.new(state.port.number) + ] + ins = [Openflow.Instruction.ApplyActions.new(actions)] - options = - [cookie: state.cookie2, - table_id: 1, - priority: 20, - match: match, - instructions: ins] + + options = [ + cookie: state.cookie2, + table_id: 1, + priority: 20, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end - describe("switch:register_farm_nat_rule:" <> - "dl_src={src_mac},arp,arp_spa={src_ip},arp_tpa={dst_ip}," <> - "actions=set_field:{farm_vmac}->eth_dst,set_field:{src_fip}->arp_spa," <> - "set_field:{dst_fip}->arp_tpa,output:{vlan_trunk_port}") do + describe "switch:register_farm_nat_rule:" <> + "dl_src={src_mac},arp,arp_spa={src_ip},arp_tpa={dst_ip}," <> + "actions=set_field:{farm_vmac}->eth_dst,set_field:{src_fip}->arp_spa," <> + "set_field:{dst_fip}->arp_tpa,output:{vlan_trunk_port}" do test "Install Flow", state do - match = Openflow.Match.new( - eth_src: @mac, - eth_type: 0x0806, - arp_spa: @client_ip, - arp_tpa: @gateway_ip - ) + match = + Openflow.Match.new( + eth_src: @mac, + eth_type: 0x0806, + arp_spa: @client_ip, + arp_tpa: @gateway_ip + ) + actions = [ Openflow.Action.SetField.new({:eth_dst, @farm_gw_mac}), Openflow.Action.SetField.new({:arp_spa, @client_farm_ip}), @@ -891,51 +1006,61 @@ defmodule FlogTest do # Following is not work properly in "SwitchNode" configuration. # Openflow.Action.PushVlan.new, # Openflow.Action.SetField.new({:vlan_vid, @user_vid}), - Openflow.Action.Output.new(state.vlan_trunk.number), + Openflow.Action.Output.new(state.vlan_trunk.number) ] + ins = [Openflow.Instruction.ApplyActions.new(actions)] - options = - [cookie: 0x3000000000000001, - table_id: 1, - priority: 30, - idle_timeout: 60, - match: match, - instructions: ins] + + options = [ + cookie: 0x3000000000000001, + table_id: 1, + priority: 30, + idle_timeout: 60, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end end - describe("switch:register_farm_nat_rule:" <> - "dl_src={src_mac},ip,nw_src={src_ip},nw_dst={dst_ip}," <> - "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,set_field:{farm_vmac}->eth_dst," <> - "set_field:{src_fip}->nw_src,output:{vlan_trunk_port}") do + describe "switch:register_farm_nat_rule:" <> + "dl_src={src_mac},ip,nw_src={src_ip},nw_dst={dst_ip}," <> + "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,set_field:{farm_vmac}->eth_dst," <> + "set_field:{src_fip}->nw_src,output:{vlan_trunk_port}" do test "Install Flow", state do - match = Openflow.Match.new( - eth_src: @mac, - eth_type: 0x0800, - ipv4_src: @client_ip, - ipv4_dst: @internet - ) + match = + Openflow.Match.new( + eth_src: @mac, + eth_type: 0x0800, + ipv4_src: @client_ip, + ipv4_dst: @internet + ) + actions = [ Openflow.Action.SetField.new({:eth_dst, @farm_gw_mac}), # Following is not work properly in "SwitchNode" configuration. # Openflow.Action.PushVlan.new, # Openflow.Action.SetField.new({:vlan_vid, @user_vid}), Openflow.Action.SetField.new({:ipv4_src, @client_farm_ip}), - Openflow.Action.Output.new(state.vlan_trunk.number), + Openflow.Action.Output.new(state.vlan_trunk.number) ] + ins = [Openflow.Instruction.ApplyActions.new(actions)] - options = - [cookie: 0x3000000000000001, - table_id: 1, - priority: 30, - idle_timeout: 60, - match: match, - instructions: ins] + + options = [ + cookie: 0x3000000000000001, + table_id: 1, + priority: 30, + idle_timeout: 60, + match: match, + instructions: ins + ] + :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep 1000 + :timer.sleep(1000) refute_receive %Openflow.ErrorMsg{}, 1000 end @@ -947,15 +1072,18 @@ defmodule FlogTest do dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) payload = "" - tcp_header = tcp( - sport: 53_688, - dport: 80, - seqno: 1_488_352_223, - off: 10, - syn: 1, - win: 29_200, - opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> - ) + + tcp_header = + tcp( + sport: 53_688, + dport: 80, + seqno: 1_488_352_223, + off: 10, + syn: 1, + win: 29_200, + opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> + ) + ipv4_header = ipv4(saddr: @client_ip, daddr: @internet, p: 6, len: 60) tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) @@ -968,7 +1096,14 @@ defmodule FlogTest do vlan_header = {:"802.1q", 0, 0, 123, 0x0800} ipv4_header = ipv4(saddr: @client_farm_ip, daddr: @internet, p: 6, len: 60) tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) - expect = [ether_header, vlan_header, ipv4(ipv4_header, sum: 1544), tcp(tcp_header, sum: tcp_sum)] + + expect = [ + ether_header, + vlan_header, + ipv4(ipv4_header, sum: 1544), + tcp(tcp_header, sum: tcp_sum) + ] + assert_receives([{@vlan_trunk_port_sniffer, expect}], 5000) Pf.stop(pid1) Pf.stop(pid2) @@ -982,12 +1117,14 @@ defmodule FlogTest do end defp assert_receives(_expects, 0, _timeout), do: :ok + defp assert_receives(expects, count, timeout) do receive do message -> assert(message in expects) assert_receives(expects, count - 1, timeout) - after timeout -> + after + timeout -> flunk("Timeout") end end @@ -1011,6 +1148,7 @@ defmodule FlogTest do case Process.whereis(Flay) do nil -> wait_for_connected() + pid when is_pid(pid) -> :ok end @@ -1018,7 +1156,7 @@ defmodule FlogTest do defp flow_del_by_cookie(context) do :ok = GenServer.cast(Flay, {:flow_del, context.cookie}) - :timer.sleep 3000 + :timer.sleep(3000) end defp get_ports_desc do diff --git a/test/ofp_action_test.exs b/test/ofp_action_test.exs index 061d036..146b27d 100644 --- a/test/ofp_action_test.exs +++ b/test/ofp_action_test.exs @@ -6,11 +6,13 @@ defmodule OfpActionTest 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] + slaves: [4, 8] ) + actions_bin = Openflow.Action.to_binary(bundle) assert actions_bin == packet assert actions == [bundle] @@ -20,12 +22,14 @@ defmodule OfpActionTest 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] @@ -35,11 +39,14 @@ defmodule OfpActionTest 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 - ) + + 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] @@ -49,12 +56,15 @@ defmodule OfpActionTest 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 - ) + + controller2 = + Openflow.Action.NxController2.new( + max_len: 1234, + reason: :invalid_ttl, + userdata: <<1, 2, 3, 4, 5>>, + pause: true + ) + assert actions == [controller2] end @@ -102,13 +112,19 @@ defmodule OfpActionTest do 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)] - ) + + 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] @@ -118,10 +134,13 @@ defmodule OfpActionTest 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)] - ) + + 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] @@ -131,10 +150,13 @@ defmodule OfpActionTest 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)] - ) + + 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] @@ -144,10 +166,13 @@ defmodule OfpActionTest 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])] - ) + + 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] @@ -159,16 +184,19 @@ defmodule OfpActionTest do 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 - ) - ] - ) + + 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] @@ -178,10 +206,13 @@ defmodule OfpActionTest 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])] - ) + + 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] @@ -192,38 +223,46 @@ defmodule OfpActionTest do 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 - ) - ] - ) + + 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" + 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 - ) - ] - ) + + 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] @@ -234,61 +273,74 @@ defmodule OfpActionTest do 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 - ) - ] - ) + + 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" + 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 - ) - ] - ) + + 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" + 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 - ) - ] - ) + + 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] @@ -299,10 +351,13 @@ defmodule OfpActionTest do 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)] - ) + + 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] @@ -312,7 +367,7 @@ defmodule OfpActionTest 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]) + ct = Openflow.Action.NxConntrack.new(exec: [Openflow.Action.NxNat.new()]) actions_bin = Openflow.Action.to_binary(ct) assert actions_bin == packet assert actions == [ct] @@ -352,7 +407,7 @@ defmodule OfpActionTest do test_file = "test/packet_data/nx_ct.raw" packet = File.read!(test_file) actions = Openflow.Action.read(packet) - ct = Openflow.Action.NxConntrack.new + ct = Openflow.Action.NxConntrack.new() actions_bin = Openflow.Action.to_binary(ct) assert actions_bin == packet assert actions == [ct] @@ -362,7 +417,7 @@ defmodule OfpActionTest 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 + ct = Openflow.Action.NxCtClear.new() actions_bin = Openflow.Action.to_binary(ct) assert actions_bin == packet assert actions == [ct] @@ -372,7 +427,7 @@ defmodule OfpActionTest 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 + dec_ttl = Openflow.Action.NxDecTtl.new() actions_bin = Openflow.Action.to_binary(dec_ttl) assert actions_bin == packet assert actions == [dec_ttl] @@ -392,7 +447,7 @@ defmodule OfpActionTest do test_file = "test/packet_data/nx_exit.raw" packet = File.read!(test_file) actions = Openflow.Action.read(packet) - exit = Openflow.Action.NxExit.new + exit = Openflow.Action.NxExit.new() actions_bin = Openflow.Action.to_binary(exit) assert actions_bin == packet assert actions == [exit] @@ -412,21 +467,24 @@ defmodule OfpActionTest 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) - ] - ) + + 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] @@ -436,24 +494,27 @@ defmodule OfpActionTest 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) - ] - ) + + 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] @@ -463,11 +524,14 @@ defmodule OfpActionTest 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 - ) + + 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] @@ -477,7 +541,7 @@ defmodule OfpActionTest 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>>) + 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] @@ -507,7 +571,7 @@ defmodule OfpActionTest 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 + pop_queue = Openflow.Action.NxPopQueue.new() actions_bin = Openflow.Action.to_binary(pop_queue) assert actions_bin == packet assert actions == [pop_queue] @@ -517,7 +581,7 @@ defmodule OfpActionTest 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) + 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] diff --git a/test/ofp_echo_test.exs b/test/ofp_echo_test.exs index 13aa181..5e21fed 100644 --- a/test/ofp_echo_test.exs +++ b/test/ofp_echo_test.exs @@ -6,8 +6,9 @@ defmodule OfpEchoTest do test "with OFP_ECHO_REQUEST packet" do {:ok, %Openflow.Echo.Request{} = echo, ""} = "test/packet_data/ofp_echo_request.raw" - |> File.read! - |> Openflow.read + |> File.read!() + |> Openflow.read() + assert echo.version == 4 assert echo.xid == 0 assert echo.data == "" @@ -16,8 +17,9 @@ defmodule OfpEchoTest do test "with OFP_ECHO_REPLY packet" do {:ok, %Openflow.Echo.Reply{} = echo, ""} = "test/packet_data/ofp_echo_reply.raw" - |> File.read! - |> Openflow.read + |> File.read!() + |> Openflow.read() + assert echo.version == 4 assert echo.xid == 0 assert echo.data == "" @@ -31,9 +33,11 @@ defmodule OfpEchoTest do xid: 0, data: "" } + expect = "test/packet_data/ofp_echo_request.raw" - |> File.read! + |> File.read!() + assert Openflow.to_binary(echo) == expect end @@ -43,9 +47,11 @@ defmodule OfpEchoTest do xid: 0, data: "" } + expect = "test/packet_data/ofp_echo_reply.raw" - |> File.read! + |> File.read!() + assert Openflow.to_binary(echo) == expect end end diff --git a/test/ofp_error_test.exs b/test/ofp_error_test.exs index a7c9de1..b49f6bf 100644 --- a/test/ofp_error_test.exs +++ b/test/ofp_error_test.exs @@ -6,8 +6,9 @@ defmodule OfpErrorTest do test "with OFP_ERROR packet" do {:ok, error, ""} = "test/packet_data/ofp_error.raw" - |> File.read! - |> Openflow.read + |> File.read!() + |> Openflow.read() + assert error.version == 4 assert error.xid == 0 assert error.type == :bad_action @@ -19,35 +20,55 @@ defmodule OfpErrorTest do describe "Openflow.to_binary/1" do test "with %Openflow.Error{}" do error = %Openflow.ErrorMsg{ - version: 4, + version: 4, xid: 0, type: :bad_action, code: :unsupported_order, - data: "fugafuga", + data: "fugafuga" } expect = "test/packet_data/ofp_error.raw" - |> File.read! + |> File.read!() assert Openflow.to_binary(error) == expect end test "with experimenter %Openflow.Error{}" do error = %Openflow.ErrorMsg{ - version: 4, + version: 4, xid: 0, type: :experimenter, exp_type: 1, - experimenter: 0xdeadbeef, - data: "hogehoge", + 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 + 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 diff --git a/test/ofp_features_test.exs b/test/ofp_features_test.exs index b03960c..5d38572 100644 --- a/test/ofp_features_test.exs +++ b/test/ofp_features_test.exs @@ -6,8 +6,9 @@ defmodule OfpFeaturesTest do test "with OFP_FEATURES_REQUEST packet" do {:ok, %Openflow.Features.Request{} = features, ""} = "test/packet_data/ofp_features_request.raw" - |> File.read! - |> Openflow.read + |> File.read!() + |> Openflow.read() + assert features.version == 4 assert features.xid == 0 end @@ -15,15 +16,23 @@ defmodule OfpFeaturesTest do test "with OFP_FEATURES_REPLY packet" do {:ok, %Openflow.Features.Reply{} = features, ""} = "test/packet_data/ofp_features_reply.raw" - |> File.read! - |> Openflow.read + |> 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] + + assert features.capabilities == [ + :flow_stats, + :table_stats, + :port_stats, + :group_stats, + :queue_stats + ] end end @@ -33,9 +42,11 @@ defmodule OfpFeaturesTest do version: 4, xid: 0 } + expect = "test/packet_data/ofp_features_request.raw" - |> File.read! + |> File.read!() + assert Openflow.to_binary(features) == expect end end @@ -48,11 +59,13 @@ defmodule OfpFeaturesTest do n_buffers: 255, n_tables: 255, aux_id: 0, - capabilities: [:flow_stats, :table_stats, :port_stats, :group_stats, :queue_stats], + capabilities: [:flow_stats, :table_stats, :port_stats, :group_stats, :queue_stats] } + expect = "test/packet_data/ofp_features_reply.raw" - |> File.read! + |> 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 index 81661a2..9671698 100644 --- a/test/ofp_flow_mod_test.exs +++ b/test/ofp_flow_mod_test.exs @@ -23,37 +23,39 @@ defmodule OfpFlowModTest do assert fm.idle_timeout == 0 assert fm.hard_timeout == 0 assert fm.priority == 123 - assert fm.buffer_id == 0xffff + 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}) - ]) - ] + 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 @@ -69,7 +71,7 @@ defmodule OfpFlowModTest do assert fm.idle_timeout == 0 assert fm.hard_timeout == 0 assert fm.priority == 123 - assert fm.buffer_id == 0xffff + assert fm.buffer_id == 0xFFFF assert fm.out_port == :any assert fm.out_group == :any assert fm.flags == [] @@ -89,15 +91,17 @@ defmodule OfpFlowModTest do assert fm.idle_timeout == 0 assert fm.hard_timeout == 0 assert fm.priority == 123 - assert fm.buffer_id == 0xffff + 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)]) - ] + Openflow.Instruction.Meter.new(1), + Openflow.Instruction.WriteActions.new([Openflow.Action.Output.new(6)]) + ] + assert Openflow.to_binary(fm) == binary end @@ -112,54 +116,57 @@ defmodule OfpFlowModTest do assert fm.idle_timeout == 0 assert fm.hard_timeout == 0 assert fm.priority == 123 - assert fm.buffer_id == 0xffff + 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.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: 651_061_555_542_690_057, + 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 @@ -168,8 +175,8 @@ defmodule OfpFlowModTest do binary = File.read!(@flow_mod5) {:ok, fm, ""} = Openflow.read(binary) - assert fm.cookie == 0x123456789abcdef0 - assert fm.cookie_mask == 0xffffffffffffffff + assert fm.cookie == 0x123456789ABCDEF0 + assert fm.cookie_mask == 0xFFFFFFFFFFFFFFFF assert fm.table_id == 2 assert fm.command == :add assert fm.idle_timeout == 0 @@ -179,51 +186,59 @@ defmodule OfpFlowModTest do 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.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) - ] + 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\ + test "with OFP_FLOW_MOD packet(6)" do {:error, :binary_too_small} = @flow_mod6 - |> File.read! - |> Openflow.read + |> 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.cookie == 0x123456789ABCDEF0 + assert fm.cookie_mask == 0xFFFFFFFFFFFFFFFF assert fm.table_id == 4 assert fm.command == :add assert fm.idle_timeout == 0 @@ -233,21 +248,25 @@ defmodule OfpFlowModTest do 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.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) - ]) - ] + Openflow.Instruction.ApplyActions.new([ + Openflow.Action.NxConjunction.new(clause: 1, id: 0xABCDEF, n_clauses: 2) + ]) + ] + assert Openflow.to_binary(fm) == binary end @@ -255,8 +274,8 @@ defmodule OfpFlowModTest do binary = File.read!(@flow_mod8) {:ok, fm, ""} = Openflow.read(binary) - assert fm.cookie == 0x123456789abcdef0 - assert fm.cookie_mask == 0xffffffffffffffff + assert fm.cookie == 0x123456789ABCDEF0 + assert fm.cookie_mask == 0xFFFFFFFFFFFFFFFF assert fm.table_id == 3 assert fm.command == :add assert fm.idle_timeout == 0 @@ -266,14 +285,16 @@ defmodule OfpFlowModTest do assert fm.out_port == 0 assert fm.out_group == 0 assert fm.flags == [] - assert fm.match == Openflow.Match.new(conj_id: 0xabcdef) + 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) - ] + 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 diff --git a/test/ofp_flow_removed_test.exs b/test/ofp_flow_removed_test.exs index 4a01473..056f78f 100644 --- a/test/ofp_flow_removed_test.exs +++ b/test/ofp_flow_removed_test.exs @@ -6,13 +6,13 @@ defmodule OfpFlowRemovedTest 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 + |> 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.priority == 0xFFFF assert flow_removed.reason == :idle_timeout assert flow_removed.table_id == 0 assert flow_removed.duration_sec == 3 @@ -27,12 +27,12 @@ defmodule OfpFlowRemovedTest do 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 + |> File.read!() + |> Openflow.read() assert flow_removed.version == 4 assert flow_removed.xid == 0 - assert flow_removed.cookie == 0x123456789abcdef0 + assert flow_removed.cookie == 0x123456789ABCDEF0 assert flow_removed.priority == 100 assert flow_removed.reason == :idle_timeout assert flow_removed.table_id == 1 @@ -42,16 +42,17 @@ defmodule OfpFlowRemovedTest do 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} - ] + 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 index 524e99f..9c51812 100644 --- a/test/ofp_get_config_test.exs +++ b/test/ofp_get_config_test.exs @@ -6,8 +6,9 @@ defmodule OfpGetConfigTest 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 + |> File.read!() + |> Openflow.read() + assert config.version == 4 assert config.xid == 0 end @@ -15,8 +16,9 @@ defmodule OfpGetConfigTest do 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 + |> File.read!() + |> Openflow.read() + assert config.version == 4 assert config.xid == 0 assert config.flags == [] @@ -30,9 +32,11 @@ defmodule OfpGetConfigTest do version: 4, xid: 0 } + expect = "test/packet_data/ofp_get_config_request.raw" - |> File.read! + |> File.read!() + assert Openflow.to_binary(config) == expect end @@ -43,9 +47,11 @@ defmodule OfpGetConfigTest do flags: [], miss_send_len: 128 } + expect = "test/packet_data/ofp_get_config_reply.raw" - |> File.read! + |> File.read!() + assert Openflow.to_binary(config) == expect end end diff --git a/test/ofp_group_mod_test.exs b/test/ofp_group_mod_test.exs index cdf7e04..7081095 100644 --- a/test/ofp_group_mod_test.exs +++ b/test/ofp_group_mod_test.exs @@ -12,14 +12,16 @@ defmodule OfpGroupModTest do 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)] - ) - ] + 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 diff --git a/test/ofp_hello_test.exs b/test/ofp_hello_test.exs index 0fd4b1e..5d47026 100644 --- a/test/ofp_hello_test.exs +++ b/test/ofp_hello_test.exs @@ -6,8 +6,9 @@ defmodule OfpHelloTest do test "with OFP_HELLO packet" do {:ok, hello, ""} = "test/packet_data/ofp_hello.raw" - |> File.read! - |> Openflow.read + |> File.read!() + |> Openflow.read() + assert hello.version == 4 assert hello.xid == 0 assert hello.elements == [versionbitmap: [30, 10, 9, 3, 2, 1]] @@ -17,9 +18,11 @@ defmodule OfpHelloTest do 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! + |> File.read!() + assert Openflow.to_binary(hello) == expect end end diff --git a/test/ofp_packet_in_test.exs b/test/ofp_packet_in_test.exs index a59e22e..b39a9a8 100644 --- a/test/ofp_packet_in_test.exs +++ b/test/ofp_packet_in_test.exs @@ -6,8 +6,8 @@ defmodule OfpPacketInTest 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 + |> File.read!() + |> Openflow.read() assert pktin.version == 4 assert pktin.xid == 0 @@ -15,73 +15,75 @@ defmodule OfpPacketInTest do 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" - ] + 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 + |> 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.in_port == 84_281_096 + 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} - ] + 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: 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: 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: 651_061_555_542_690_057, + 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 index 332ba10..07075da 100644 --- a/test/ofp_packet_out_test.exs +++ b/test/ofp_packet_out_test.exs @@ -3,27 +3,112 @@ defmodule OfpPacketOutTest do 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 + 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 + |> File.read!() + |> Openflow.read() assert pktout.version == 4 assert pktout.xid == 0 @@ -37,12 +122,12 @@ defmodule OfpPacketOutTest do describe "Openflow.to_binary/1" do pktout = %Openflow.PacketOut{ actions: [Openflow.Action.Output.new(port_number: :all)], - data: @packet + data: @packet } expect = "test/packet_data/libofproto-OFP13-ofp_packet_out_packet_library.packet" - |> File.read! + |> File.read!() assert Openflow.to_binary(pktout) == expect end diff --git a/test/ofp_port_status_test.exs b/test/ofp_port_status_test.exs index fa6b5ea..0c667b1 100644 --- a/test/ofp_port_status_test.exs +++ b/test/ofp_port_status_test.exs @@ -6,8 +6,8 @@ defmodule OfpPortStatusTest do test "with OFP_PORT_STATUS packet" do {:ok, port_status, ""} = "test/packet_data/libofproto-OFP13-port_status.packet" - |> File.read! - |> Openflow.read + |> File.read!() + |> Openflow.read() assert port_status.version == 4 assert port_status.xid == 0 @@ -27,8 +27,8 @@ defmodule OfpPortStatusTest do 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 + |> File.read!() + |> Openflow.read() assert port_status.version == 4 assert port_status.xid == 0 diff --git a/test/ofp_set_config_test.exs b/test/ofp_set_config_test.exs index 7266c6f..fd7b7e6 100644 --- a/test/ofp_set_config_test.exs +++ b/test/ofp_set_config_test.exs @@ -6,8 +6,9 @@ defmodule OfpSetConfigTest do test "with OFP_SET_CONFIG packet" do {:ok, %Openflow.SetConfig{} = config, ""} = "test/packet_data/ofp_set_config.raw" - |> File.read! - |> Openflow.read + |> File.read!() + |> Openflow.read() + assert config.version == 4 assert config.xid == 0 assert config.flags == [] @@ -23,9 +24,11 @@ defmodule OfpSetConfigTest do flags: [], miss_send_len: 128 } + expect = "test/packet_data/ofp_set_config.raw" - |> File.read! + |> File.read!() + assert Openflow.to_binary(config) == expect end end diff --git a/test/pf.ex b/test/pf.ex index 6257408..e96109c 100644 --- a/test/pf.ex +++ b/test/pf.ex @@ -8,11 +8,9 @@ defmodule Pf do end defmodule State do - defstruct [ - ifname: nil, - pcap_ref: nil, - tester_pid: nil - ] + defstruct ifname: nil, + pcap_ref: nil, + tester_pid: nil end def inject!(pid, packet, payload \\ "") do @@ -33,24 +31,28 @@ defmodule Pf do end def handle_cast({:inject, {headers, payload}}, state) do - headers_bin = for header <- headers do - case header do - ether() -> :pkt.ether(header) - {:"802.1q", _, _, _, _} = vlan -> :pkt_802_1q.codec(vlan) - arp() -> :pkt.arp(header) - ipv4() -> :pkt.ipv4(header) - lldp() -> :pkt.lldp(header) - udp() -> :pkt.udp(header) - tcp() -> :pkt.tcp(header) + headers_bin = + for header <- headers do + case header do + ether() -> :pkt.ether(header) + {:"802.1q", _, _, _, _} = vlan -> :pkt_802_1q.codec(vlan) + arp() -> :pkt.arp(header) + ipv4() -> :pkt.ipv4(header) + lldp() -> :pkt.lldp(header) + udp() -> :pkt.udp(header) + tcp() -> :pkt.tcp(header) + end end - end + binary = Enum.join(headers_bin, "") :epcap.send(state.pcap_ref, <>) {:noreply, state} end + def handle_cast(:stop, state) do {:stop, :normal, state} end + def handle_cast(_req, state) do {:noreply, state} end @@ -61,6 +63,7 @@ defmodule Pf do send(state.tester_pid, {to_string(state.ifname), Enum.take(packet, packet_len - 1)}) {:noreply, state} end + def handle_info(_info, state) do {:noreply, state} end From 4c517a8147d80033405877d4d47d3488ae51eab8 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Wed, 31 Jan 2018 02:31:56 +0900 Subject: [PATCH 29/50] Add API for get current transaction ID --- lib/tres/controller.ex | 5 ++++- lib/tres/secure_channel.ex | 5 +++++ lib/tres/switch_registry.ex | 13 +++++++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/tres/controller.ex b/lib/tres/controller.ex index c9e0a38..15cdaa1 100644 --- a/lib/tres/controller.ex +++ b/lib/tres/controller.ex @@ -1,7 +1,10 @@ defmodule Tres.Controller do def controller_helpers do quote do - import Tres.SwitchRegistry, only: [send_message: 2] + import Tres.SwitchRegistry, only: [ + send_message: 2, + get_current_xid: 1 + ] use Tres.Messages use Tres.MessageHelper diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index b486182..e44d022 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -251,6 +251,11 @@ defmodule Tres.SecureChannel do :keep_state_and_data end + defp handle_CONNECTED({:call, from}, :get_xid, state_data) do + xid = State.get_transaction_id(state_data.xid) + {:keep_state_and_data, [{:reply, from, {:ok, xid}}]} + end + defp handle_CONNECTED(:cast, {:send_message, message} = action, state_data) do new_action_queue = if XACT_KV.is_empty(state_data.xact_kv_ref) do diff --git a/lib/tres/switch_registry.ex b/lib/tres/switch_registry.ex index 40653b6..7fafd99 100644 --- a/lib/tres/switch_registry.ex +++ b/lib/tres/switch_registry.ex @@ -19,13 +19,22 @@ defmodule Tres.SwitchRegistry do end def send_message(message, {_dpid, _aux_id} = datapath_id) do - Registry.dispatch(__MODULE__, datapath_id, &dispatch(&1, message)) + Registry.dispatch(__MODULE__, datapath_id, &do_send_message(&1, message)) end def send_message(message, dpid) when is_binary(dpid) do send_message(message, {dpid, 0}) end + def get_current_xid({_dpid, _aux_id} = datapath_id) do + [{pid, _}|_] = Registry.lookup(__MODULE__, datapath_id) + :gen_statem.call(pid, :get_xid, 1000) + end + + def get_current_xid(datapath_id) do + get_current_xid({datapath_id, 0}) + end + def monitor(datapath_id) do datapath_id |> lookup_pid @@ -34,7 +43,7 @@ defmodule Tres.SwitchRegistry do # private function - defp dispatch(entries, message) do + defp do_send_message(entries, message) do for {pid, _} <- entries, do: :gen_statem.cast(pid, {:send_message, message}) end end From 0df6336f5e01d5be3c68d8ced3af6383e990c279 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Wed, 31 Jan 2018 03:23:31 +0900 Subject: [PATCH 30/50] Update Learning Switch --- examples/learning_switch/lib/learning_switch/ofctl.ex | 9 +++------ examples/learning_switch/mix.lock | 9 +++++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/learning_switch/lib/learning_switch/ofctl.ex b/examples/learning_switch/lib/learning_switch/ofctl.ex index 19c6121..21b123f 100644 --- a/examples/learning_switch/lib/learning_switch/ofctl.ex +++ b/examples/learning_switch/lib/learning_switch/ofctl.ex @@ -18,7 +18,6 @@ defmodule LearningSwitch.Ofctl do defmodule State do defstruct [ datapath_id: nil, - conn_ref: nil, fdb_pid: nil ] end @@ -27,14 +26,12 @@ defmodule LearningSwitch.Ofctl do GenServer.start_link(__MODULE__, [datapath_id, args]) end - def init([datapath_id, _args]) do + def init([{datapath_id, _aux_id}, _args]) do :ok = debug("Switch Ready: datapath_id: #{inspect(datapath_id)}") - conn_ref = SwitchRegistry.monitor(datapath_id) {:ok, pid} = FDB.start_link init_datapath(datapath_id) state = %State{ datapath_id: datapath_id, - conn_ref: conn_ref, fdb_pid: pid } {:ok, state} @@ -48,8 +45,8 @@ defmodule LearningSwitch.Ofctl do :ok = debug("PacketIn: eth_src: #{eth_src} datapath_id: #{inspect(state.datapath_id)}") {:noreply, state} end - def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do - :ok = debug("Switch Disconnected: datapath_id: #{inspect(state.datapath_id)}") + def handle_info({:switch_disconnected, reason}, state) do + :ok = warn("[#{__MODULE__}] Switch Disconnected: datapath_id: #{state.datapath_id} by #{reason}") {:stop, :normal, state} end def handle_info(info, state) do diff --git a/examples/learning_switch/mix.lock b/examples/learning_switch/mix.lock index 9380bbc..cbc80d1 100644 --- a/examples/learning_switch/mix.lock +++ b/examples/learning_switch/mix.lock @@ -1,2 +1,7 @@ -%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, - "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"}} +%{ + "binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, + "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, + "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, + "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"}, + "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}, +} From 658b7448dac4cbc0a95209ca4b8e7a8609163cd8 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 2 Feb 2018 01:55:46 +0900 Subject: [PATCH 31/50] Fix to handler start_link fail --- README.md | 4 +- examples/heckle/.gitignore | 20 - examples/heckle/README.md | 21 - examples/heckle/config/config.exs | 31 - examples/heckle/lib/heckle.ex | 18 - examples/heckle/lib/heckle/application.ex | 20 - examples/heckle/lib/heckle/controller.ex | 201 --- examples/heckle/lib/heckle/flow_patterns.ex | 125 -- .../heckle/lib/heckle/pipeline_profiles.ex | 32 - examples/heckle/mix.exs | 23 - examples/heckle/mix.lock | 6 - examples/heckle/test/heckle_test.exs | 8 - examples/heckle/test/test_helper.exs | 1 - lib/tres/secure_channel.ex | 26 +- test/flay.ex | 250 ---- test/flog_test.exs | 1166 ----------------- test/pf.ex | 77 -- 17 files changed, 17 insertions(+), 2012 deletions(-) delete mode 100644 examples/heckle/.gitignore delete mode 100644 examples/heckle/README.md delete mode 100644 examples/heckle/config/config.exs delete mode 100644 examples/heckle/lib/heckle.ex delete mode 100644 examples/heckle/lib/heckle/application.ex delete mode 100644 examples/heckle/lib/heckle/controller.ex delete mode 100644 examples/heckle/lib/heckle/flow_patterns.ex delete mode 100644 examples/heckle/lib/heckle/pipeline_profiles.ex delete mode 100644 examples/heckle/mix.exs delete mode 100644 examples/heckle/mix.lock delete mode 100644 examples/heckle/test/heckle_test.exs delete mode 100644 examples/heckle/test/test_helper.exs delete mode 100644 test/flay.ex delete mode 100644 test/flog_test.exs delete mode 100644 test/pf.ex diff --git a/README.md b/README.md index 305c5c7..79d0845 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,12 @@ Tres is a framework and set of helper libraries to develop OpenFlow controllers in Elixir. -the purpose of this project is to design and prototype an experimental network switch controller that implements the OpenFlow 1.3.x. The project explores the scalability and robustness of such controllers on a scale much larger than typically considered: 100,000s of endpoints, 10,000s of switches. - ## Installation ```elixir def deps do [ - {:tres, git: "https://gh.iiji.jp/Isono/tres", branch: "develop"} + {:tres, github: "shun159/tres", branch: "develop"} ] end ``` diff --git a/examples/heckle/.gitignore b/examples/heckle/.gitignore deleted file mode 100644 index 12179ea..0000000 --- a/examples/heckle/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -# 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/examples/heckle/README.md b/examples/heckle/README.md deleted file mode 100644 index eabaf9d..0000000 --- a/examples/heckle/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Heckle - -**TODO: Add description** - -## Installation - -If [available in Hex](https://hex.pm/docs/publish), the package can be installed -by adding `heckle` to your list of dependencies in `mix.exs`: - -```elixir -def deps do - [ - {:heckle, "~> 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/heckle](https://hexdocs.pm/heckle). - diff --git a/examples/heckle/config/config.exs b/examples/heckle/config/config.exs deleted file mode 100644 index 0b8ca46..0000000 --- a/examples/heckle/config/config.exs +++ /dev/null @@ -1,31 +0,0 @@ -# This file is responsible for configuring your application -# and its dependencies with the aid of the Mix.Config module. -use Mix.Config - -config :heckle, - vlan_tagging: true, - vlan_id: 123, - vlan_trunk1: "1", - vlan_trunk2: "4", - access_port1: "2", - access_port2: "3", - receiver_mac: "1a2c3d0e0191", - receiver_ip: {192,168,123,1}, - sender_mac: "cec9bd531dfc", - inside_local: {192,168,11,1}, - outside_local: {192,168,123,2}, - flow_pattern: :nat # :nat || :bum - -config :tres, - protocol: :tcp, - port: 6633, - max_connections: 10, - num_acceptors: 10, - callback_module: Heckle.Controller, - callback_args: ["0002b05ada98d790"] - -config :logger, - level: :info, - format: "$date $time [$level] $metadata$message\n", - metadata: [:application], - handle_otp_reports: true diff --git a/examples/heckle/lib/heckle.ex b/examples/heckle/lib/heckle.ex deleted file mode 100644 index ff5d007..0000000 --- a/examples/heckle/lib/heckle.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule Heckle do - @moduledoc """ - Documentation for Heckle. - """ - - @doc """ - Hello world. - - ## Examples - - iex> Heckle.hello - :world - - """ - def hello do - :world - end -end diff --git a/examples/heckle/lib/heckle/application.ex b/examples/heckle/lib/heckle/application.ex deleted file mode 100644 index 7f46096..0000000 --- a/examples/heckle/lib/heckle/application.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule Heckle.Application do - # See https://hexdocs.pm/elixir/Application.html - # for more information on OTP Applications - @moduledoc false - - use Application - - def start(_type, _args) do - # List all child processes to be supervised - children = [ - # Starts a worker by calling: Heckle.Worker.start_link(arg) - # {Heckle.Worker, arg}, - ] - - # See https://hexdocs.pm/elixir/Supervisor.html - # for other strategies and supported options - opts = [strategy: :one_for_one, name: Heckle.Supervisor] - Supervisor.start_link(children, opts) - end -end diff --git a/examples/heckle/lib/heckle/controller.ex b/examples/heckle/lib/heckle/controller.ex deleted file mode 100644 index 0eded9e..0000000 --- a/examples/heckle/lib/heckle/controller.ex +++ /dev/null @@ -1,201 +0,0 @@ -defmodule Heckle.Controller do - use GenServer - use Bitwise - use Tres.Controller - - import Logger - - alias Heckle.PipelineProfiles - alias Heckle.FlowPatterns - - defmodule State do - defstruct [ - dpid: nil, - conn_ref: nil, - access_port1_name: nil, - access_port2_name: nil, - trunk_port1_name: nil, - trunk_port2_name: nil, - access_port1: nil, - access_port2: nil, - trunk_port1: nil, - trunk_port2: nil, - vlan_tagging: nil, - vlan_id: nil, - receiver_mac: nil, - receiver_ip: nil, - sender_mac: nil, - inside_local: nil, - outside_local: nil, - flow_pattern: nil - ] - end - - def start_link(dpid, args) do - GenServer.start(__MODULE__, [dpid, args]) - end - - def init([{dpid, _aux_id}, [dpid]]) do - :ok = info("Switch Ready: dpid: #{inspect(dpid)} on #{inspect(self())}") - state = init_state(dpid) - {:ok, state} - end - def init([{dpid, _aux_id}, [_dpid]]) do - :ok = info("Switch Ready: dpid: #{inspect(dpid)} but not acceptable") - :ignore - end - - def handle_cast(:send_flows, state) do - :ok = print_flows(state) - - state - |> FlowPatterns.flows - |> Enum.each(&send_flow_mod_add(state.dpid, &1)) - {:noreply, state} - end - - def handle_info(%Desc.Reply{mfr_desc: "Aruba"} = desc, %State{dpid: dpid} = state) do - info("Switch Desc: mfr = #{desc.mfr_desc} hw = #{desc.hw_desc} sw = #{desc.sw_desc}") - :ok = PipelineProfiles.of_aruba() - |> TableFeatures.Request.new - |> send_message(dpid) - {:noreply, state} - end - def handle_info(%Desc.Reply{} = desc, state) do - :ok = info("Switch Desc: mfr = #{desc.mfr_desc} hw = #{desc.hw_desc} sw = #{desc.sw_desc}") - :ok = GenServer.cast(self(), :send_flows) - {:noreply, state} - end - def handle_info(%TableFeatures.Reply{xid: xid}, state) do - :ok = info("Pipeline modification is success (xid: #{xid})") - :ok = GenServer.cast(self(), :send_flows) - {:noreply, state} - end - def handle_info(%Flow.Reply{flows: flows}, state) do - for flow <- flows do - :ok = info( - "table_id: #{flow.table_id} " <> - "pkt_count: #{flow.packet_count} "<> - "byt_count: #{flow.byte_count} "<> - "match: #{inspect(flow.match)} "<> - "insts: #{inspect(flow.instructions)} " - ) - end - {:noreply, state} - end - def handle_info(%PortDesc.Reply{ports: ports}, state) do - info("Received Port Desc") - access_port1 = Enum.find(ports, fn(port) -> port.name == state.access_port1_name end) - access_port2 = Enum.find(ports, fn(port) -> port.name == state.access_port2_name end) - trunk_port1 = Enum.find(ports, fn(port) -> port.name == state.trunk_port1_name end) - trunk_port2 = Enum.find(ports, fn(port) -> port.name == state.trunk_port2_name end) - :ok = desc_stats_request(state.dpid) - {:noreply, %{state| - access_port1: access_port1, - access_port2: access_port2, - trunk_port1: trunk_port1, - trunk_port2: trunk_port2} - } - end - def handle_info(%ErrorMsg{code: code, type: type, data: data, xid: xid}, state) do - :ok = warn("Request Failed(xid: #{xid}):"<> - " code: #{code}"<> - " type: #{type}"<> - " data: #{inspect(data)}"<> - " dpid: #{inspect(state.dpid)}") - {:stop, :request_failed, state} - end - def handle_info(%PacketIn{data: data}, state) do - :ok = warn("Table miss occured:"<> - " data: #{inspect(:pkt.decapsulate(data))}"<> - " dpid: #{inspect(state.dpid)}") - {:noreply, state} - end - def handle_info({:'DOWN', ref, :process, _pid, _reason}, %State{conn_ref: ref} = state) do - :ok = debug("Switch Disconnected: dpid: #{inspect(state.dpid)}") - {:stop, :normal, state} - end - def handle_info(info, state) do - :ok = info("Unhandled message: #{inspect(info)}") - {:noreply, state} - end - - # private functions - defp init_state(dpid) do - :ok = init_datapath(dpid) - conn_ref = SwitchRegistry.monitor(dpid) - config = Application.get_all_env(:heckle) - %State{ - dpid: dpid, - conn_ref: conn_ref, - access_port1_name: config[:access_port1], - access_port2_name: config[:access_port2], - trunk_port1_name: config[:vlan_trunk1], - trunk_port2_name: config[:vlan_trunk2], - vlan_tagging: config[:vlan_tagging] || true, - vlan_id: 0x1000 ||| (config[:vlan_id] || 0), - receiver_mac: config[:receiver_mac], - receiver_ip: config[:receiver_ip], - sender_mac: config[:sender_mac], - inside_local: config[:inside_local], - outside_local: config[:outside_local], - flow_pattern: config[:flow_pattern] || :nat - } - end - - defp init_datapath(dpid) do - :ok = send_flow_mod_delete(dpid) - :ok = port_desc_stats_request(dpid) - :ok = set_config(dpid) - end - - defp desc_stats_request(dpid) do - :ok = send_message(Desc.Request.new, dpid) - end - - defp port_desc_stats_request(dpid) do - :ok = send_message(PortDesc.Request.new, dpid) - end - - defp set_config(dpid) do - :ok = send_message(SetConfig.new(miss_send_len: :no_buffer), dpid) - end - - defp print_flows(state) do - state - |> FlowPatterns.flows - |> Enum.each(&print_flow/1) - end - - defp print_flow(flow_opts) do - flow_opts - |> FlowMod.new - |> Openflow.to_binary - |> binary_to_space_delimited_hex - |> ofp_print_cmd - |> Logger.info - end - - defp ofp_print_cmd(print_args) do - {result, _code} = System.cmd("ovs-ofctl", ["ofp-print", "#{print_args}"]) - result - end - - defp binary_to_space_delimited_hex(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/examples/heckle/lib/heckle/flow_patterns.ex b/examples/heckle/lib/heckle/flow_patterns.ex deleted file mode 100644 index 1c2e036..0000000 --- a/examples/heckle/lib/heckle/flow_patterns.ex +++ /dev/null @@ -1,125 +0,0 @@ -defmodule Heckle.FlowPatterns do - use Bitwise - use Tres.Controller - - @mcast {"010000000000", "010000000000"} - - def flows(%Heckle.Controller.State{flow_pattern: :nat} = state) do - [[table_id: 1, - priority: 30, - cookie: 0x3000000000000001, - match: Match.new( - eth_dst: state.receiver_mac, - eth_src: state.sender_mac, - eth_type: 0x0800, - ipv4_src: state.inside_local, - ipv4_dst: state.receiver_ip - ), - instructions: [ - ApplyActions.new([ - SetField.new({:eth_dst, state.receiver_mac}), - SetField.new({:ipv4_src, state.outside_local}), - Output.new(state.trunk_port2.number) - ])] - ], - [table_id: 1, - priority: 30, - cookie: 0x3000000000000001, - match: Match.new( - eth_dst: state.sender_mac, - eth_src: state.receiver_mac, - eth_type: 0x0800, - vlan_vid: 123, - ipv4_src: state.receiver_ip, - ipv4_dst: state.outside_local - ), - instructions: [ - ApplyActions.new([ - PopVlan.new, - SetField.new({:eth_src, state.receiver_mac}), - SetField.new({:eth_dst, state.sender_mac}), - SetField.new({:ipv4_dst, state.inside_local}), - Output.new(state.access_port1.number) - ])] - ], - ] ++ classifier(state) - end - def flows(%Heckle.Controller.State{flow_pattern: :bum} = state) do - [[table_id: 1, - priority: 30, - cookie: 0x3000000000000001, - match: Match.new( - vlan_vid: state.vlan_id, - eth_dst: @mcast - ), - instructions: [ - ApplyActions.new([ - Openflow.Action.Output.new(state.trunk_port1.number), - Openflow.Action.PopVlan.new, - Openflow.Action.Output.new(state.access_port1.number), - Openflow.Action.Output.new(state.access_port2.number) - ])] - ]] ++ classifier(state) - end - - # private functions - - defp classifier(state) do - [ - [table_id: 0, - priority: 50, - cookie: 0x1000000000000001, - match: Match.new( - in_port: state.trunk_port1.number, - vlan_vid: state.vlan_id - ), - instructions: [GotoTable.new(1)] - ], - [table_id: 0, - priority: 50, - cookie: 0x1000000000000001, - match: Match.new( - in_port: state.trunk_port2.number, - vlan_vid: state.vlan_id - ), - instructions: [GotoTable.new(1)] - ], - [table_id: 0, - priority: 20, - cookie: 0x1000000000000001, - match: Match.new( - in_port: state.access_port1.number, - eth_src: state.sender_mac, - ), - instructions: [ - ApplyActions.new([ - PushVlan.new, - SetField.new({:vlan_vid, state.vlan_id}) - ]), - GotoTable.new(1) - ] - ], - [table_id: 0, - priority: 20, - cookie: 0x1000000000000001, - match: Match.new( - in_port: state.access_port2.number, - eth_src: state.sender_mac, - ), - instructions: [ - ApplyActions.new([ - PushVlan.new, - SetField.new({:vlan_vid, state.vlan_id}) - ]), - GotoTable.new(1) - ] - ], - [table_id: 0, - priority: 1, - match: Match.new, - cookie: 0x1000000000000001, - instructions: [ApplyActions.new(Output.new(:controller))] - ], - ] - end -end diff --git a/examples/heckle/lib/heckle/pipeline_profiles.ex b/examples/heckle/lib/heckle/pipeline_profiles.ex deleted file mode 100644 index 6327d1a..0000000 --- a/examples/heckle/lib/heckle/pipeline_profiles.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Heckle.PipelineProfiles do - use Tres.Controller - - def of_aruba do - [ - TableFeatures.Body.new( - table_id: 0, - name: "classifier", - max_entries: 10, - config: [:table_miss_mask], # deprecated mask. - match: [:in_port, :vlan_vid, :eth_src], - wildcards: [:in_port, :vlan_vid, :eth_src], - instructions: [GotoTable, ApplyActions], - apply_actions: [Output, PushVlan, SetField, PopVlan], - apply_setfield: [:eth_dst, :vlan_vid], - next_tables: [1], - ), - TableFeatures.Body.new( - table_id: 1, - name: "process", - max_entries: 10, - config: [:table_miss_mask], - match: [:eth_src, :masked_eth_dst, :vlan_vid, :eth_type, :ipv4_src, :ipv4_dst], - wildcards: [:eth_src, :masked_eth_dst, :vlan_vid, :eth_type, :ipv4_src, :ipv4_dst], - instructions: [ApplyActions], - apply_actions: [SetField, PopVlan, PushVlan, Output], - apply_setfield: [:eth_src, :eth_dst, :vlan_vid, :ipv4_src, :ipv4_dst], - next_tables: [], - ) - ] - end -end diff --git a/examples/heckle/mix.exs b/examples/heckle/mix.exs deleted file mode 100644 index 06e195a..0000000 --- a/examples/heckle/mix.exs +++ /dev/null @@ -1,23 +0,0 @@ -defmodule Heckle.Mixfile do - use Mix.Project - - @tres_path "../../../tres" - - def project do - [app: :heckle, - version: "0.1.0", - elixir: "~> 1.5", - start_permanent: Mix.env == :prod, - deps: deps()] - end - - def application do - [extra_applications: [:logger, :tres], - mod: {Heckle.Application, []}] - end - - defp deps do - [{:tres, path: @tres_path}, - {:pkt, github: "msantos/pkt", branch: "master"}] - end -end diff --git a/examples/heckle/mix.lock b/examples/heckle/mix.lock deleted file mode 100644 index e337173..0000000 --- a/examples/heckle/mix.lock +++ /dev/null @@ -1,6 +0,0 @@ -%{"binpp": {:git, "https://github.com/jtendo/binpp.git", "64bd68d215d1a6cd35871e7c134d7fe2e46214ea", [branch: "master"]}, - "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, - "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, - "pkt": {:git, "https://github.com/msantos/pkt.git", "3afb1967f34324c1dec5035a6e36232da815c2e6", [branch: "master"]}, - "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [], [], "hexpm"}, - "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}} diff --git a/examples/heckle/test/heckle_test.exs b/examples/heckle/test/heckle_test.exs deleted file mode 100644 index 855f884..0000000 --- a/examples/heckle/test/heckle_test.exs +++ /dev/null @@ -1,8 +0,0 @@ -defmodule HeckleTest do - use ExUnit.Case - doctest Heckle - - test "greets the world" do - assert Heckle.hello() == :world - end -end diff --git a/examples/heckle/test/test_helper.exs b/examples/heckle/test/test_helper.exs deleted file mode 100644 index 869559e..0000000 --- a/examples/heckle/test/test_helper.exs +++ /dev/null @@ -1 +0,0 @@ -ExUnit.start() diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index e44d022..d0c028b 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -124,15 +124,19 @@ defmodule Tres.SecureChannel do defp init_handler(state_data) do %State{datapath_id: dpid, aux_id: aux_id} = state_data - {:ok, pid} = MessageHandlerSup.start_child({dpid, aux_id}) - ref = Process.monitor(pid) - %{state_data | handler_pid: pid, handler_ref: ref} + case MessageHandlerSup.start_child({dpid, aux_id}) do + {:ok, pid} -> + ref = Process.monitor(pid) + %{state_data | handler_pid: pid, handler_ref: ref} + {:error, reason} -> + {:stop, reason} + end end # INIT state defp handle_INIT(:enter, _old_state, state_data) do debug( - "[#{__MODULE__}] Initiate HELLO handshake: " <> ">#{state_data.ip_addr}:#{state_data.port}" + "[#{__MODULE__}] Initiate HELLO handshake: " <> "#{state_data.ip_addr}:#{state_data.port}" ) initiate_hello_handshake(state_data) @@ -202,9 +206,13 @@ defmodule Tres.SecureChannel do # CONNECTED state defp handle_CONNECTED(:enter, :CONNECTING, state_data) do - new_state_data = init_handler(state_data) - start_periodic_idle_check() - {:keep_state, new_state_data} + case init_handler(state_data) do + %State{} = new_state_data -> + start_periodic_idle_check() + {:keep_state, new_state_data} + {:stop, reason} -> + close_connection({:handler_down, reason}, state_data) + end end defp handle_CONNECTED(:info, :idle_check, state_data) do @@ -563,10 +571,8 @@ defmodule Tres.SecureChannel do {:stop, :normal, %{state_data | socket: nil}} end - defp close_connection({:handler_down = disconnected_reason, reason}, state_data) do + defp close_connection({:handler_down = _disconnected_reason, reason}, state_data) do warn("[#{__MODULE__}] connection terminated: Handler process down by #{reason}") - %State{handler_pid: handler_pid} = state_data - send(handler_pid, {:switch_disconnected, disconnected_reason}) {:stop, :normal, %{state_data | socket: nil}} end diff --git a/test/flay.ex b/test/flay.ex deleted file mode 100644 index 2c26cf5..0000000 --- a/test/flay.ex +++ /dev/null @@ -1,250 +0,0 @@ -defmodule Flay do - use GenServer - use Tres.Controller - - import Logger - - defmodule State do - defstruct datapath_id: nil, - tester_pid: nil, - conn_ref: nil, - reply_to: nil, - default_profile: nil - end - - def start_link(datapath, args) do - GenServer.start_link(__MODULE__, [datapath, args], name: __MODULE__) - end - - def init(args) do - state = init_controller(args) - GenServer.cast(Flay, :desc_stats) - GenServer.cast(Flay, :flow_del) - {:ok, state} - end - - def handle_call(:port_desc_stats, from, state) do - send_message(PortDesc.Request.new(), state.datapath_id) - {:noreply, %{state | reply_to: from}} - end - - def handle_call(:flow_stats, from, state) do - send_message(Flow.Request.new(), state.datapath_id) - {:noreply, %{state | reply_to: from}} - end - - def handle_cast(:desc_stats, state) do - send_message(Desc.Request.new(), state.datapath_id) - {:noreply, state} - end - - def handle_cast({:register_pid, tester_pid}, state) do - {:noreply, %{state | tester_pid: tester_pid}} - end - - def handle_cast({:flow_install, flow_opts, tester_pid}, state) do - send_flow_mod_add(state.datapath_id, flow_opts) - flow_opts_to_ofp_print(flow_opts) - {:noreply, %{state | tester_pid: tester_pid}} - end - - def handle_cast(:flow_del, state) do - send_flow_mod_delete(state.datapath_id) - {:noreply, state} - end - - def handle_cast({:flow_del, cookie}, state) do - send_flow_mod_delete(state.datapath_id, cookie: cookie, cookie_mask: 0xFFFFFFFFFFFFFFFF) - {:noreply, state} - end - - def handle_info(%ErrorMsg{} = error, state) do - send(state.tester_pid, error) - {:noreply, state} - end - - def handle_info(%PacketIn{} = pktin, state) do - send(state.tester_pid, pktin) - {:noreply, state} - end - - def handle_info(%TableFeatures.Reply{} = table, state) do - {:noreply, %{state | default_profile: table}} - end - - def handle_info(%PortDesc.Reply{} = desc, state) do - GenServer.reply(state.reply_to, desc) - {:noreply, state} - end - - def handle_info(%Desc.Reply{} = desc, state) do - info( - "[#{__MODULE__}] Switch Desc: " <> - "mfr = #{desc.mfr_desc} " <> "hw = #{desc.hw_desc} " <> "sw = #{desc.sw_desc} " - ) - - init_bridge(state.datapath_id, desc) - {:noreply, state} - end - - def handle_info(%Flow.Reply{} = desc, state) do - GenServer.reply(state.reply_to, desc) - {:noreply, %{state | reply_to: nil}} - end - - # `Catch all` function is required. - def handle_info(info, state) do - :ok = warn("[#{__MODULE__}] unhandled message #{inspect(info)}") - {:noreply, state} - end - - # private functions - - defp flow_opts_to_ofp_print(flow_opts) do - flow_opts - |> FlowMod.new() - |> Openflow.to_binary() - |> binary_to_space_delimited_hex - |> ofp_print_cmd - |> Logger.info() - end - - defp ofp_print_cmd(print_args) do - {result, _code} = System.cmd("ovs-ofctl", ["ofp-print", "#{print_args}"]) - result - end - - defp binary_to_space_delimited_hex(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 - - defp init_controller([datapath_id, tester_pid]) do - conn_ref = SwitchRegistry.monitor(datapath_id) - - %State{ - datapath_id: datapath_id, - tester_pid: tester_pid, - conn_ref: conn_ref - } - end - - defp init_bridge(datapath_id, %Desc.Reply{mfr_desc: "Aruba"}) do - :ok = info("Transform flow table pipeline") - - tables = [ - TableFeatures.Body.new( - table_id: 0, - name: "classifier", - max_entries: 50, - config: [:table_miss_mask], - match: [ - :in_port, - :eth_type, - :eth_src, - :masked_eth_dst, - :ip_proto, - :vlan_vid, - :ipv4_src, - :udp_dst, - :tcp_dst - ], - wildcards: [ - :in_port, - :eth_src, - :eth_type, - :masked_eth_dst, - :vlan_vid, - :ip_proto, - :ipv4_src, - :udp_dst, - :tcp_dst - ], - instructions: [ - Openflow.Instruction.GotoTable, - Openflow.Instruction.ApplyActions - ], - apply_actions: [ - Openflow.Action.Output, - Openflow.Action.PushVlan, - Openflow.Action.PopVlan, - Openflow.Action.SetField - ], - apply_setfield: [ - :eth_dst, - :vlan_vid - ], - next_tables: [ - 1 - ] - ), - TableFeatures.Body.new( - table_id: 1, - name: "admission_control", - max_entries: 50, - config: [:table_miss_mask], - match: [ - :eth_type, - :eth_src, - :masked_eth_dst, - :vlan_vid, - :ip_proto, - :udp_dst, - :tcp_dst, - :ipv4_src, - :ipv4_dst - ], - wildcards: [ - :eth_type, - :eth_src, - :masked_eth_dst, - :vlan_vid, - :ip_proto, - :udp_dst, - :tcp_dst, - :ipv4_src, - :ipv4_dst - ], - instructions: [ - Openflow.Instruction.GotoTable, - Openflow.Instruction.ApplyActions - ], - apply_actions: [ - Openflow.Action.Output, - Openflow.Action.PushVlan, - Openflow.Action.PopVlan, - Openflow.Action.SetField - ], - apply_setfield: [ - :eth_dst, - :vlan_vid, - :ipv4_src, - :ipv4_dst - ] - ) - ] - - TableFeatures.Request.new(tables) - |> send_message(datapath_id) - - send_flow_mod_delete(datapath_id, table_id: :all) - end - - defp init_bridge(_datapath_id, _mfr) do - :ok = info("Flow pipeline profile is not defined") - :ok - end -end diff --git a/test/flog_test.exs b/test/flog_test.exs deleted file mode 100644 index dc31453..0000000 --- a/test/flog_test.exs +++ /dev/null @@ -1,1166 +0,0 @@ -defmodule FlogTest do - use ExUnit.Case, async: false - use Bitwise - - @listen_port 6653 - - # FIXME: - @vlan_trunk_port "veth0" - # FIXME: - @access_port1 "veth3" - # FIXME: - @access_port2 "veth4" - - # FIXME: - @vlan_trunk_port_sniffer "veth1" - # FIXME: - @access_port1_sniffer "veth2" - # FIXME: - @access_port2_sniffer "veth5" - - @bootnet_vid 0x1000 ||| 5 - @user_vid 0x1000 ||| 123 - @vlan_present {0x1000, 0x1000} - @mcast {"010000000000", "010000000000"} - @sdl_vmac "000000000001" - @bcast "ffffffffffff" - @mac "000102030405" - @auth_ipv4_address {192, 168, 5, 4} - @captive_ipv4_address {192, 168, 5, 5} - @quad_0_ip {0, 0, 0, 0} - @bcast_ip {255, 255, 255, 255} - @client_ip {192, 168, 5, 10} - @client_farm_ip {192, 168, 255, 1} - @gateway_ip {192, 168, 5, 4} - @farm_gw_ip {192, 168, 255, 254} - @farm_gw_mac "00000000000f" - @internet {8, 8, 8, 8} - @trusted_macs [ - "0800274d3297", - "0800274d3298", - "0800274d3299" - ] - - import Record - # Extract Erlang record for msantos/pkt - for {name, schema} <- extract_all(from_lib: "pkt/include/pkt.hrl") do - defrecord(name, schema) - end - - Code.load_file("test/flay.ex") - Code.load_file("test/pf.ex") - - # GIVEN - setup_all do - setup_applications() - wait_for_connected() - ports = get_ports_desc() - vlan_trunk = Enum.find(ports, fn port -> port.name == @vlan_trunk_port end) - port = Enum.find(ports, fn port -> port.name == @access_port1 end) - port2 = Enum.find(ports, fn port -> port.name == @access_port2 end) - cookie = 0x1000000000000001 - cookie2 = 0x2000000000000001 - timeout = 32_678 - - options = [ - vlan_trunk: vlan_trunk, - port: port, - port2: port2, - cookie: cookie, - cookie2: cookie2, - timeout: timeout - ] - - {:ok, options} - end - - describe "switch:merged_handler:table=0,priority=0,cookie=0x8000000000000000,actions=drop" do - test "Install Flow" do - options = [ - cookie: 0x8000000000000000, - table_id: 0, - priority: 0 - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - payload = <<0::size(16)-unit(8)>> - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) - ] - - Pf.inject!(pid1, packet, payload) - refute_receive {@vlan_trunk_port_sniffer, ^packet}, 1000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "switch:merged_handler:" <> - "table=0,priority=200,cookie=0x4000000000000000,in_port=%d,dl_dst=%s,dl_vlan=0x0000/0x1fff,dl_type=%s," <> - "actions=controller" do - test "Install Flow", state do - match = - Openflow.Match.new( - in_port: state.vlan_trunk.number, - eth_dst: @bcast, - vlan_vid: 0x0000, - eth_type: 0x88CC - ) - - action = Openflow.Action.Output.new(:controller) - ins = Openflow.Instruction.ApplyActions.new(action) - - options = [ - cookie: 0x4000000000000000, - table_id: 0, - priority: 200, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(state.vlan_trunk.hw_addr, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x88CC), - lldp( - pdus: [ - chassis_id(value: "hogehoge"), - port_id(subtype: :mac_address, value: shost), - ttl(value: 128) - ] - ) - ] - - Pf.inject!(pid2, packet) - in_port = state.vlan_trunk.number - assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 - refute_receive {@vlan_trunk_port_sniffer, [^packet, ""]}, 1000 - refute_receive {@access_port1_sniffer, [^packet, ""]}, 1000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "switch:merged_handler:" <> - "table=0,priority=201,cookie=0x4000000000000000,in_port=%d,dl_src=%s,actions=drop" do - test "Install Flow", state do - for port <- [state.vlan_trunk, state.port] do - match = Openflow.Match.new(in_port: port.number, eth_src: port.hw_addr) - options = [cookie: 0x4000000000000000, table_id: 0, priority: 201, match: match] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - end - - describe "switch:uplink_escalation_flow:" <> - "table=0,priority=10,cookie=0x1000000000000000,arp,actions=controller" do - test "Install Flow", state do - match = Openflow.Match.new(eth_type: 0x0806) - action = Openflow.Action.Output.new(:controller) - ins = Openflow.Instruction.ApplyActions.new(action) - - options = [ - cookie: 0x1000000000000000, - table_id: 0, - priority: 10, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - payload = <<0::size(16)-unit(8)>> - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) - ] - - Pf.inject!(pid1, packet, payload) - in_port = state.port.number - assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 - refute_receive {@vlan_trunk_port_sniffer, ^packet}, 1000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "switch:uplink_escalation_flow:" <> - "table=0,priority=10,cookie=0x1000000000000000,udp,udp_dst=67,actions=controller" do - test "Install Flow", state do - match = Openflow.Match.new(eth_type: 0x0800, ip_proto: 17, udp_dst: 67) - action = Openflow.Action.Output.new(:controller) - ins = Openflow.Instruction.ApplyActions.new(action) - - options = [ - cookie: 0x1000000000000000, - table_id: 0, - priority: 10, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - - payload = File.read!("test/packet_data/dhcp_discover.raw") - length = 8 + byte_size(payload) - udp_header = udp(sport: 68, dport: 67, ulen: length) - ipv4_header = ipv4(saddr: @quad_0_ip, daddr: @bcast_ip, p: 17, len: 20 + length) - udp_sum = :pkt.makesum([ipv4_header, udp_header, payload]) - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x0800), - ipv4_header, - udp(udp_header, sum: udp_sum) - ] - - Pf.inject!(pid1, packet, <>) - in_port = state.port.number - assert_receive %Openflow.PacketIn{in_port: ^in_port}, 1000 - refute_receive {@vlan_trunk_port_sniffer, ^packet}, 1000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "switch:uplink_escalation_flow:" <> - "table=0,priority=11,cookie=0x1000000000000000,in_port={trunk_port},actions=drop" do - test "Install Flow", state do - match = Openflow.Match.new(in_port: state.vlan_trunk.number) - options = [cookie: 0x1000000000000000, table_id: 0, priority: 11, match: match] - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - payload = <<0::size(16)-unit(8)>> - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) - ] - - Pf.inject!(pid2, packet, payload) - refute_receive {@access_port1_sniffer, ^packet}, 1000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "dl_src={mac},in_port={port_no}," <> - "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,output:{vlan_trunk_port}" do - test "Install Flow", state do - match = - Openflow.Match.new( - in_port: state.port.number, - eth_src: @mac - ) - - actions = [ - Openflow.Action.PushVlan.new(), - Openflow.Action.SetField.new({:vlan_vid, @bootnet_vid}), - Openflow.Action.Output.new(state.vlan_trunk.number) - ] - - ins = Openflow.Instruction.ApplyActions.new(actions) - - options = [ - cookie: state.cookie, - table_id: 0, - priority: 20, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) - ] - - expect = [ - ether(dhost: dhost, shost: shost, type: 0x8100), - {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) - ] - - Pf.inject!(pid1, packet) - assert_receive {@vlan_trunk_port_sniffer, ^expect}, 3000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "in_port={vlan_trunk_port},dl_vlan={vlan},dl_dst={mcast}," <> - "actions=strip_vlan,{outputs}" do - test "Install Flow", state do - match = - Openflow.Match.new( - in_port: state.vlan_trunk.number, - vlan_vid: @bootnet_vid, - eth_dst: @mcast - ) - - actions = [ - Openflow.Action.PopVlan.new(), - Openflow.Action.Output.new(state.port.number), - Openflow.Action.Output.new(state.port2.number) - ] - - ins = Openflow.Instruction.ApplyActions.new(actions) - - options = [ - cookie: state.cookie, - table_id: 0, - priority: 50, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@access_port2_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x8100), - {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) - ] - - expect = [ - ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 1, sha: shost, sip: @client_ip, tip: @gateway_ip) - ] - - Pf.inject!(pid2, packet) - assert_receive {@access_port1_sniffer, ^expect}, 3000 - assert_receive {@access_port2_sniffer, ^expect}, 3000 - Pf.stop(pid1) - Pf.stop(pid2) - Pf.stop(pid3) - end - end - - describe "associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "in_port={vlan_trunk_port},dl_vlan={vlan},dl_dst={mac}," <> - "actions=strip_vlan,output:{port_no}" do - test "Install Flow", state do - match = - Openflow.Match.new( - in_port: state.vlan_trunk.number, - vlan_vid: @bootnet_vid, - eth_dst: @mac - ) - - actions = [ - Openflow.Action.PopVlan.new(), - Openflow.Action.Output.new(state.port.number) - ] - - ins = Openflow.Instruction.ApplyActions.new(actions) - - options = [ - cookie: state.cookie, - table_id: 0, - priority: 50, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) - dhost = Openflow.Match.Field.codec(@mac, :eth_dst) - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x8100), - {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 2, sha: shost, tip: @client_ip, sip: @gateway_ip) - ] - - expect = [ - ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 2, sha: shost, tip: @client_ip, sip: @gateway_ip) - ] - - Pf.inject!(pid2, packet) - assert_receive {@access_port1_sniffer, ^expect}, 3000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "associate:register_bootstrap_rule:" <> - "send_flow_rem," <> "dl_src={mac},in_port={vlan_trunk_port}," <> "actions=drop" do - test "Install Flow", state do - match = - Openflow.Match.new( - in_port: state.vlan_trunk.number, - eth_src: @mac - ) - - options = [ - cookie: state.cookie, - table_id: 0, - priority: 19, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - payload = <<0::size(16)-unit(8)>> - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 2, sha: shost, sip: @client_ip, tip: @gateway_ip) - ] - - Pf.inject!(pid2, packet, payload) - refute_receive {@access_port1_sniffer, ^packet}, 3000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "associate:register_bootstrap_rule:" <> - "send_flow_rem," <> - "in_port={vlan_trunk_port},dl_vlan={vlan},dl_src={trusted},dl_dst={mac}," <> - "actions=strip_vlan,output:{port_no}" do - test "Install Flow", state do - for trusted <- @trusted_macs do - match = - Openflow.Match.new( - in_port: state.vlan_trunk.number, - vlan_vid: @bootnet_vid, - eth_src: trusted, - eth_dst: @mac - ) - - actions = [ - Openflow.Action.PopVlan.new(), - Openflow.Action.Output.new(state.port.number) - ] - - ins = Openflow.Instruction.ApplyActions.new(actions) - - options = [ - cookie: state.cookie, - table_id: 0, - priority: 50, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - test "Inject Packet(ARP)" do - for trusted <- @trusted_macs do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(trusted, :eth_src) - dhost = Openflow.Match.Field.codec(@mac, :eth_dst) - payload = <<0::size(16)-unit(8)>> - - packet = [ - ether(dhost: dhost, shost: shost, type: 0x8100), - {:"802.1q", 0, 0, 5, 0x0806}, - arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) - ] - - expect = [ - ether(dhost: dhost, shost: shost, type: 0x0806), - arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) - ] - - Pf.inject!(pid2, packet, payload) - assert_receive {@access_port1_sniffer, ^expect}, 3000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - end - - describe "associate:register_bootstrap_rule:" <> - "send_flow_rem," <> "dl_src={trusted}," <> "actions=drop" do - test "Install Flow", state do - for trusted <- @trusted_macs do - match = Openflow.Match.new(eth_src: trusted) - - options = [ - cookie: state.cookie, - table_id: 0, - priority: 29, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - end - - describe "associate:register_usernet_rule:" <> - "in_port={vlan_trunk_port},dl_vlan={vlan}," <> "actions=goto_table:1" do - setup [:flow_del_by_cookie] - - test "Install Flow", state do - match = Openflow.Match.new(in_port: state.vlan_trunk.number, vlan_vid: @user_vid) - ins = [Openflow.Instruction.GotoTable.new(1)] - - options = [ - cookie: state.cookie2, - table_id: 0, - priority: 50, - match: match, - instructions: ins - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "associate:register_usernet_rule:" <> - "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem, dl_src={mac},in_port={port_no}," <> - "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,goto_table:1" do - test "Install Flow", state do - match = Openflow.Match.new(in_port: state.port.number, eth_src: @mac) - - actions = [ - Openflow.Action.PushVlan.new(), - Openflow.Action.SetField.new({:vlan_vid, @user_vid}) - ] - - ins = [ - Openflow.Instruction.ApplyActions.new(actions), - Openflow.Instruction.GotoTable.new(1) - ] - - options = [ - cookie: state.cookie2, - table_id: 0, - priority: 20, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: ins - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "associate:register_usernet_rule:" <> - "vlan_vid=0x1000/0x1000,tcp,tcp_dst=443,nw_dst={auth_ipv4_address}," <> - "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vlan_trunk_port}" do - test "Install Flow", state do - match = - Openflow.Match.new( - vlan_vid: @vlan_present, - eth_type: 0x0800, - ip_proto: 6, - ipv4_dst: @auth_ipv4_address, - tcp_dst: 443 - ) - - actions = [ - Openflow.Action.PopVlan.new(), - Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(state.vlan_trunk.number) - ] - - ins = Openflow.Instruction.ApplyActions.new(actions) - - options = [ - cookie: state.cookie2, - table_id: 1, - priority: 30, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(TCP:443)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) - - payload = "" - - tcp_header = - tcp( - sport: 53_688, - dport: 443, - seqno: 1_488_352_223, - off: 10, - syn: 1, - win: 29_200, - opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> - ) - - ipv4_header = ipv4(saddr: @client_ip, daddr: @auth_ipv4_address, p: 6, len: 60) - tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) - ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) - packet = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] - expect = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] - Pf.inject!(pid1, packet, <>) - assert_receive {@vlan_trunk_port_sniffer, ^expect}, 3000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "associate:register_usernet_rule:" <> - "vlan_vid=0x1000/0x1000,tcp,tcp_dst=80,nw_dst={captive_ipv4_address}," <> - "strip_vlan,set_field:{sdl_vmac}->eth_dst,output:{vlan_trunk_port}" do - test "Install Flow", state do - match = - Openflow.Match.new( - vlan_vid: @vlan_present, - eth_type: 0x0800, - ip_proto: 6, - ipv4_dst: @captive_ipv4_address, - tcp_dst: 80 - ) - - actions = [ - Openflow.Action.PopVlan.new(), - Openflow.Action.SetField.new({:eth_dst, @sdl_vmac}), - Openflow.Action.Output.new(state.vlan_trunk.number) - ] - - ins = Openflow.Instruction.ApplyActions.new(actions) - - options = [ - cookie: state.cookie2, - table_id: 1, - priority: 30, - match: match, - instructions: [ins] - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(TCP:80)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) - - payload = "" - - tcp_header = - tcp( - sport: 53_688, - dport: 80, - seqno: 1_488_352_223, - off: 10, - syn: 1, - win: 29_200, - opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> - ) - - ipv4_header = ipv4(saddr: @client_ip, daddr: @captive_ipv4_address, p: 6, len: 60) - tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) - ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) - packet = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] - expect = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] - Pf.inject!(pid1, packet, <>) - assert_receive {@vlan_trunk_port_sniffer, ^expect}, 3000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "associate:register_usernet_rule:" <> - "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,dl_src={mac},in_port={vlan_trunk_port}," <> - "actions=drop" do - test "Install Flow", state do - match = Openflow.Match.new(in_port: state.vlan_trunk.number, eth_src: @mac) - - options = [ - cookie: state.cookie2, - table_id: 0, - priority: 19, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "associate:register_usernet_rule:" <> - "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,dl_src={mac}," <> - "actions=controller" do - test "Install Flow", state do - match = Openflow.Match.new(eth_src: @mac) - actions = [Openflow.Action.Output.new(:controller)] - ins = [Openflow.Instruction.ApplyActions.new(actions)] - - options = [ - cookie: state.cookie2, - table_id: 0, - priority: 18, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: ins - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "associate:register_usernet_rule:" <> - "idle_timeout={itimeout},hard_timeout={htimeout},send_flow_rem,vlan_vid=0x1000/0x1000,dl_dst={mac}," <> - "actions=strip_vlan,output:{port_no}" do - test "Install Flow", state do - match = Openflow.Match.new(vlan_vid: @vlan_present, eth_dst: @mac) - actions = [Openflow.Action.PopVlan.new(), Openflow.Action.Output.new(state.port.number)] - ins = [Openflow.Instruction.ApplyActions.new(actions)] - - options = [ - cookie: state.cookie2, - table_id: 1, - priority: 50, - flags: [:send_flow_rem], - idle_timeout: state.timeout, - hard_timeout: state.timeout, - match: match, - instructions: ins - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(ARP)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) - dhost = Openflow.Match.Field.codec(@mac, :eth_dst) - vlan_ether_header = ether(dhost: dhost, shost: shost, type: 0x8100) - ether_header = ether(dhost: dhost, shost: shost, type: 0x0806) - vlan_header = {:"802.1q", 0, 1, 123, 0x0806} - arp_header = arp(op: 2, sha: shost, tha: dhost, tip: @client_ip, sip: @gateway_ip) - packet = [vlan_ether_header, vlan_header, arp_header] - expect = [ether_header, arp_header] - Pf.inject!(pid2, packet) - assert_receive {@access_port1_sniffer, ^expect}, 1000 - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - describe "associate:register_usernet_rule:" <> - "dl_vlan={vlan},dl_dst={mcast}," <> - "actions=output:{vlan_trunk_port},strip_vlan,{outputs}" do - test "Install Flow", state do - match = Openflow.Match.new(vlan_vid: @user_vid, eth_dst: @mcast) - - actions = [ - Openflow.Action.Output.new(state.vlan_trunk.number), - Openflow.Action.PopVlan.new(), - Openflow.Action.Output.new(state.port.number), - Openflow.Action.Output.new(state.port2.number) - ] - - ins = [Openflow.Instruction.ApplyActions.new(actions)] - - options = [ - cookie: state.cookie2, - table_id: 1, - priority: 60, - match: match, - instructions: ins - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(ARP) from VLAN TRUNK" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@access_port2_sniffer) - - shost = Openflow.Match.Field.codec(@sdl_vmac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - vlan_ether_header = ether(dhost: dhost, shost: shost, type: 0x8100) - ether_header = ether(dhost: dhost, shost: shost, type: 0x0806) - vlan_header = {:"802.1q", 0, 1, 123, 0x0806} - arp_header = arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip) - packet = [vlan_ether_header, vlan_header, arp_header] - Pf.inject!(pid2, packet) - expect = [ether_header, arp_header] - assert_receive {@access_port1_sniffer, ^expect}, 3000 - assert_receive {@access_port2_sniffer, ^expect}, 3000 - Pf.stop(pid1) - Pf.stop(pid2) - Pf.stop(pid3) - end - - test "Inject Packet(ARP) from ACCESS" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - {:ok, pid3} = Pf.start_link(@access_port2_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@bcast, :eth_dst) - vlan_ether_header = ether(dhost: dhost, shost: shost, type: 0x8100) - ether_header = ether(dhost: dhost, shost: shost, type: 0x0806) - vlan_header = {:"802.1q", 0, 0, 123, 0x0806} - arp_header = arp(op: 1, sha: shost, tip: @client_ip, sip: @gateway_ip) - packet = [ether_header, arp_header] - Pf.inject!(pid1, packet) - - expects = [ - {@vlan_trunk_port_sniffer, [vlan_ether_header, vlan_header, arp_header]}, - {@access_port2_sniffer, [ether_header, arp_header]} - ] - - :ok = assert_receives(expects) - Pf.stop(pid1) - Pf.stop(pid2) - Pf.stop(pid3) - end - end - - describe "associate:register_usernet_rule:" <> - "dl_vlan={vlan}," <> "actions=output:{vlan_trunk_port},strip_vlan,{outputs}" do - test "Install Flow", state do - match = Openflow.Match.new(vlan_vid: @user_vid) - - actions = [ - Openflow.Action.Output.new(state.vlan_trunk.number), - Openflow.Action.PopVlan.new(), - Openflow.Action.Output.new(state.port.number) - ] - - ins = [Openflow.Instruction.ApplyActions.new(actions)] - - options = [ - cookie: state.cookie2, - table_id: 1, - priority: 20, - match: match, - instructions: ins - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "switch:register_farm_nat_rule:" <> - "dl_src={src_mac},arp,arp_spa={src_ip},arp_tpa={dst_ip}," <> - "actions=set_field:{farm_vmac}->eth_dst,set_field:{src_fip}->arp_spa," <> - "set_field:{dst_fip}->arp_tpa,output:{vlan_trunk_port}" do - test "Install Flow", state do - match = - Openflow.Match.new( - eth_src: @mac, - eth_type: 0x0806, - arp_spa: @client_ip, - arp_tpa: @gateway_ip - ) - - actions = [ - Openflow.Action.SetField.new({:eth_dst, @farm_gw_mac}), - Openflow.Action.SetField.new({:arp_spa, @client_farm_ip}), - Openflow.Action.SetField.new({:arp_tpa, @farm_gw_ip}), - # Following is not work properly in "SwitchNode" configuration. - # Openflow.Action.PushVlan.new, - # Openflow.Action.SetField.new({:vlan_vid, @user_vid}), - Openflow.Action.Output.new(state.vlan_trunk.number) - ] - - ins = [Openflow.Instruction.ApplyActions.new(actions)] - - options = [ - cookie: 0x3000000000000001, - table_id: 1, - priority: 30, - idle_timeout: 60, - match: match, - instructions: ins - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - end - - describe "switch:register_farm_nat_rule:" <> - "dl_src={src_mac},ip,nw_src={src_ip},nw_dst={dst_ip}," <> - "actions=push_vlan:0x8100,set_field:{vlan}->vlan_vid,set_field:{farm_vmac}->eth_dst," <> - "set_field:{src_fip}->nw_src,output:{vlan_trunk_port}" do - test "Install Flow", state do - match = - Openflow.Match.new( - eth_src: @mac, - eth_type: 0x0800, - ipv4_src: @client_ip, - ipv4_dst: @internet - ) - - actions = [ - Openflow.Action.SetField.new({:eth_dst, @farm_gw_mac}), - # Following is not work properly in "SwitchNode" configuration. - # Openflow.Action.PushVlan.new, - # Openflow.Action.SetField.new({:vlan_vid, @user_vid}), - Openflow.Action.SetField.new({:ipv4_src, @client_farm_ip}), - Openflow.Action.Output.new(state.vlan_trunk.number) - ] - - ins = [Openflow.Instruction.ApplyActions.new(actions)] - - options = [ - cookie: 0x3000000000000001, - table_id: 1, - priority: 30, - idle_timeout: 60, - match: match, - instructions: ins - ] - - :ok = GenServer.cast(Flay, {:flow_install, options, self()}) - :timer.sleep(1000) - refute_receive %Openflow.ErrorMsg{}, 1000 - end - - test "Inject Packet(TCP:80)" do - {:ok, pid1} = Pf.start_link(@access_port1_sniffer) - {:ok, pid2} = Pf.start_link(@vlan_trunk_port_sniffer) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@sdl_vmac, :eth_dst) - - payload = "" - - tcp_header = - tcp( - sport: 53_688, - dport: 80, - seqno: 1_488_352_223, - off: 10, - syn: 1, - win: 29_200, - opt: <<2, 4, 5, 180, 4, 2, 8, 10, 156, 30, 232, 154, 0, 0, 0, 0, 1, 3, 3, 7>> - ) - - ipv4_header = ipv4(saddr: @client_ip, daddr: @internet, p: 6, len: 60) - tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) - ether_header = ether(dhost: dhost, shost: shost, type: 0x0800) - packet = [ether_header, ipv4_header, tcp(tcp_header, sum: tcp_sum)] - Pf.inject!(pid1, packet, <>) - - shost = Openflow.Match.Field.codec(@mac, :eth_src) - dhost = Openflow.Match.Field.codec(@farm_gw_mac, :eth_dst) - ether_header = ether(dhost: dhost, shost: shost, type: 0x8100) - vlan_header = {:"802.1q", 0, 0, 123, 0x0800} - ipv4_header = ipv4(saddr: @client_farm_ip, daddr: @internet, p: 6, len: 60) - tcp_sum = :pkt.makesum([ipv4_header, tcp_header, payload]) - - expect = [ - ether_header, - vlan_header, - ipv4(ipv4_header, sum: 1544), - tcp(tcp_header, sum: tcp_sum) - ] - - assert_receives([{@vlan_trunk_port_sniffer, expect}], 5000) - Pf.stop(pid1) - Pf.stop(pid2) - end - end - - # private functions - - defp assert_receives(expects, timeout \\ 1000) do - assert_receives(expects, length(expects), timeout) - end - - defp assert_receives(_expects, 0, _timeout), do: :ok - - defp assert_receives(expects, count, timeout) do - receive do - message -> - assert(message in expects) - assert_receives(expects, count - 1, timeout) - after - timeout -> - flunk("Timeout") - end - end - - defp setup_applications do - Application.put_env(:tres, :protocol, :tcp, persistent: true) - Application.put_env(:tres, :port, @listen_port, persistent: true) - Application.put_env(:tres, :mac_connections, 1, persistent: true) - Application.put_env(:tres, :mac_acceptors, 1, persistent: true) - Application.put_env(:tres, :callback_module, Flay, persistent: true) - Application.put_env(:tres, :callback_args, self(), persistent: true) - Application.start(:binpp) - Application.start(:pkt) - Application.start(:epcap) - Application.start(:ranch) - Application.start(:eovsdb) - Application.start(:tres) - end - - defp wait_for_connected do - case Process.whereis(Flay) do - nil -> - wait_for_connected() - - pid when is_pid(pid) -> - :ok - end - end - - defp flow_del_by_cookie(context) do - :ok = GenServer.cast(Flay, {:flow_del, context.cookie}) - :timer.sleep(3000) - end - - defp get_ports_desc do - port_desc = GenServer.call(Flay, :port_desc_stats, 5000) - port_desc.ports - end -end diff --git a/test/pf.ex b/test/pf.ex deleted file mode 100644 index e96109c..0000000 --- a/test/pf.ex +++ /dev/null @@ -1,77 +0,0 @@ -defmodule Pf do - use GenServer - - import Record - # Extract Erlang record for msantos/pkt - for {name, schema} <- extract_all(from_lib: "pkt/include/pkt.hrl") do - defrecord(name, schema) - end - - defmodule State do - defstruct ifname: nil, - pcap_ref: nil, - tester_pid: nil - end - - def inject!(pid, packet, payload \\ "") do - GenServer.cast(pid, {:inject, {packet, payload}}) - end - - def stop(pid) do - GenServer.cast(pid, :stop) - end - - def start_link(ifname) do - ifname = String.to_charlist(ifname) - GenServer.start_link(__MODULE__, [ifname, self()]) - end - - def init([ifname, tester_pid]) do - {:ok, init_pf(ifname, tester_pid)} - end - - def handle_cast({:inject, {headers, payload}}, state) do - headers_bin = - for header <- headers do - case header do - ether() -> :pkt.ether(header) - {:"802.1q", _, _, _, _} = vlan -> :pkt_802_1q.codec(vlan) - arp() -> :pkt.arp(header) - ipv4() -> :pkt.ipv4(header) - lldp() -> :pkt.lldp(header) - udp() -> :pkt.udp(header) - tcp() -> :pkt.tcp(header) - end - end - - binary = Enum.join(headers_bin, "") - :epcap.send(state.pcap_ref, <>) - {:noreply, state} - end - - def handle_cast(:stop, state) do - {:stop, :normal, state} - end - - def handle_cast(_req, state) do - {:noreply, state} - end - - def handle_info({:packet, _dlt, _time, _len, data}, state) do - packet = :pkt.decapsulate(data) - packet_len = length(packet) - send(state.tester_pid, {to_string(state.ifname), Enum.take(packet, packet_len - 1)}) - {:noreply, state} - end - - def handle_info(_info, state) do - {:noreply, state} - end - - # private functions - - defp init_pf(ifname, tester_pid) do - {:ok, epcap_pid} = :epcap.start_link(interface: ifname, promiscuous: true, inject: true) - %State{pcap_ref: epcap_pid, ifname: ifname, tester_pid: tester_pid} - end -end From ce56d2b7befa444b808e005874a5985706d80d59 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 9 Feb 2018 00:49:57 +0900 Subject: [PATCH 32/50] Add an example for leader election based multiple controller --- examples/leader_example/.formatter.exs | 4 + examples/leader_example/.gitignore | 24 ++++++ examples/leader_example/README.md | 21 +++++ examples/leader_example/config/config.exs | 21 +++++ examples/leader_example/config/dev.exs | 3 + examples/leader_example/config/node1.exs | 9 +++ examples/leader_example/config/node2.exs | 9 +++ .../lib/leader_example/application.ex | 20 +++++ .../lib/leader_example/leader.ex | 77 +++++++++++++++++++ examples/leader_example/mix.exs | 25 ++++++ examples/leader_example/mix.lock | 10 +++ .../test/leader_example_test.exs | 8 ++ examples/leader_example/test/test_helper.exs | 1 + lib/tres/message_helper.ex | 10 +++ 14 files changed, 242 insertions(+) create mode 100644 examples/leader_example/.formatter.exs create mode 100644 examples/leader_example/.gitignore create mode 100644 examples/leader_example/README.md create mode 100644 examples/leader_example/config/config.exs create mode 100644 examples/leader_example/config/dev.exs create mode 100644 examples/leader_example/config/node1.exs create mode 100644 examples/leader_example/config/node2.exs create mode 100644 examples/leader_example/lib/leader_example/application.ex create mode 100644 examples/leader_example/lib/leader_example/leader.ex create mode 100644 examples/leader_example/mix.exs create mode 100644 examples/leader_example/mix.lock create mode 100644 examples/leader_example/test/leader_example_test.exs create mode 100644 examples/leader_example/test/test_helper.exs diff --git a/examples/leader_example/.formatter.exs b/examples/leader_example/.formatter.exs new file mode 100644 index 0000000..525446d --- /dev/null +++ b/examples/leader_example/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/examples/leader_example/.gitignore b/examples/leader_example/.gitignore new file mode 100644 index 0000000..28bf3b2 --- /dev/null +++ b/examples/leader_example/.gitignore @@ -0,0 +1,24 @@ +# 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 + +# Ignore package tarball (built via "mix hex.build"). +leader_example-*.tar + diff --git a/examples/leader_example/README.md b/examples/leader_example/README.md new file mode 100644 index 0000000..dfed475 --- /dev/null +++ b/examples/leader_example/README.md @@ -0,0 +1,21 @@ +# LeaderExample + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `leader_example` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:leader_example, "~> 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/leader_example](https://hexdocs.pm/leader_example). + diff --git a/examples/leader_example/config/config.exs b/examples/leader_example/config/config.exs new file mode 100644 index 0000000..ce42502 --- /dev/null +++ b/examples/leader_example/config/config.exs @@ -0,0 +1,21 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config + +config :libcluster, + topologies: [ + gossip_example: [ + strategy: Cluster.Strategy.Gossip, + config: [ + port: 45_892, + if_addr: {0, 0, 0, 0}, + multicast_addr: {230, 1, 1, 251}, + multicast_ttl: 1]]] + +config :logger, + level: :debug, + format: "$date $time [$level] $metadata$message\n", + metadata: [:application], + handle_otp_reports: true + +import_config "#{Mix.env}.exs" diff --git a/examples/leader_example/config/dev.exs b/examples/leader_example/config/dev.exs new file mode 100644 index 0000000..0d0917d --- /dev/null +++ b/examples/leader_example/config/dev.exs @@ -0,0 +1,3 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +use Mix.Config diff --git a/examples/leader_example/config/node1.exs b/examples/leader_example/config/node1.exs new file mode 100644 index 0000000..52a9823 --- /dev/null +++ b/examples/leader_example/config/node1.exs @@ -0,0 +1,9 @@ +use Mix.Config + +config :tres, + protocol: :tcp, + port: 6653, + max_connections: 10, + num_acceptors: 10, + callback_module: LeaderExample.Leader, + callback_args: [] diff --git a/examples/leader_example/config/node2.exs b/examples/leader_example/config/node2.exs new file mode 100644 index 0000000..bbc9aef --- /dev/null +++ b/examples/leader_example/config/node2.exs @@ -0,0 +1,9 @@ +use Mix.Config + +config :tres, + protocol: :tcp, + port: 6633, + max_connections: 10, + num_acceptors: 10, + callback_module: LeaderExample.Leader, + callback_args: [] diff --git a/examples/leader_example/lib/leader_example/application.ex b/examples/leader_example/lib/leader_example/application.ex new file mode 100644 index 0000000..89c4ca6 --- /dev/null +++ b/examples/leader_example/lib/leader_example/application.ex @@ -0,0 +1,20 @@ +defmodule LeaderExample.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + def start(_type, _args) do + # List all child processes to be supervised + children = [ + # Starts a worker by calling: LeaderExample.Worker.start_link(arg) + # {LeaderExample.Worker, arg}, + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: LeaderExample.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/examples/leader_example/lib/leader_example/leader.ex b/examples/leader_example/lib/leader_example/leader.ex new file mode 100644 index 0000000..9fb0e69 --- /dev/null +++ b/examples/leader_example/lib/leader_example/leader.ex @@ -0,0 +1,77 @@ +defmodule LeaderExample.Leader do + use Tres.Controller + + @behaviour :locks_leader + + require Logger + + def start_link(datapath_id, args) do + :locks_leader.start_link(__MODULE__, [datapath_id, args], []) + end + + def init([{datapath_id, _aux_id}, _args]) do + :ok = Logger.debug("Switch Ready: datapath_id: #{inspect(datapath_id)}") + :ok = role_request(datapath_id, role: :slave, generation_id: 0) + {:ok, %{am_leader: false, gen_id: 0, datapath_id: datapath_id}} + end + + def elected(state, _info, _cand) do + :ok = Logger.info("Elected: #{inspect(Node.self)} for #{state.datapath_id} gen_id: #{state.gen_id}") + :ok = role_request(state.datapath_id, role: :master, generation_id: state.gen_id) + {:ok, {:elected, {Node.self(), state.gen_id + 1}}, %{state|am_leader: true}} + end + + def surrendered(%{am_leader: true} = state, {:elected, {_node, next_gen_id}}, _info) do + :ok = Logger.info("network split possible detected") + :ok = role_request(state.datapath_id, role: :slave, generation_id: state.gen_id) + {:ok, %{state|am_leader: false, gen_id: next_gen_id}} + end + + def surrendered(state, {:elected, {node, next_gen_id}}, _info) do + :ok = Logger.info("Surrendered: elected node is #{node} for #{state.datapath_id}") + :ok = role_request(state.datapath_id, role: :slave, generation_id: state.gen_id) + {:ok, %{state|am_leader: false, gen_id: next_gen_id}} + end + + def handle_DOWN(_pid, state, _info) do + :ok = Logger.warn("DOWN detected") + {:ok, state} + end + + def handle_leader_call(msg, from, state, _info) do + :ok = Logger.info("leader call with: #{inspect(msg)} from: #{inspect(from)} on #{Node.self()}") + {:reply, :ok, state} + end + + def handle_leader_cast(_msg, state, _info) do + {:ok, state} + end + + def from_leader(_from_leader, state, _info) do + {:ok, state} + end + + def handle_call(_msg, _from, state, _info) do + {:reply, :ok, state} + end + + def handle_cast(_msg, state, _info) do + {:noreply, state} + end + + def handle_info(%Role.Reply{datapath_id: dpid} = role, %{datapath_id: dpid} = state, _info) do + :ok = Logger.info("#{Node.self()} is on #{role.role} for #{dpid}") + {:noreply, state} + end + def handle_info(_msg, state, _info) do + {:noreply, state} + end + + def code_change(_from_vsn, state, _info, _extra) do + {:ok, state} + end + + def terminate(_reason, _state) do + :ok + end +end diff --git a/examples/leader_example/mix.exs b/examples/leader_example/mix.exs new file mode 100644 index 0000000..864f603 --- /dev/null +++ b/examples/leader_example/mix.exs @@ -0,0 +1,25 @@ +defmodule LeaderExample.MixProject do + use Mix.Project + + def project do + [ + app: :leader_example, + version: "0.1.0", + elixir: "~> 1.6", + deps: deps() + ] + end + + def application do + [ + extra_applications: [:logger, :libcluster, :locks, :tres], + mod: {LeaderExample.Application, []} + ] + end + + defp deps do + [{:locks, "~> 0.2.0"}, + {:libcluster, "~> 2.3.0"}, + {:tres, path: "../../../tres"}] + end +end diff --git a/examples/leader_example/mix.lock b/examples/leader_example/mix.lock new file mode 100644 index 0000000..6318ab4 --- /dev/null +++ b/examples/leader_example/mix.lock @@ -0,0 +1,10 @@ +%{ + "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, + "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, + "libcluster": {:hex, :libcluster, "2.3.0", "d2fb8e8b2054a4a0c7faf7ced7c41dd13f7b49cce689590d37ecf30098e1b344", [:mix], [{:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "locks": {:hex, :locks, "0.2.0", "c8a5db52bdbfacfcc979b38920adb2ab8909fa7fb36015ddff6b753d1c950b92", [:rebar3], [{:plain_fsm, "1.4.1", [hex: :plain_fsm, repo: "hexpm", optional: false]}], "hexpm"}, + "plain_fsm": {:hex, :plain_fsm, "1.4.1", "47e9bf6ac9322fc7586fb6df8de7198391e93764571c75165f2c45b27acde1d0", [:rebar3], [], "hexpm"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, + "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"}, + "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}, +} diff --git a/examples/leader_example/test/leader_example_test.exs b/examples/leader_example/test/leader_example_test.exs new file mode 100644 index 0000000..e7d1d8d --- /dev/null +++ b/examples/leader_example/test/leader_example_test.exs @@ -0,0 +1,8 @@ +defmodule LeaderExampleTest do + use ExUnit.Case + doctest LeaderExample + + test "greets the world" do + assert LeaderExample.hello() == :world + end +end diff --git a/examples/leader_example/test/test_helper.exs b/examples/leader_example/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/examples/leader_example/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index 87d6a00..41c571b 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -93,6 +93,16 @@ defmodule Tres.MessageHelper do send_message(group_mod, datapath_id) end + + defp role_request(datapath_id, options) do + role_request = + Openflow.Role.Request.new( + role: options[:role] || :nochange, + generation_id: options[:generation_id] || 0 + ) + + send_message(role_request, datapath_id) + end end end end From c173d8a922c7d6fa18ccbd66d9cdc3a2aa74c0f1 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 9 Feb 2018 00:52:22 +0900 Subject: [PATCH 33/50] Update README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 79d0845..e48df98 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,9 @@ To use `Tres.Controller` with your code, set the handler callback_module to your This module must implement the `Module.start_link/2` that returns `on_start`. Set the callback_args to the terms you want pass to the `start_link/2` callback function. + +## Examples + +- learning-switch: Simple Layer2 switch +- leader-example: Simple Leader election based multiple controller + From fa2c0c5bbccb77a27451d2dd28ca7c52cec291f6 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sat, 17 Feb 2018 00:14:53 +0900 Subject: [PATCH 34/50] role_request/2 -> send_role_request/2 --- examples/leader_example/lib/leader_example/leader.ex | 8 ++++---- lib/tres/message_helper.ex | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/leader_example/lib/leader_example/leader.ex b/examples/leader_example/lib/leader_example/leader.ex index 9fb0e69..f895dc1 100644 --- a/examples/leader_example/lib/leader_example/leader.ex +++ b/examples/leader_example/lib/leader_example/leader.ex @@ -11,25 +11,25 @@ defmodule LeaderExample.Leader do def init([{datapath_id, _aux_id}, _args]) do :ok = Logger.debug("Switch Ready: datapath_id: #{inspect(datapath_id)}") - :ok = role_request(datapath_id, role: :slave, generation_id: 0) + :ok = send_role_request(datapath_id, role: :slave, generation_id: 0) {:ok, %{am_leader: false, gen_id: 0, datapath_id: datapath_id}} end def elected(state, _info, _cand) do :ok = Logger.info("Elected: #{inspect(Node.self)} for #{state.datapath_id} gen_id: #{state.gen_id}") - :ok = role_request(state.datapath_id, role: :master, generation_id: state.gen_id) + :ok = send_role_request(state.datapath_id, role: :master, generation_id: state.gen_id) {:ok, {:elected, {Node.self(), state.gen_id + 1}}, %{state|am_leader: true}} end def surrendered(%{am_leader: true} = state, {:elected, {_node, next_gen_id}}, _info) do :ok = Logger.info("network split possible detected") - :ok = role_request(state.datapath_id, role: :slave, generation_id: state.gen_id) + :ok = send_role_request(state.datapath_id, role: :slave, generation_id: state.gen_id) {:ok, %{state|am_leader: false, gen_id: next_gen_id}} end def surrendered(state, {:elected, {node, next_gen_id}}, _info) do :ok = Logger.info("Surrendered: elected node is #{node} for #{state.datapath_id}") - :ok = role_request(state.datapath_id, role: :slave, generation_id: state.gen_id) + :ok = send_role_request(state.datapath_id, role: :slave, generation_id: state.gen_id) {:ok, %{state|am_leader: false, gen_id: next_gen_id}} end diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index 41c571b..0c7946e 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -94,7 +94,7 @@ defmodule Tres.MessageHelper do send_message(group_mod, datapath_id) end - defp role_request(datapath_id, options) do + defp send_role_request(datapath_id, options) do role_request = Openflow.Role.Request.new( role: options[:role] || :nochange, From 2fb0be0348ac14738033a269ca7d719926126a0b Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sat, 17 Feb 2018 22:31:17 +0900 Subject: [PATCH 35/50] Update README --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e48df98..dbf190b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -# Tres - a Elixir OpenFlow development platform +# Tres - an Elixir OpenFlow development platform ## Overview Tres is a framework and set of helper libraries to develop OpenFlow controllers in Elixir. -## Installation +## How to build your own controller application + +### Installation ```elixir def deps do @@ -14,7 +16,7 @@ def deps do end ``` -## callbacks +### callbacks ```elixir config :tres, @@ -27,8 +29,49 @@ This module must implement the `Module.start_link/2` that returns `on_start`. Set the callback_args to the terms you want pass to the `start_link/2` callback function. +``` +% cat lib/sample.ex +defmodule Sample do + use GenServer + use Tres.Controller + + def start_link(datapath_id, _start_args) do + GenServer.start_link(__MODULE__, [datapath_id]) + end + + def init([datapath_id]) do + # As an example create a flow to receive all packet_ins from port 1 + redirect_action = Output.new(:controller) + apply_ins = ApplyActions.new(redirect_action) + match = Match.new(in_port: 1) + :ok = send_flow_mod_add(datapath_id, + priority: 1, + match: match, + instructions: [apply_ins]) + {:ok, datapath_id} + end + + def handle_info(%PacketIn{} = packet_in, datapath_id) do + # print the packet_in message struct + IO.inspect(packet_in) + {:noreply, datapath_id} + end +end +``` + +## To run the controller + +```bash +$ iex -S mix +``` + ## Examples - learning-switch: Simple Layer2 switch -- leader-example: Simple Leader election based multiple controller +- leader-example: Simple election based multiple controller using Ulf Wiger's Locks Leader +License +------- +Tres is released under the Apache license Version 2.0: + +* https://www.apache.org/licenses/LICENSE-2.0 From 76b64a45c035d61324f3f424a37c58c1ac54890b Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sat, 17 Feb 2018 22:58:48 +0900 Subject: [PATCH 36/50] Add architecture image --- images/tres_inside.png | Bin 0 -> 111664 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/tres_inside.png diff --git a/images/tres_inside.png b/images/tres_inside.png new file mode 100644 index 0000000000000000000000000000000000000000..dc58075cfa24703fd4c179cee8f5d34e270c63c1 GIT binary patch literal 111664 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%0DOO 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 Date: Sun, 18 Feb 2018 00:32:37 +0900 Subject: [PATCH 37/50] Licensed --- LICENSE | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ea5a6eb --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright [2018] [Eishun Kondoh] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. From 51729542f3b33814e258ca301c47891fca2867da Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sun, 25 Feb 2018 23:44:58 +0900 Subject: [PATCH 38/50] Add patch_panel example --- examples/patch_panel/.formatter.exs | 4 + examples/patch_panel/.gitignore | 24 ++++++ examples/patch_panel/README.md | 16 ++++ examples/patch_panel/config/config.exs | 7 ++ examples/patch_panel/lib/patch_panel.ex | 18 +++++ .../lib/patch_panel/application.ex | 20 +++++ .../lib/patch_panel/openflow/controller.ex | 81 +++++++++++++++++++ .../lib/patch_panel/openflow/registry.ex | 16 ++++ examples/patch_panel/mix.exs | 26 ++++++ examples/patch_panel/mix.lock | 6 ++ .../patch_panel/test/patch_panel_test.exs | 8 ++ examples/patch_panel/test/test_helper.exs | 1 + 12 files changed, 227 insertions(+) create mode 100644 examples/patch_panel/.formatter.exs create mode 100644 examples/patch_panel/.gitignore create mode 100644 examples/patch_panel/README.md create mode 100644 examples/patch_panel/config/config.exs create mode 100644 examples/patch_panel/lib/patch_panel.ex create mode 100644 examples/patch_panel/lib/patch_panel/application.ex create mode 100644 examples/patch_panel/lib/patch_panel/openflow/controller.ex create mode 100644 examples/patch_panel/lib/patch_panel/openflow/registry.ex create mode 100644 examples/patch_panel/mix.exs create mode 100644 examples/patch_panel/mix.lock create mode 100644 examples/patch_panel/test/patch_panel_test.exs create mode 100644 examples/patch_panel/test/test_helper.exs diff --git a/examples/patch_panel/.formatter.exs b/examples/patch_panel/.formatter.exs new file mode 100644 index 0000000..525446d --- /dev/null +++ b/examples/patch_panel/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/examples/patch_panel/.gitignore b/examples/patch_panel/.gitignore new file mode 100644 index 0000000..b22822d --- /dev/null +++ b/examples/patch_panel/.gitignore @@ -0,0 +1,24 @@ +# 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 + +# Ignore package tarball (built via "mix hex.build"). +patch_panel-*.tar + diff --git a/examples/patch_panel/README.md b/examples/patch_panel/README.md new file mode 100644 index 0000000..ce2181c --- /dev/null +++ b/examples/patch_panel/README.md @@ -0,0 +1,16 @@ +# PatchPanel + +openflow controller that emulates a software patch panel + +## prerequisites + + - Erlang 20 or higher + - Elixir 1.6.1 or higher + - OpenFlow switch supports version 1.3 + +```iex +# To add a patch link +iex > :ok = PatchPanel.Openflow.Controller.create_patch("de780562fb45, 1, 2) +# To delete a patch link +iex > :ok = PatchPanel.Openflow.Controller.delete_patch("de780562fb45, 1, 2) +``` diff --git a/examples/patch_panel/config/config.exs b/examples/patch_panel/config/config.exs new file mode 100644 index 0000000..ce4f069 --- /dev/null +++ b/examples/patch_panel/config/config.exs @@ -0,0 +1,7 @@ +# 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: PatchPanel.Openflow.Controller, + callback_args: [] diff --git a/examples/patch_panel/lib/patch_panel.ex b/examples/patch_panel/lib/patch_panel.ex new file mode 100644 index 0000000..f6e0dbe --- /dev/null +++ b/examples/patch_panel/lib/patch_panel.ex @@ -0,0 +1,18 @@ +defmodule PatchPanel do + @moduledoc """ + Documentation for PatchPanel. + """ + + @doc """ + Hello world. + + ## Examples + + iex> PatchPanel.hello + :world + + """ + def hello do + :world + end +end diff --git a/examples/patch_panel/lib/patch_panel/application.ex b/examples/patch_panel/lib/patch_panel/application.ex new file mode 100644 index 0000000..b156391 --- /dev/null +++ b/examples/patch_panel/lib/patch_panel/application.ex @@ -0,0 +1,20 @@ +defmodule PatchPanel.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + + use Application + + alias PatchPanel.Openflow + + def start(_type, _args) do + import Supervisor.Spec + + children = [ + worker(Registry, [[keys: :unique, name: Openflow.Registry]], id: Openflow.Registry), + ] + + opts = [strategy: :one_for_one, name: PatchPanel.Supervisor] + Supervisor.start_link(children, opts) + end +end diff --git a/examples/patch_panel/lib/patch_panel/openflow/controller.ex b/examples/patch_panel/lib/patch_panel/openflow/controller.ex new file mode 100644 index 0000000..bbecf04 --- /dev/null +++ b/examples/patch_panel/lib/patch_panel/openflow/controller.ex @@ -0,0 +1,81 @@ +defmodule PatchPanel.Openflow.Controller do + use GenServer + use Tres.Controller + + import Logger + + defmodule State do + defstruct [:datapath_id] + end + + def create_patch(datapath_id, port_a, port_b) do + case PatchPanel.Openflow.Registry.lookup_pid(datapath_id) do + nil -> + {:error, :not_found} + pid when is_pid(pid) -> + GenServer.call(pid, {:create, port_a, port_b}) + end + end + + def delete_patch(datapath_id, port_a, port_b) do + case PatchPanel.Openflow.Registry.lookup_pid(datapath_id) do + nil -> + {:error, :not_found} + pid when is_pid(pid) -> + GenServer.call(pid, {:delete, port_a, port_b}) + end + end + + def start_link({datapath_id, _aux_id}, _start_args) do + GenServer.start_link(__MODULE__, [datapath_id]) + end + + def init([datapath_id]) do + :ok = info("Switch Connected: datapath_id = #{datapath_id}") + {:ok, _} = PatchPanel.Openflow.Registry.register(datapath_id) + {:ok, %State{datapath_id: datapath_id}} + end + + def handle_call({:create, port_a, port_b}, _from, %State{datapath_id: datapath_id} = state) do + :ok = add_flow_entries(datapath_id, port_a, port_b) + {:reply, :ok, state} + end + def handle_call({:delete, port_a, port_b}, _from, %State{datapath_id: datapath_id} = state) do + :ok = del_flow_entries(datapath_id, port_a, port_b) + {:reply, :ok, state} + end + + def handle_info({:switch_disconnected, reason}, %State{datapath_id: datapath_id} = state) do + :ok = warn("#{datapath_id} disconnected: reason = #{inspect(reason)}") + :ok = PatchPanel.Openflow.Registry.unregister(datapath_id) + {:stop, :normal, state} + end + def handle_info(_info, state) do + {:noreply, state} + end + + def terminate(reason, state) do + {reason, state} + end + + # private functions + + defp add_flow_entries(datapath_id, port_a, port_b) do + :ok = send_flow_mod_add( + datapath_id, + match: Match.new(in_port: port_a), + instructions: [ApplyActions.new(Output.new(port_b))] + ) + + :ok = send_flow_mod_add( + datapath_id, + match: Match.new(in_port: port_b), + instructions: [ApplyActions.new(Output.new(port_a))] + ) + end + + defp del_flow_entries(datapath_id, port_a, port_b) do + :ok = send_flow_mod_delete(datapath_id, match: Match.new(in_port: port_a)) + :ok = send_flow_mod_delete(datapath_id, match: Match.new(in_port: port_b)) + end +end diff --git a/examples/patch_panel/lib/patch_panel/openflow/registry.ex b/examples/patch_panel/lib/patch_panel/openflow/registry.ex new file mode 100644 index 0000000..a24ba0b --- /dev/null +++ b/examples/patch_panel/lib/patch_panel/openflow/registry.ex @@ -0,0 +1,16 @@ +defmodule PatchPanel.Openflow.Registry do + def register(datapath_id) do + {:ok, _} = Registry.register(__MODULE__, datapath_id, []) + end + + def unregister(datapath_id) do + :ok = Registry.unregister(__MODULE__, datapath_id) + end + + def lookup_pid(datapath_id) do + case Registry.lookup(__MODULE__, datapath_id) do + [{pid, _}] -> pid + [] -> nil + end + end +end diff --git a/examples/patch_panel/mix.exs b/examples/patch_panel/mix.exs new file mode 100644 index 0000000..91b4af6 --- /dev/null +++ b/examples/patch_panel/mix.exs @@ -0,0 +1,26 @@ +defmodule PatchPanel.MixProject do + use Mix.Project + + def project do + [ + app: :patch_panel, + version: "0.1.0", + elixir: "~> 1.6", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger], + mod: {PatchPanel.Application, []} + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [{:tres, path: "../../../tres"}] + end +end diff --git a/examples/patch_panel/mix.lock b/examples/patch_panel/mix.lock new file mode 100644 index 0000000..b51b792 --- /dev/null +++ b/examples/patch_panel/mix.lock @@ -0,0 +1,6 @@ +%{ + "eovsdb": {:git, "https://github.com/shun159/eovsdb.git", "1ff1572708d72fd25631c681f2102407903252a3", [branch: "master"]}, + "jsone": {:git, "https://github.com/sile/jsone.git", "eecc9666c7165e1870b78a7a762549ae8d1c391b", [tag: "1.2.1"]}, + "ranch": {:hex, :ranch, "1.4.0", "10272f95da79340fa7e8774ba7930b901713d272905d0012b06ca6d994f8826b", [:rebar3], [], "hexpm"}, + "uuid": {:git, "https://github.com/avtobiff/erlang-uuid.git", "585c2474afb4a597ae8c8bf6d21e5a9c73f18e0b", [tag: "v0.5.0"]}, +} diff --git a/examples/patch_panel/test/patch_panel_test.exs b/examples/patch_panel/test/patch_panel_test.exs new file mode 100644 index 0000000..d6f8a89 --- /dev/null +++ b/examples/patch_panel/test/patch_panel_test.exs @@ -0,0 +1,8 @@ +defmodule PatchPanelTest do + use ExUnit.Case + doctest PatchPanel + + test "greets the world" do + assert PatchPanel.hello() == :world + end +end diff --git a/examples/patch_panel/test/test_helper.exs b/examples/patch_panel/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/examples/patch_panel/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() From 442fe2eaa46a925255ce5211e4bff7f0f19cadcc Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sun, 25 Feb 2018 23:45:51 +0900 Subject: [PATCH 39/50] Add patch_panel example --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dbf190b..69efd9b 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ $ iex -S mix - learning-switch: Simple Layer2 switch - leader-example: Simple election based multiple controller using Ulf Wiger's Locks Leader +- patch\_panel: inteligent patch\_panel example License ------- From 112e0ce4b5d467984c7a2b3f73cd6714e29cda0a Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sat, 17 Mar 2018 22:43:58 +0900 Subject: [PATCH 40/50] Formatted and fix minor fixes --- lib/openflow/enums.ex | 8 +++++--- lib/tres/controller.ex | 9 +++++---- lib/tres/secure_channel.ex | 3 +++ lib/tres/switch_registry.ex | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex index 7ea33b5..58cf0a0 100644 --- a/lib/openflow/enums.ex +++ b/lib/openflow/enums.ex @@ -993,20 +993,22 @@ defmodule Openflow.Enums do 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 + 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) + :throw, _ -> unquote(key) end end + end + for {_key, value} <- enum_def do def to_atom(unquote(value), unquote(String.to_atom(enum_name))) do try do unquote(to_atom_fn_name)(unquote(value)) catch - _ -> unquote(value) + :throw, _ -> unquote(value) end end end diff --git a/lib/tres/controller.ex b/lib/tres/controller.ex index 15cdaa1..92d962e 100644 --- a/lib/tres/controller.ex +++ b/lib/tres/controller.ex @@ -1,10 +1,11 @@ defmodule Tres.Controller do def controller_helpers do quote do - import Tres.SwitchRegistry, only: [ - send_message: 2, - get_current_xid: 1 - ] + import Tres.SwitchRegistry, + only: [ + send_message: 2, + get_current_xid: 1 + ] use Tres.Messages use Tres.MessageHelper diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index d0c028b..80e41c0 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -124,10 +124,12 @@ defmodule Tres.SecureChannel do defp init_handler(state_data) do %State{datapath_id: dpid, aux_id: aux_id} = state_data + case MessageHandlerSup.start_child({dpid, aux_id}) do {:ok, pid} -> ref = Process.monitor(pid) %{state_data | handler_pid: pid, handler_ref: ref} + {:error, reason} -> {:stop, reason} end @@ -210,6 +212,7 @@ defmodule Tres.SecureChannel do %State{} = new_state_data -> start_periodic_idle_check() {:keep_state, new_state_data} + {:stop, reason} -> close_connection({:handler_down, reason}, state_data) end diff --git a/lib/tres/switch_registry.ex b/lib/tres/switch_registry.ex index 7fafd99..e901fe5 100644 --- a/lib/tres/switch_registry.ex +++ b/lib/tres/switch_registry.ex @@ -27,7 +27,7 @@ defmodule Tres.SwitchRegistry do end def get_current_xid({_dpid, _aux_id} = datapath_id) do - [{pid, _}|_] = Registry.lookup(__MODULE__, datapath_id) + [{pid, _} | _] = Registry.lookup(__MODULE__, datapath_id) :gen_statem.call(pid, :get_xid, 1000) end From 02ca49adb0863fde77fd8335c02193aff4e65abe Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sat, 17 Mar 2018 22:46:11 +0900 Subject: [PATCH 41/50] secure_channel: disable gen_statem debug option --- lib/tres/secure_channel.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 80e41c0..1917634 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -41,7 +41,7 @@ defmodule Tres.SecureChannel do " #{state_data.ip_addr}:#{state_data.port}" <> " on #{inspect(self())}" ) - :gen_statem.enter_loop(__MODULE__, [debug: [:debug]], :INIT, state_data, []) + :gen_statem.enter_loop(__MODULE__, [], :INIT, state_data, []) end # TCP handler From 25b0271ab165c7f683eeefcda2be24afc41eb37c Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sat, 17 Mar 2018 22:54:47 +0900 Subject: [PATCH 42/50] secure_channel: Fix transact_db to contains original xid in the error_msg --- src/tres_xact_kv.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tres_xact_kv.erl b/src/tres_xact_kv.erl index cb6c33a..fdbf225 100644 --- a/src/tres_xact_kv.erl +++ b/src/tres_xact_kv.erl @@ -64,5 +64,5 @@ ms_for_update(Xid, Msg) -> ms_for_handle_error(Tid, Xid, Error) -> [#?ENTRY{orig = Orig}|_] = get(Tid, Xid), - Error1 = maps:merge(Error, #{data => Orig}), + Error1 = maps:merge(Error, #{data => Orig, xid => Xid}), ets:fun2ms(fun(#?ENTRY{xid = TXid} = E) when TXid == Xid -> E#?ENTRY{pending = Error1} end). From 57611f9dc02591f9f289eedd9c339a416b71db24 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sun, 18 Mar 2018 22:35:03 +0900 Subject: [PATCH 43/50] Fix to use ETS based transaction_id counter --- config/config.exs | 2 +- lib/tres/secure_channel.ex | 16 ++++++++-------- lib/tres/secure_channel_state.ex | 27 +++++++++++++++++++-------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/config/config.exs b/config/config.exs index 40896b3..c983f46 100644 --- a/config/config.exs +++ b/config/config.exs @@ -11,7 +11,7 @@ config :tres, callback_args: [] config :logger, - level: :info, + level: :debug, format: "$date $time [$level] $metadata$message\n", metadata: [:application], handle_otp_reports: true diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 1917634..8e7ffc8 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -11,7 +11,7 @@ defmodule Tres.SecureChannel do @process_flags [ trap_exit: true, - message_queue_data: :on_heap + message_queue_data: :off_heap ] @supported_version 4 @@ -379,7 +379,7 @@ defmodule Tres.SecureChannel do defp initiate_hello_handshake(state_data) do send_hello(state_data) - ref = :erlang.send_after(@hello_handshake_timeout, self(), :hello_timeout) + ref = Process.send_after(self(), :hello_timeout, @hello_handshake_timeout) {:keep_state, %{state_data | timer_ref: ref}} end @@ -397,7 +397,7 @@ defmodule Tres.SecureChannel do defp initiate_features_handshake(state_data) do new_xid = State.increment_transaction_id(state_data.xid) send_features(new_xid, state_data) - ref = :erlang.send_after(@features_handshake_timeout, self(), :features_timeout) + ref = Process.send_after(self(), :features_timeout, @features_handshake_timeout) {:keep_state, %{state_data | timer_ref: ref}} end @@ -445,7 +445,7 @@ defmodule Tres.SecureChannel do end defp start_periodic_idle_check do - :erlang.send_after(@ping_interval, self(), :idle_check) + Process.send_after(self(), :idle_check, @ping_interval) end defp maybe_ping(state_data) do @@ -463,10 +463,10 @@ defmodule Tres.SecureChannel do false end - defp send_ping(%State{xid: x_agent} = state_data) do - xid = State.increment_transaction_id(x_agent) + defp send_ping(%State{xid: table_ref} = state_data) do + xid = State.increment_transaction_id(table_ref) send_echo_request(xid, "", state_data) - ping_ref = :erlang.send_after(@ping_timeout, self(), :ping_timeout) + ping_ref = Process.send_after(self(), :ping_timeout, @ping_timeout) %{state_data | ping_timer_ref: ping_ref, ping_xid: xid} end @@ -516,7 +516,7 @@ defmodule Tres.SecureChannel do defp maybe_cancel_timer(ref) when not is_reference(ref), do: :ok defp maybe_cancel_timer(ref) do - :erlang.cancel_timer(ref) + Process.cancel_timer(ref) :ok end diff --git a/lib/tres/secure_channel_state.ex b/lib/tres/secure_channel_state.ex index 4d1c29b..cbb91f6 100644 --- a/lib/tres/secure_channel_state.ex +++ b/lib/tres/secure_channel_state.ex @@ -29,7 +29,7 @@ defmodule Tres.SecureChannelState do 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) + {:ok, table_ref} = create_counter() kv_ref = XACT_KV.create() %SecureChannelState{ @@ -38,20 +38,31 @@ defmodule Tres.SecureChannelState do transport: transport, ip_addr: :inet.ntoa(ip_addr), port: port, - xid: xid_agent, + xid: table_ref, xact_kv_ref: kv_ref } end - def increment_transaction_id(xid_agent) do - Agent.get_and_update(xid_agent, &{&1 + 1, &1 + 1}) + def increment_transaction_id(table_ref) do + :ets.update_counter(table_ref, :datapath_xid, {2, 1, 0xffffffff, 0}) end - def set_transaction_id(xid_agent, xid) do - Agent.update(xid_agent, fn _ -> xid end) + def set_transaction_id(table_ref, xid) do + :ets.insert(table_ref, {:datapath_xid, xid}) end - def get_transaction_id(xid_agent) do - Agent.get(xid_agent, & &1) + def get_transaction_id(table_ref) do + case :ets.lookup(table_ref, :datapath_xid) do + [{_, xid} | _] -> xid + end + end + + # private functions + + @spec create_counter() :: reference() + defp create_counter do + table_ref = :ets.new(:xid_counter, [:set, :private]) + _ = :ets.insert(table_ref, {:datapath_xid, 0}) + {:ok, table_ref} end end From 238933bc1d81d2b83d861597cb94eabcd56659f6 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sun, 18 Mar 2018 22:38:13 +0900 Subject: [PATCH 44/50] secure_channel: formatted --- lib/tres/secure_channel_state.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tres/secure_channel_state.ex b/lib/tres/secure_channel_state.ex index cbb91f6..6ab8283 100644 --- a/lib/tres/secure_channel_state.ex +++ b/lib/tres/secure_channel_state.ex @@ -44,7 +44,7 @@ defmodule Tres.SecureChannelState do end def increment_transaction_id(table_ref) do - :ets.update_counter(table_ref, :datapath_xid, {2, 1, 0xffffffff, 0}) + :ets.update_counter(table_ref, :datapath_xid, {2, 1, 0xFFFFFFFF, 0}) end def set_transaction_id(table_ref, xid) do From cf5a09dfb44a32a35f21d3d28e448fab49039c49 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sun, 18 Mar 2018 22:57:05 +0900 Subject: [PATCH 45/50] Fix minor bug --- lib/tres/utils.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tres/utils.ex b/lib/tres/utils.ex index e215ff0..5bc6c12 100644 --- a/lib/tres/utils.ex +++ b/lib/tres/utils.ex @@ -25,7 +25,7 @@ defmodule Tres.Utils do packet = Openflow.to_binary(message) transport.send(socket, packet) catch - _ -> + _class, _reason -> error("[#{__MODULE__}] Unencodable error: #{inspect(message)}") end end From bb74f3d8c400b06613c9b39a315afdb2eb8d8ccd Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Mon, 19 Mar 2018 23:39:55 +0900 Subject: [PATCH 46/50] secure_channel: Fix transactional_send API to accept xid from handler processes --- lib/tres/secure_channel.ex | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 8e7ffc8..5093032 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -489,7 +489,7 @@ defmodule Tres.SecureChannel do {:keep_state, %{state_data | ping_timer_ref: nil, ping_xid: nil}} end - defp xactional_send_message(message, state_data) do + defp xactional_send_message(%{xid: 0} = message, state_data) do xid = State.increment_transaction_id(state_data.xid) messages = [ @@ -500,6 +500,17 @@ defmodule Tres.SecureChannel do XACT_KV.insert(state_data.xact_kv_ref, xid, message) send_message(messages, state_data) end + defp xactional_send_message(%{xid: xid} = message, state_data) do + _ = State.set_transaction_id(state_data.xid, xid) + + messages = [ + %{message | xid: xid}, + %{Openflow.Barrier.Request.new() | xid: xid} + ] + + XACT_KV.insert(state_data.xact_kv_ref, xid, message) + send_message(messages, state_data) + end defp send_message(message, %State{socket: socket, transport: transport}) do if is_list(message) do From 4803c61061e844b86987b6fbf2e9996747b1ac07 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Thu, 22 Mar 2018 23:49:03 +0900 Subject: [PATCH 47/50] Fix to accept transaction_id to all message structs --- lib/openflow/barrier/request.ex | 4 ++-- lib/openflow/echo/reply.ex | 8 +++++++- lib/openflow/echo/request.ex | 8 +++++++- lib/openflow/experimenter.ex | 1 + lib/openflow/features/request.ex | 4 ++-- lib/openflow/flow_mod.ex | 2 ++ lib/openflow/get_async/request.ex | 4 ++++ lib/openflow/get_config/request.ex | 4 ++-- lib/openflow/group_mod.ex | 3 ++- lib/openflow/multipart/aggregate/request.ex | 2 ++ lib/openflow/multipart/desc/request.ex | 4 ++-- lib/openflow/multipart/flow/request.ex | 2 ++ lib/openflow/multipart/group/ *Minibuf-1* | 1 - lib/openflow/multipart/group/.# *Minibuf-1* | 1 - lib/openflow/multipart/group/request.ex | 9 ++++++++- lib/openflow/multipart/group_desc/request.ex | 4 ++-- lib/openflow/multipart/group_features/request.ex | 4 ++-- lib/openflow/multipart/meter/request.ex | 9 ++++++++- lib/openflow/multipart/meter_config/request.ex | 9 ++++++++- lib/openflow/multipart/port_desc/request.ex | 4 ++-- lib/openflow/multipart/port_stats/request.ex | 9 ++++++++- lib/openflow/multipart/queue/request.ex | 3 ++- lib/openflow/multipart/table/request.ex | 4 ++-- lib/openflow/multipart/table_features/request.ex | 7 +++++-- lib/openflow/role/request.ex | 3 ++- lib/openflow/set_async.ex | 12 ++++++++++++ lib/openflow/set_config.ex | 3 ++- lib/openflow/table_mod.ex | 8 +++++++- lib/tres/message_helper.ex | 16 +++++++++++++--- 29 files changed, 118 insertions(+), 34 deletions(-) delete mode 100644 lib/openflow/multipart/group/ *Minibuf-1* delete mode 120000 lib/openflow/multipart/group/.# *Minibuf-1* diff --git a/lib/openflow/barrier/request.ex b/lib/openflow/barrier/request.ex index 399e533..346270c 100644 --- a/lib/openflow/barrier/request.ex +++ b/lib/openflow/barrier/request.ex @@ -12,8 +12,8 @@ defmodule Openflow.Barrier.Request do def ofp_type, do: 20 - def new do - %Request{} + def new(xid \\ 0) do + %Request{xid: xid} end def read(_) do diff --git a/lib/openflow/echo/reply.ex b/lib/openflow/echo/reply.ex index 455ef64..dbc7029 100644 --- a/lib/openflow/echo/reply.ex +++ b/lib/openflow/echo/reply.ex @@ -13,7 +13,13 @@ defmodule Openflow.Echo.Reply do def ofp_type, do: 3 - def new(data \\ "") do + def new(options) when is_list(options) do + %Reply{ + xid: options[:xid] || 0, + data: options[:data] || "" + } + end + def new(data) when is_binary(data) do %Reply{data: data} end diff --git a/lib/openflow/echo/request.ex b/lib/openflow/echo/request.ex index f3a6af3..36dbc0d 100644 --- a/lib/openflow/echo/request.ex +++ b/lib/openflow/echo/request.ex @@ -13,7 +13,13 @@ defmodule Openflow.Echo.Request do def ofp_type, do: 2 - def new(data \\ "") do + def new(options) when is_list(options) do + %Request{ + xid: options[:xid] || 0, + data: options[:data] || "" + } + end + def new(data) when is_binary(data) do %Request{data: data} end diff --git a/lib/openflow/experimenter.ex b/lib/openflow/experimenter.ex index 1d540f0..52f1505 100644 --- a/lib/openflow/experimenter.ex +++ b/lib/openflow/experimenter.ex @@ -15,6 +15,7 @@ defmodule Openflow.Experimenter do def new(options) do %Experimenter{ + xid: options[:xid] || 0, exp_id: Keyword.get(options, :exp_id, 0), exp_type: Keyword.get(options, :exp_type, 0), data: Keyword.get(options, :data, "") diff --git a/lib/openflow/features/request.ex b/lib/openflow/features/request.ex index 83aadbb..8fb5f4e 100644 --- a/lib/openflow/features/request.ex +++ b/lib/openflow/features/request.ex @@ -12,8 +12,8 @@ defmodule Openflow.Features.Request do def ofp_type, do: 5 - def new do - %Request{} + def new(xid \\ 0) do + %Request{xid: xid} end def read(_) do diff --git a/lib/openflow/flow_mod.ex b/lib/openflow/flow_mod.ex index 1c1d972..4b3b541 100644 --- a/lib/openflow/flow_mod.ex +++ b/lib/openflow/flow_mod.ex @@ -24,6 +24,7 @@ defmodule Openflow.FlowMod do def ofp_type, do: 14 def new(options \\ []) do + xid = Keyword.get(options, :xid, 0) cookie = Keyword.get(options, :cookie, 0) cookie_mask = Keyword.get(options, :cookie_mask, 0) table_id = Keyword.get(options, :table_id, 0) @@ -39,6 +40,7 @@ defmodule Openflow.FlowMod do instructions = Keyword.get(options, :instructions, []) %FlowMod{ + xid: xid, cookie: cookie, cookie_mask: cookie_mask, priority: priority, diff --git a/lib/openflow/get_async/request.ex b/lib/openflow/get_async/request.ex index 3be0048..0306095 100644 --- a/lib/openflow/get_async/request.ex +++ b/lib/openflow/get_async/request.ex @@ -12,6 +12,10 @@ defmodule Openflow.GetAsync.Request do def ofp_type, do: 26 + def new(xid \\ 0) do + %Request{xid: xid} + end + def read(_) do %Request{} end diff --git a/lib/openflow/get_config/request.ex b/lib/openflow/get_config/request.ex index e9e0af4..f5ec984 100644 --- a/lib/openflow/get_config/request.ex +++ b/lib/openflow/get_config/request.ex @@ -12,8 +12,8 @@ defmodule Openflow.GetConfig.Request do def ofp_type, do: 7 - def new do - %Request{} + def new(xid \\ 0) do + %Request{xid: xid} end def read(_) do diff --git a/lib/openflow/group_mod.ex b/lib/openflow/group_mod.ex index f8f2a54..6f76684 100644 --- a/lib/openflow/group_mod.ex +++ b/lib/openflow/group_mod.ex @@ -15,11 +15,12 @@ defmodule Openflow.GroupMod do def ofp_type, do: 15 def new(options \\ []) do + xid = Keyword.get(options, :xid, 0) 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} + %GroupMod{xid: xid, command: command, type: type, group_id: group_id, buckets: buckets} end def read(<>) do diff --git a/lib/openflow/multipart/aggregate/request.ex b/lib/openflow/multipart/aggregate/request.ex index 89f55e3..0335cb9 100644 --- a/lib/openflow/multipart/aggregate/request.ex +++ b/lib/openflow/multipart/aggregate/request.ex @@ -18,6 +18,7 @@ defmodule Openflow.Multipart.Aggregate.Request do def ofp_type, do: 18 def new(options) do + xid = Keyword.get(options, :xid, 0) table_id = Keyword.get(options, :table_id, :all) out_port = Keyword.get(options, :out_port, :any) out_group = Keyword.get(options, :out_group, :any) @@ -26,6 +27,7 @@ defmodule Openflow.Multipart.Aggregate.Request do match = Keyword.get(options, :match, []) %Request{ + xid: xid, table_id: table_id, out_port: out_port, out_group: out_group, diff --git a/lib/openflow/multipart/desc/request.ex b/lib/openflow/multipart/desc/request.ex index 4d9bef7..f0b17c5 100644 --- a/lib/openflow/multipart/desc/request.ex +++ b/lib/openflow/multipart/desc/request.ex @@ -11,8 +11,8 @@ defmodule Openflow.Multipart.Desc.Request do def ofp_type, do: 18 - def new do - %Request{} + def new(xid \\ 0) do + %Request{xid: xid} end def read("") do diff --git a/lib/openflow/multipart/flow/request.ex b/lib/openflow/multipart/flow/request.ex index dc14e43..c3b37b6 100644 --- a/lib/openflow/multipart/flow/request.ex +++ b/lib/openflow/multipart/flow/request.ex @@ -18,6 +18,7 @@ defmodule Openflow.Multipart.Flow.Request do def ofp_type, do: 18 def new(options \\ []) do + xid = Keyword.get(options, :xid, 0) table_id = Keyword.get(options, :table_id, :all) out_port = Keyword.get(options, :out_port, :any) out_group = Keyword.get(options, :out_group, :any) @@ -26,6 +27,7 @@ defmodule Openflow.Multipart.Flow.Request do match = Keyword.get(options, :match, Openflow.Match.new()) %Request{ + xid: xid, table_id: table_id, out_port: out_port, out_group: out_group, diff --git a/lib/openflow/multipart/group/ *Minibuf-1* b/lib/openflow/multipart/group/ *Minibuf-1* deleted file mode 100644 index 04e6d8d..0000000 --- a/lib/openflow/multipart/group/ *Minibuf-1* +++ /dev/null @@ -1 +0,0 @@ -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* deleted file mode 120000 index 7cdb737..0000000 --- a/lib/openflow/multipart/group/.# *Minibuf-1* +++ /dev/null @@ -1 +0,0 @@ -shun159@shun159.8967:1509553730 \ No newline at end of file diff --git a/lib/openflow/multipart/group/request.ex b/lib/openflow/multipart/group/request.ex index bc7130c..30103d8 100644 --- a/lib/openflow/multipart/group/request.ex +++ b/lib/openflow/multipart/group/request.ex @@ -12,7 +12,14 @@ defmodule Openflow.Multipart.Group.Request do def ofp_type, do: 18 - def new(group_id \\ :all) do + def new(options) when is_list(options) do + %Request{ + xid: options[:xid] || 0, + group_id: options[:group_id] || :all + } + end + + def new(group_id) when is_integer(group_id) or is_atom(group_id) do %Request{group_id: group_id} end diff --git a/lib/openflow/multipart/group_desc/request.ex b/lib/openflow/multipart/group_desc/request.ex index 7fda473..c8c3dae 100644 --- a/lib/openflow/multipart/group_desc/request.ex +++ b/lib/openflow/multipart/group_desc/request.ex @@ -11,8 +11,8 @@ defmodule Openflow.Multipart.GroupDesc.Request do def ofp_type, do: 18 - def new do - %Request{} + def new(xid \\ 0) do + %Request{xid: xid} end def read("") do diff --git a/lib/openflow/multipart/group_features/request.ex b/lib/openflow/multipart/group_features/request.ex index b44c0dd..e473878 100644 --- a/lib/openflow/multipart/group_features/request.ex +++ b/lib/openflow/multipart/group_features/request.ex @@ -11,8 +11,8 @@ defmodule Openflow.Multipart.GroupFeatures.Request do def ofp_type, do: 18 - def new do - %Request{} + def new(xid \\ 0) do + %Request{xid: xid} end def read("") do diff --git a/lib/openflow/multipart/meter/request.ex b/lib/openflow/multipart/meter/request.ex index 084b2fb..ea7bbdf 100644 --- a/lib/openflow/multipart/meter/request.ex +++ b/lib/openflow/multipart/meter/request.ex @@ -12,10 +12,17 @@ defmodule Openflow.Multipart.Meter.Request do def ofp_type, do: 18 - def new(meter_id \\ :all) do + def new(meter_id) when is_integer(meter_id) or is_atom(meter_id) do %Request{meter_id: meter_id} end + def new(options) when is_list(options) do + %Request{ + xid: options[:xid] || 0, + meter_id: options[:meter_id] || :all + } + end + def read(<>) do meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) %Request{meter_id: meter_id} diff --git a/lib/openflow/multipart/meter_config/request.ex b/lib/openflow/multipart/meter_config/request.ex index afd7dd1..bd4c7a2 100644 --- a/lib/openflow/multipart/meter_config/request.ex +++ b/lib/openflow/multipart/meter_config/request.ex @@ -12,10 +12,17 @@ defmodule Openflow.Multipart.MeterConfig.Request do def ofp_type, do: 18 - def new(meter_id \\ :all) do + def new(meter_id) when is_integer(meter_id) or is_atom(meter_id) do %Request{meter_id: meter_id} end + def new(options) when is_list(options) do + %Request{ + xid: options[:xid] || 0, + meter_id: options[:meter_id] || :all + } + end + def read(<>) do meter_id = Openflow.Utils.get_enum(meter_id_int, :meter_id) %Request{meter_id: meter_id} diff --git a/lib/openflow/multipart/port_desc/request.ex b/lib/openflow/multipart/port_desc/request.ex index e6bfe44..2a029f3 100644 --- a/lib/openflow/multipart/port_desc/request.ex +++ b/lib/openflow/multipart/port_desc/request.ex @@ -11,8 +11,8 @@ defmodule Openflow.Multipart.PortDesc.Request do def ofp_type, do: 18 - def new do - %Request{} + def new(xid \\ 0) do + %Request{xid: xid} end def read("") do diff --git a/lib/openflow/multipart/port_stats/request.ex b/lib/openflow/multipart/port_stats/request.ex index 6b91648..ba85c04 100644 --- a/lib/openflow/multipart/port_stats/request.ex +++ b/lib/openflow/multipart/port_stats/request.ex @@ -12,10 +12,17 @@ defmodule Openflow.Multipart.PortStats.Request do def ofp_type, do: 18 - def new(port_no \\ :any) do + def new(port_no) when is_integer(port_no) or is_atom(port_no) do %Request{port_number: port_no} end + def new(options) when is_list(options) do + %Request{ + xid: options[:xid] || 0, + port_number: options[:port_no] || :any + } + end + def read(<>) do port_no = Openflow.Utils.get_enum(port_no_int, :openflow13_port_no) %Request{port_number: port_no} diff --git a/lib/openflow/multipart/queue/request.ex b/lib/openflow/multipart/queue/request.ex index 7cab958..4240ede 100644 --- a/lib/openflow/multipart/queue/request.ex +++ b/lib/openflow/multipart/queue/request.ex @@ -14,9 +14,10 @@ defmodule Openflow.Multipart.Queue.Request do def ofp_type, do: 18 def new(options) do + xid = Keyword.get(options, :xid, 0) 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} + %Request{xid: xid, port_number: port_no, queue_id: queue_id} end def read(<>) do diff --git a/lib/openflow/multipart/table/request.ex b/lib/openflow/multipart/table/request.ex index 39938c2..1e0390d 100644 --- a/lib/openflow/multipart/table/request.ex +++ b/lib/openflow/multipart/table/request.ex @@ -11,8 +11,8 @@ defmodule Openflow.Multipart.Table.Request do def ofp_type, do: 18 - def new do - %Request{} + def new(xid \\ 0) do + %Request{xid: xid} end def read("") do diff --git a/lib/openflow/multipart/table_features/request.ex b/lib/openflow/multipart/table_features/request.ex index b4b9fad..451977d 100644 --- a/lib/openflow/multipart/table_features/request.ex +++ b/lib/openflow/multipart/table_features/request.ex @@ -14,8 +14,11 @@ defmodule Openflow.Multipart.TableFeatures.Request do def ofp_type, do: 18 - def new(tables \\ []) do - %Request{tables: tables} + def new(options \\ []) do + %Request{ + xid: options[:xid] || 0, + tables: options[:tables] || [] + } end def read(<>) do diff --git a/lib/openflow/role/request.ex b/lib/openflow/role/request.ex index fcb66f1..3bacde8 100644 --- a/lib/openflow/role/request.ex +++ b/lib/openflow/role/request.ex @@ -15,9 +15,10 @@ defmodule Openflow.Role.Request do def ofp_type, do: 24 def new(options \\ []) do + xid = Keyword.get(options, :xid, 0) role = Keyword.get(options, :role, :nochange) generation_id = Keyword.get(options, :generation_id, 0) - %Request{role: role, generation_id: generation_id} + %Request{xid: xid, role: role, generation_id: generation_id} end def read(<>) do diff --git a/lib/openflow/set_async.ex b/lib/openflow/set_async.ex index 717d644..f42095b 100644 --- a/lib/openflow/set_async.ex +++ b/lib/openflow/set_async.ex @@ -18,6 +18,18 @@ defmodule Openflow.SetAsync do def ofp_type, do: 28 + def new(options \\ []) do + %SetAsync{ + xid: options[:xid] || 0, + packet_in_mask_master: options[:packet_in_mask_master], + packet_in_mask_slave: options[:packet_in_mask_slave], + port_status_mask_master: options[:port_status_mask_master], + port_status_mask_slave: options[:port_status_mask_slave], + flow_removed_mask_master: options[:flow_removed_mask_master], + flow_removed_mask_slave: options[:flow_removed_mask_slave] + } + end + def read( <> diff --git a/lib/openflow/set_config.ex b/lib/openflow/set_config.ex index 18f3a8c..95929a8 100644 --- a/lib/openflow/set_config.ex +++ b/lib/openflow/set_config.ex @@ -14,9 +14,10 @@ defmodule Openflow.SetConfig do def ofp_type, do: 9 def new(options \\ []) do + xid = Keyword.get(options, :xid, 0) 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} + %SetConfig{xid: xid, flags: flags, miss_send_len: miss_send_len} end def read(<>) do diff --git a/lib/openflow/table_mod.ex b/lib/openflow/table_mod.ex index 2ea364a..1f6adb4 100644 --- a/lib/openflow/table_mod.ex +++ b/lib/openflow/table_mod.ex @@ -12,7 +12,13 @@ defmodule Openflow.TableMod do def ofp_type, do: 17 - def new(table_id) do + def new(options) when is_list(options) do + %TableMod{ + xid: options[:xid] || 0, + table_id: options[:table_id] || 0 + } + end + def new(table_id) when is_integer(table_id) or is_atom(table_id) do %TableMod{table_id: table_id} end diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index 0c7946e..8a6dc4a 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -24,6 +24,7 @@ defmodule Tres.MessageHelper do command = Tres.Utils.flow_command(:modify, options) flow_mod = %Openflow.FlowMod{ + xid: options[:xid] || 0, cookie: options[:cookie] || 0, table_id: options[:table_id] || 0, command: command, @@ -42,6 +43,7 @@ defmodule Tres.MessageHelper do command = Tres.Utils.flow_command(:delete, options) flow_mod = %Openflow.FlowMod{ + xid: options[:xid] || 0, cookie: options[:cookie] || 0, cookie_mask: options[:cookie_mask] || 0, table_id: options[:table_id] || :all, @@ -56,6 +58,7 @@ defmodule Tres.MessageHelper do defp send_packet_out(datapath_id, options \\ []) do packet_out = %Openflow.PacketOut{ + xid: options[:xid] || 0, buffer_id: options[:buffer_id] || :no_buffer, in_port: options[:in_port] || :controller, actions: options[:actions] || [], @@ -68,6 +71,7 @@ defmodule Tres.MessageHelper do defp send_group_mod_add(datapath_id, options \\ []) do group_mod = Openflow.GroupMod.new( + xid: options[:xid] || 0, command: :add, type: options[:type] || :all, group_id: options[:group_id] || 0, @@ -77,14 +81,19 @@ defmodule Tres.MessageHelper do send_message(group_mod, datapath_id) end - defp send_group_mod_delete(datapath_id, group_id) do - group_mod = Openflow.GroupMod.new(command: :delete, group_id: group_id) + defp send_group_mod_delete(datapath_id, options \\ []) do + group_mod = Openflow.GroupMod.new( + xid: options[:xid] || 0, + command: :delete, + group_id: options[:group_id] || :all + ) send_message(group_mod, datapath_id) end - defp send_group_mod_modify(datapath_id, options) do + defp send_group_mod_modify(datapath_id, options \\ []) do group_mod = Openflow.GroupMod.new( + xid: options[:xid] || 0, command: :modify, type: options[:type] || :all, group_id: options[:group_id] || 0, @@ -97,6 +106,7 @@ defmodule Tres.MessageHelper do defp send_role_request(datapath_id, options) do role_request = Openflow.Role.Request.new( + xid: options[:xid] || 0, role: options[:role] || :nochange, generation_id: options[:generation_id] || 0 ) From 07fe33df58fac31ab815dd8684839d86a58a078e Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 23 Mar 2018 21:28:12 +0900 Subject: [PATCH 48/50] secure_channel: Fix ping_timeout event handling --- lib/tres/secure_channel.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 5093032..1f41636 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -280,7 +280,7 @@ defmodule Tres.SecureChannel do end # WATING state - defp handle_WATING(:enter, :CONNECTING, state_data) do + defp handle_WATING(:enter, _state, state_data) do warn("[#{__MODULE__}] Possible HANG Detected on datapath_id: #{state_data.datapath_id} !") %State{handler_pid: handler_pid, datapath_id: dpid, aux_id: aux_id} = state_data send(handler_pid, {:switch_hang, {dpid, aux_id}}) @@ -311,7 +311,7 @@ defmodule Tres.SecureChannel do end defp handle_packet("", state_data, _state, actions) do - {:keep_state, state_data, Enum.reverse(actions)} + {:keep_state, %{state_data | ping_fail_count: 0}, Enum.reverse(actions)} end defp handle_packet(packet, %State{buffer: buffer} = state_data, state, actions) do @@ -470,9 +470,9 @@ defmodule Tres.SecureChannel do %{state_data | ping_timer_ref: ping_ref, ping_xid: xid} end - defp handle_ping_timeout(%State{ping_fail_count: fail_count} = state_data, :CONNECTING) + defp handle_ping_timeout(%State{ping_fail_count: fail_count} = state_data, :CONNECTED) when fail_count > @ping_fail_max_count do - {:next_state, :WAITING, state_data} + {:next_state, :WAITING, %{state_data | ping_fail_count: fail_count + 1}} end defp handle_ping_timeout(%State{ping_fail_count: fail_count} = state_data, :WAITING) @@ -481,11 +481,11 @@ defmodule Tres.SecureChannel do end defp handle_ping_timeout(state_data, _) do - new_state_data = maybe_ping(state_data) - {:keep_state, new_state_data} + {:keep_state, %{state_data | ping_fail_count: state_data.ping_fail_count + 1}} end defp handle_ping_reply(state_data) do + :ok = maybe_cancel_timer(state_data.ping_timer_ref) {:keep_state, %{state_data | ping_timer_ref: nil, ping_xid: nil}} end From 44919bc079593b1df5c4f2a40822d3fad0e8c001 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Fri, 23 Mar 2018 11:14:17 +0900 Subject: [PATCH 49/50] Refactored Enum macro --- config/config.exs | 2 +- lib/openflow/enums.ex | 49 +++++++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/config/config.exs b/config/config.exs index c983f46..40896b3 100644 --- a/config/config.exs +++ b/config/config.exs @@ -11,7 +11,7 @@ config :tres, callback_args: [] config :logger, - level: :debug, + level: :info, format: "$date $time [$level] $metadata$message\n", metadata: [:application], handle_otp_reports: true diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex index 58cf0a0..5ae9212 100644 --- a/lib/openflow/enums.ex +++ b/lib/openflow/enums.ex @@ -988,59 +988,58 @@ defmodule Openflow.Enums do ] 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") + to_int_fn_name = :"#{enum_name}_to_int" + to_atom_fn_name = :"#{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 - :throw, _ -> unquote(key) - end + def to_int(unquote(key), unquote(enum_name)) do + unquote(to_int_fn_name)(unquote(key)) + catch + _class, _reason -> unquote(key) end end for {_key, value} <- enum_def do - def to_atom(unquote(value), unquote(String.to_atom(enum_name))) do - try do - unquote(to_atom_fn_name)(unquote(value)) - catch - :throw, _ -> unquote(value) - end + def to_atom(unquote(value), unquote(enum_name)) do + unquote(to_atom_fn_name)(unquote(value)) + catch + _class, _reason -> unquote(value) end end - def to_int(_int, unquote(String.to_atom(enum_name))) do + def to_int(_int, unquote(enum_name)) do throw(:bad_enum) end - def to_atom(_, unquote(String.to_atom(enum_name))) do + def to_atom(_, unquote(enum_name)) do throw(:bad_enum) end + end + + for {enum_name, enum_def} <- @enums do + to_int_fn_name = :"#{enum_name}_to_int" + to_atom_fn_name = :"#{enum_name}_to_atom" for {key, value} <- enum_def do def unquote(to_int_fn_name)(unquote(key)), do: unquote(value) end - def unquote(to_int_fn_name)(_), do: throw(:bad_enum) for {key, value} <- enum_def do def unquote(to_atom_fn_name)(unquote(value)), do: unquote(key) end - def unquote(to_atom_fn_name)(_), do: throw(:bad_enum) + end - 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)))) + for {enum_name, enum_def} <- @enums do + def int_to_flags(int, unquote(enum_name)) do + Openflow.Utils.int_to_flags([], int, enum_of(unquote(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)))) + def flags_to_int(flags, unquote(enum_name)) do + Openflow.Utils.flags_to_int(0, flags, enum_of(unquote(enum_name))) end - defp enum_of(unquote(String.to_atom(enum_name))), do: unquote(enum_def) + defp enum_of(unquote(enum_name)), do: unquote(enum_def) end end From 0dd7867ceb70d77056a4fe8cfa384652dee81a66 Mon Sep 17 00:00:00 2001 From: Eishun Kondoh Date: Sat, 24 Mar 2018 01:27:48 +0900 Subject: [PATCH 50/50] Add openflow enum generator --- bin/enum_gen | Bin 0 -> 1422494 bytes lib/openflow/echo/reply.ex | 1 + lib/openflow/echo/request.ex | 1 + lib/openflow/enum_gen.ex | 1122 +++ lib/openflow/enums.ex | 13392 +++++++++++++++++++++++++++++++-- lib/openflow/table_mod.ex | 1 + lib/tres/message_helper.ex | 12 +- lib/tres/secure_channel.ex | 1 + mix.exs | 3 +- 9 files changed, 14005 insertions(+), 528 deletions(-) create mode 100755 bin/enum_gen create mode 100644 lib/openflow/enum_gen.ex diff --git a/bin/enum_gen b/bin/enum_gen new file mode 100755 index 0000000000000000000000000000000000000000..eae0d856fd43bf0fca0f15843c72a862b82ebf2a GIT binary patch literal 1422494 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
  • w3g)I9!d5qA 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͇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 ScbzFH{il#lhp1_`+4HGlPAMYV)5Q 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

    $73~u?1IW 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

    Kllbg?Zu!)K4${Xsg@V#%?`sdIQpeUPzQb?) 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 z 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%0DvU=XURORp@q{)}Zs$duOV`>7PMjyF=G8vEAqi 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 z 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

    {=RY1hgI9RhJFp3 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 z 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 z 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

    pu? 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

    2!RAKMAK)hmWk|33hjLH{^|=> z<@@Gu-SX9rZ%#jW?~H9nkYh~n$|E;Y!mr-nHenU}j^iuszFwW4TYp*R!hz}fkM2K7 zul|`MwS0SE*w$?){f{ozynOrbfhNDcL)Q|-4%_FxJYw-n|MH~DGj^CiTDI9Jv{>_PEOYw{E0jLHOltg-D8{M8L;qDkHY7};Xj>a#44?=@6KM} z^r1oEf>yzbDz$ZcT~6HdwqJWrJUEci|Hjvg&rWU8Tz$iP-qR~~alda(zp(U1^9GjM zUsKx@ryWne%or&5@9l~P_1`98Z(cHNITX9j#kONjDf2Du8QP*p1E)_u?RHD(@V9FV zF8d36bLNMciEiEL5icHA@c7{dk-CyYUymearBw@Q?0ezmIfd)W=bpHH`6Y8B1p~t- z_8(7Pq8$_*?O+qEnCoZ$_d(qjU5^NDk-)@hCH5CYCLUXT>RB%1%AZf?MkOxO{u8n6 z-4lMnS(mNnTJ*@?@_Xjavv=X_d*{9_op1JQ;Ud;gY=-gb76RNEllCgs**b`*eQ9a3 z=!u*sXJEU(^sB^{NWn{i3(GZLbKI)&n6!Oisp%;rS=LQYT_?)1Kge@XxFu!7o2ET^ z+sWWZ2EA^3?pv{Mx~6bTQSI8CMrQs`bEozl=;F%D4gTY=e}2cU5{Jl+LY}ARQrfR? z=6dHMXOo(CR)@hp z!d~U9ll|FQ%hz!~I$;0dTG;Z+1^)Y-|Ihp$JF$O*|FK)|YI9F~I8u1X>Cg`bL#y-p zPYcdopZZyj>(ko@sjj=vO@V+zr_$EGK0`TyxS#4>-5flu`B2P}d2 z_M5eCxhmPRdB*bS_uc6=v))IfUN~m^<=kowZHpYiw--j8N9!y zoYY!4-9jN+RX^Xf-Fr_&%L6tY znV&lfH8o$o&;QbNByfH6#F_ffUUwJt&3eDgJzu=w;L3-ZS<|{7^>HtCpTN3YS@BNr z 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

    f7Jnzs=>!i)Vg%^Z2`7 z$?KWxa!=m5`{vfWBPd{roym)KGXZx?dbIOiH$^7`#F-7WV?El-!x~o>4y>aLD zJo95;KXBMf$H-XxJOA_C`?j~g&h96 z+nqBe3tTmS{W>p7?8w?O-U7brqTHV!>yAeG&dk1f>to__d7k+OneTkRuNG`Ko_FQ= z#J~3|K0oq(HM8$q?Ve?K7Y8#R%AdDKsODu_NzA1g2No!Ozus&&cay@~gqH^Hsb5{P zo>qRX6pVZu&Zcw!tzgMpF0Xu@xJ!SZ$0cvJPF+`!abm>=`Os~PZoPClbmv94bi97# zuV;qSe?1DD|K)a>ddk^brKfM3ims0?yK-;uqRr)B{Hve!Wlr2#R{BXtwX+78e7wy`2A zMx~Z^e!ac>Ro3iVYN*^5$SDcV=cGn1!kK*0CzGPxu;QzTPdlssS#-7v8+SlCUZ(Lv)%Ra^S$cWwyjy@WvA+$ehYqDhC#iqVecG39sq!gx7wz9|xO%(0>W*IWkzILHBffl;PTg~XNBT*O)BF>^ zLR@s$PBvWRcm2f1MI7O`|4ci6EPrZlZQPZRw%j+}RgsDxeLG!k<4o&bUefxiY15llK4B{ zAAYiY6!-tcwmh+o&z#u1O<$;P{LpQ8BluW%^`ho4+;bnR6bt;m;C1fnr2A)PD$SQ$ zxu|N&%WDD6m(z~y`JsI5=A-MeZ$%&ezPNejTduxoEq4F3lpg)Pd6>n1*Yd6lWr=|) zW}W8sRX%To6D3`fdS-1Ff35ic=H2agy~URDu1?yz#$Nisy~O650%lWw8rGY2^`1NvxyVkM2&f++`e1DL6l=u2eueUVJTe3V;QFpoM7b9DL{SRSxI<1Sv@3rmz z*tf^{`-6FRCYC>)yW`yL*RL#MW!JCn+x2S6>T|nZEn1zsW$LZ+TUxoiR}O{lU@R?Q ze|N}sMLXNQx!)huE@1FGB$*JfWc9vXx8lR&FNZ2iE?I3``jt2K>m$w=qOrXfu6leC zd(~YNP;bKW(@=YJvs*?}{R2Up=Clev)dEI6C(S92mLU$R=Ue|4a1=e1coOXQ{=)Gu zni>hL)&&gR>-er*Eie6Q8!NkT_2H7MzTws{veM<&uTCy86y1m))zch?RD-`Ul{FbTbEqDg|Dzg+%&;mq{+@T_@aqOTWo>!9kK66oHxpU zJN`aF{|@{2!}}@(rKgsM>Q0|kC+9!W+Ub`k;MV2IY3cuN5?j%HlSxrQF0TUhzp%|; zxVpHsbl$h=*H)Nj_HU`*lJ?eelLG6_m#a1|G%hs_)V^{q!^e8HX!i51+_&U!xxSqg zwrXm2=`G*4dh#dOKOVd@zy5&xA+H~NMv1mL4W>GMM>k96h|NAbDZ`}(-`~tzA?J7S{-csP2hV>h-X-N{r?S|SVWxxHBmtwDK8g$f2(5qgbWcg=YQb!81#H!e5nez(y6 za?UJg^NS*XEa!DCe;Dw>S*D}w^GD7auKCAWe{iikYF;3_uQT7Uckhuo5!uhq$mp&M zOj&h(%Z&>L>-Cc!FSeg=X{@uae1^j8|I995^S%fyM?WY(WYOYBYOD+limVI_l8A%y z9gEUSb5rw5kgh3FWW9%UO-XF1Z;r6L$h@x>-)?m#J}Twl{3%&rvB2ygqY{ViCABM@ zfx+ql0op00d%yL~^}V+H-9Ht^g<7nR+>4Z+q;W3YS&*$-m2oxLHL%*jX$AYOXw|5T ztBx-V$`8G2{l#hA#?Pp1G`= z_DoVIDAaq~(<7!~nIL>vb(yOv5 zxATDLW?$BrWLuY2MT#fnPWjICa5;2y#%9%o$0i@&79<=t{nqgyi@WVE!@mh0(zgoY zY!shKZJQ$g`B87#{t8Ed+fNxi{XWjT8097~xwMFld(n|}@pi?omDQcL6}A@}tutlV zbPm2a$|t96H1os(kKKP)9%7zX;41Kai=qu>OP{k!NQ zukmX~lgv}CNUziG3;Sc9DYi}joO|7le|x|pBIi`JU#l&4wm!#Js{87kVbnIB z!%>&ca_PQ4b3ta+t)8l9O_{ngOEUXrUI{C0x1Vo1VO4(2Gvg$M4l9%#HPcVsdHrvl5LD}2@d3FS=R*vy`nvqqh}xxM+m)Q#GP zf72}%D%3T6pAh@)ca69J>lczOFXu$CVRzA70yOT&cco)zd4_ z=imAHV(RVkv@1;)_Li-l(I5Z2LNWZ@wD;?#pNqS8_UHFJy*akO9e*!=|4BMHJnvjz zoc(^QhPr>LD!muBgj&ge`TogjTa|qCd9gd`>m+TA#4icNmH52lP;-3OUK_i7f2GmX zzklyv6u)n`&*D7iMy1)o;-#lI*UoD5_`Le6jnL=M8oU3v^}fGry3WIJ{(_u(uBk^K zR+{j#b6T`t__lt(<>@C^!aF3YK7P1n*(}V?|BpFo|9`tJ7`Kr8v;4=d$;7~*!oGogwZVm=8U@rd5Zi2xT2SaNq&= zkvB0bf2JPQ^!#{qDQnD8)lOH{kCrnlZ^k&}Wd%m271jxQr^Tglmgm)ZW%^~+q{T(q zMP~*2CB<3QS7mwHOftA&@chq|5QV9HN0@j741_x@8)vizCM;u^JVA(gaZ~ez4a}1_ zHCr=(_F=VWW^-$v$>!ta$JE28w(JbwJEk*-dKir*XS6g)NC?Zc%t$x-!O3AD*5Jw_ zAuThbRk4YS>kQ99RaZp?Z<}Inhb?RZ#?FSqQ_`4!3U)|Nyy3G>G~uh7LOG+McE~*D zQ)vyWQX9KAJDg=^RGWWBXu`cR1-k%4_3hUuzMs(4@cN(RW~F8K*KR1=H0{Hz6({~o zE4}W{xN}3#45Q1Fzy3eJNk3tOX8bw9-w#x}gX^AVP7bf0Huu(Xxjn}pz1$^M_WkNd ztKEEH7< z-_bez$EN;#z1zZQv)7U}pC&sm$V(~E(bQyObuZ>Su~gDWljlt8(Mkor*pGs1rz+!_Z@(me?p*onsN~Yhwq?_Pv08Rc{q>V|Wo4%2&!@jsT~{9crMZ&z=&uP= zmc_?RS<~-PQgz@83&YVm+jDc6(5vh#){WO!FflMJW@2EFLsZ$pB}JKe>3Yr?iABYF zZaMjhC6Gk6m?;J+k>v(EW?yj-ndkY&Y<6Detdi(1C2xKw$8FJD7hQ9{wQK63Ra>{I zA5@v0vVF7K%o!=KwUm-HUB4)_$}!2=REX}WxG-6xN=Wcv^=titqqD{DrzCH8V&6B@ zJb&N!XM0ah|EcXgnd9U9^wY-E(-uuN=2&>Y;o-$i&QU5FaotRhu67r%b`ubLE}P}j zv`lclo2hPec2%0o3h__tOs{4KU03hZl@e?TnLSZ9EhfuU*7~tjnuGJjO@9xV^`6*f zRQ5n3u69A5dsO@6B`Gsw?_b>FlfqJ<_Jgr^g{SO3H|D1eO+_p9S87WK>iY2g=DaEs zbXDP0YgP7kW}%x=u}W7I6PGZq_p(vmae4duca~Wo2)LhAdcfB1aKEajfu#r=E#ef)U6{EybdJ;AP=)7G!D;5h9U^B^T~+K2NplO6@F zXPK-la_QK<4Hzr?r_d_kTWxINR-2X2>ZhSmDo!9L|*xS(L-V13HF6fj@I3twy z-`-G7DmABX=XaB;f{Y-R7Z;rVXKuZyv0d-SgvTGOl6h{PoO$i9^OmhA*;WbkXKrfQ z)x1~ncT2>%d8hr-&nvUG*j=3Xw`Jy^C1tPmg|{qAxWbH{#$OkFe51j_z@W^+z#xxE zCoSvv*RxK#dd9P5Cw*U^+GuCWOwLQ?+mQhEmI{Z}VV&YCasmb_btZ*#rC2w7`8n_N-1EVik@DB{uF6>(LD$+UdM~g{P((Hk=NnWd+2Nv%D*^wPA6;zn1tWLTwM_R-zCWs2SQ?7my4 z`j$-!3YfAdSM<`hMOUi73RfwHXJ7PD^quJEuN-?dAn+{9o(~CDzMm_Pe7cen95wHk zv8MG=$;jZ1-fCZ?O{PA~b)FgebxP=}|KTf_oy%Ig?w#MZ$$mR8&-b4B^H$TzNdebd z5|s~&{m?Zwys>kg(Gdxj4%r97-;*RJJ7+Mo9^$Y1)g~0|p0+YHBBm;v^OOqb1Qt%o zNgW+B27=5V1ZJ+XGEUj{YQ~vK4yLeKtaor)=XF5Vg~Mp#{fV!i++g5(V4y6|<;(xjpGE1!MxVVd z9G;>poNy{BF9NRhFf3AM6LEb#nGPLA|TZCZ<9t6XHn37^Aj@@ z6`SrbDTati-hMK}E--^_0{6tcZH((~S|08&Xp6ZK`ES3~qnPA_9NgPtI9)D;wi#%a zZz!*}5_l=l`6R7l*IBWsh*Q_*PD_+5%C%OhE;4t#8dg+&r;YQX#FJa!*}pAFmrQcz z_|B);&~e4FN|oLJ1WV)LZKiMKUVXN_9Lw4K`z<%mS01}I24`jFBmqg4^Z(qLC8O^C zKl)VosF&R2#`KJjk9ONiSKR72zB}f|=DS;OUT%>ntK1X4``~728;14!&OfeIX05$d z&3?VVYGau{_l2kT&5woLzFKokPO)#Asc#Z&i$nE$x`cJuRO_4D2zv9bT z_53OJdu(bx{yX;gp$o6ofCWXnJ=u|6IWQmleaBa z!(QtAeDh_~_wl{A&d}9S={mnN{Yurjsb&{XsRXV%>}h#*FT=5ry+%w9U$egb;yZf0 z;!O9RBkVs@fMDMY{~l^kEO7p7cvKezs3c# zGBEhDGBC&>3K^%w6t~j6?r&~rc8*A>$iDLTW#whJmu|PdJ#Cs@ z-c7gv7mW5yNm6i5jh3o3;W*}=IOFc#yM8n0-aNCIU-i}rWtC6N@|}^)EY1H6-2{_O zKJp*@;m+)JMZ!10B{^x)kr_WaJ{&GCPrrTD;^a)r=Xc&)Kc81DZ@j7cyj05~d;auo z@9w;Md%Hv6nt;>GZ#|i3B+otAFi-yD+BxYr!j2W@xL-0kJZp=ou9@VH*q}4{9nan* zoiwP7`EGOk#4ovWWmoI>vu%Q|6!*P7_Ib~f4|CpqtZA`cmM1!k`_AQ6GrUghShjQC zW!wFT-H8^P)8nUE|GRvAo^_jOsfeKD>Q@`qsCi1hP2%WZ zT|KR*IjiiXMx)weR#TSu!KqSSac)26Sa}`X6JV&h?~2PxuS2XXd6BA_raq@af;5iK z=Q@%va(~Bn?oB?70rfN6F0R({6g@EEYTYHKc^b*iono6fTX==9*!yHu{eA!PEN54J z>zgbq9*I>PPBT^%UDXOUJ@(`E$j`X|gZLcmY#OSvPmLK!2&yg0mb4=tTC_5aFE#Vfs5 z2Je!*!WlTp<5;_r!q_D^29t9a(v-< z{w8jZF-flQsa9nh{#B5Wa&4-=}{Iy@=rRA-%jv;LQ)8a>q9#$^feCuHRQLb&`OjPuTV9LvQ0f={xFQ zX9PU(@3u^NruDf-LWS#+><24mSyoFcru{R5ix+fD&0^ZCDIoHMll7m#9}|~9UmNpN zWB;8J{`=-v_J^08f3`|(`0(Dq=3p#CW$L>x|0c^H*Vi^l{=R8j=k2%taw95qrg)Gk0%&`Zn|B#EVxx=ij+}sc?Du{R>yS`_J6} z`|hIuwiwTO?|&4$pB*b(zvshLqaD80-;QjR_rLQq@#pmWd+MJqk6*L@UYE_cpN#*b zzRg^%{{Gev5vk0uKeAdz^5$v5@|H$D|y`}bYe@suj5)MwkKCX^f%Jm~R}%OUz# zdzNwbvWt8cF%6Oq-v03$_7@brxxMlIu{@G9g z9&Y9E?J?0@8eo=NYJF)<@Xb{lj2w8>ef16=-PrrK=gaLISDLmotvbjav?@qK!MEYZ ze@l-DU*+W(Dtmq1li1MQuU#IJQ;WKLxu@DZJF{r%uFGQIZd!zMJbSIW($zM+Na?@s z%r;BMvjw5ob9Qtbxxdvn_V~7_bD^QK(*L_>6`0}f_>)x6JPXBmODDRFy1oVQde}fgWvw+ItiCv>wB&j%&_rh zxi0X5XBA`E8>gN*JqP9*2*vJQS#L3Q=8Pj{Pj8$#DXF${3F8zASIKE2g4YhzNg6!% zTDCG}>d9qEf}TlzS~`{IuM23eV@QkiR9>_%E?ltKtYZRGt*XPBNld#%<<}_cbqW;k zQ``I~^}rgfeu4k@vpmB@I%Yb*6YXGF({fAUksy=M)D*2!e_>9Rz@Ui>l(u+!ZC}5P zC1vI^(L+L>zPgVYEhA6s{kUx8a&e77t5bWvtH{J71-Zu`tkdREzov6YR5|8Sl1Ga- z%f&Zd^DkV~by@mWaijm5;vZ5n8_G-+wEsVwv0UbK>VBsvox6{hAN?uCt+Ms%&O=)+ zA5uF|Fwt%Q1&hO5&3jrbR~<-`-^q7NcVTeajvKiP`F1or$hED=m$=k&*K2-=^Y6)# z70a{bm&liF$+`G7uZf}2;eyuzp%xA^0SBgu4xio#HkJ8{?frdVU3V&n+2#L}k1bpE z_0O03ryORLzXSc*;`Yr*pT1}Jv5MDj=i9FdetF}_f8cS7t+jQKp+@-f<>gh?hUdjM z+<9u8Jj=WGo~qvL)!oMaZ*Fav#M39Uul)9ds1LWF?wfnKxLUU$&vZ^@;g^--=cN1Z z?&nzYi?z)1b%`=gjZF&UHlVf9lA8>>Bsz z_D_xb2ah+b$)3!4d3G?T)k3Mq`iYl2?=-Jg`H=rvzAp0uM*GlHv7>t@3j@Oz76t~; zweHXh+&x_@^gQ#@@*zFXEiB&1MbOl2ADK{*hWBssUK$jfe#1QN`XRmiCmS0k1$o|z zOgXqVAkn+iz9q5jI}`I}<8+h0ETO9=z0q~?)r=&QrCa}PP&IYx?KD(V z@%8b|RdK72*eJEQ^I-M#g&Ni;XMVn0e17Np;%9!>Cm&2>Fsgh1;dj;FPCrqG02A}P zE(Z2VM_N{V6kL*}q4PrHkiWSE zFKIjTtTHsXM0Jga&3^x$RY5#$A3t=dxOv|S?N(AVlmplfo4T^n!W+~=EnO!+zj zpBVg@q2RaJPsY8+ns3S58=Dt~OvzsVvHoGE;u2-^+&Me6mhS#lqB<+>jbQSw+Vva5 z1GvB4vU2%rVdr0YX6JTwh1VH8nVG9!OcHTC@4s1Ur`PN)`WI$y+hyl^`Er<$%VH1b zB8jEE4=1M^l}~!Kae|K=$HKzIsE|idS{r93I!%EA#*<;kg5E8QDc)^)y>j`|t%v1p1f8P(-26HH`AW5Q(lg$Q z9520LIop!6Zzto`1?jtyi`*xW^UD%iH9H?|=C1r}ZgZ=jn65;E`Xra3n!l|)CpczCAb>(r-nj?O9ZpQyY- zVP)bfgHW@BTs3VQ4xKc-x}j@j<5iK^t+ibxrLVTUoHeCfcfpHWZ%Za9%H1+cTBUqg zI@5@`;dFh2h?Bx9q1c|IEv;8PuE{O?(>cYzQk9si<1~`v0c( z>E4eAtV>Tzdr!W1c(rJMjHs~qwhHwNn&01CmEWJ+zd3gG^BQK|;MLpjS$+O~`T4$I z2h9Ta-1+uTJ-dDGdfm8+cisB$_eYghzwJCKzRq4g&hFQhUs``>ZYuk5;P21ZPt~VS zZ+bU*?dgfPCo3eJiGPb4$EgJemy?5{QDKQ>#w7)yb-Mw$zS8nIr(qo ziG@Eu%3%PwnQxc-v}%&U z^~(OO)_>R-(C$ljSb1onA~OSn3^N0R z5~B5mGN$a5SpwyQCog1}AOArNx#fn$dx$tn%)j~d{N0t7@8)r(mCbOpTam8GD!TSo zB}-VEl ^yKh%izk91!UH#m)RV_)SvxCi@>*3_Rp$ZaCEdoCyChB`8L`~A4>iDg| z)@1Ycl@plv|NM6*fB)}qw|~ZdzPc@n!6-_)mUp`JnLc9%2X%%8KRWy;3U3Omy=#)P zb@{H9<_RvVrBjV%tz81ccAEy4lpVh^K{GpRO0UP)n~J)NPIHy9`Z#eP^N~5*l@a7~ zFmj^9i3^MLIA62zvHIwwCOUJju>EO!L*%l|YL&Gz7excl7%{V17Qbjq%Tn}B+-=%y zzd$qPd0BPwtXUFU_nfJ4=zM52`$CrZs&@gZcXU@re3EQSvnW(*DgDJYbH*{t-?J2( zGcKw4_;fKee(@3#5lV5I!om~Yx8Qj6iFv8q-PirDng60%K56B%o9lAnq1cVP`yw2k-SUh76XdddTc1Mn0oMu62LyJ!o#|j5zvcXL znHSy>=ebJqzSP}(o_BkB_h!e>KUsGE&NA3`e#;59wZ={cb7$Dq?SFV-lDYZg$9$Xj z&B^6hs>#0U=c#G)>uk!`9j>1clJD{N?X|tFP1wsx018ypP@f zjm5`DnWy&tEZe!_^Z6q_*|&ck;xZ5Vz2?c+Z;$Ry+7%pqyLR&8=lg37!oqLwl>T_B zc>9rqw?(DjAKyOzeBHha_Mu-Hwqz_UtCm~Pc&94qS)0hF&3k)TZd8OUWegEK5y|yk z@6RloT^7a`vz@wUCwdjI73M4GWg8w||Nd8EWdS@!l{V| z>a952YdgEGRu)%uY`Sy9O8mQs*0Jov3tBl^3-%=BUwnV)Ql8~=!}Qk8LLa(h)_8ia zQ%m_9_~1x<Ip^o(q$ZanCgr5+dHU-GmlUNY z=IVj-R|RuBO8)ZAx#J=-?_-Mjw%Mwi%gW48>a7iJb_pqaY^Eimwb1jBnpfnd(2Vt8 ztKQzcvhw=oPgR#~7j~^;S8-So#6L~zRHv~xpNC9*)*)-V6=KThku&|6Q^1TvNlYjhA^dl3!;% zUTm?*FMeCD{TsvEVO5JS^wx^;1>WCn@pjX{1Nr#+T`@E?8)(v zcfS%}osrAGQpTKavcyu#Rp$}=)^j$e&cES)msD^?uCRLQZIdIM6Si!YkP73Bbz6FB zGPn5kS=QHgn@H6bz5i#MG10f7#N)&)Ka(j9xy%R5Zq8(py)>7F`Q6J8d=r0sKh5HO zi?J;}n&0Acv)A*F<@Nu*oPYlzdQpFS;wu~jkGoD$<= zb#xpm=k2hH*LayD%{E1RQ^tne0@?1Ax%pn})XLY*2w0bxWKw!EM=4nSRpyi}%Xym^ z9`HYzwWDY4KZh?Y9qxP$r{0Q~zm$|OT3xg{^=DRD{qY&bhw}UW9}hnFY^qVb$=un~ zo~G|So3d|KA!qz8iPr0zwz|Ze{Glbu-n9I`ZN|@y2R`g#@qXN%xBT))tGQ(#xcA(T z%FsP~g^z#o`{MU9a&^CEUi`73Jo)wG&3TeFag*ggbLu{m`|N%GBX{nf6ol1^x4w>l%by9? z6)U8Dq9)GPxsuhja?i3o>-H?v(JX!H`&8yYFW&5hC6{ z&82wyZW+bgqq{oyswG7^=QwyJl)uj_y18Nd?z?waFaHq`{Y&9j!sO*sgIF{*B19Y= zKP+TnY5u`}Q9XW9(~k_Uqv8Aa-k!AM!|eCxYd-(Io&W65>D8hwlioSy{yS8&p^Qs$ zjY7-3GgovrXICu^J)CD4ciE@<@I{lv#mVMOmp9g4$~(trGG|+Ml#fZ%0pr(m8-A|b zcKJ<&&Q&w1d5^n&pH;0%WLYyaX&&o3wrHITzszi>FIx7q@p>l9tZ9A9*WUP9Ki!zN z{9wn~IPZuyc@=Y?^Zc7O9`!!6d8TQ?t$nAZ8UyNW6K3V_JJ^vJx${Qaz3kFkXZW(_ zJ+EMTZ8k&t>D-iwb3QFvHP7E6W!fDBo%u`qpG?t~Q!|a#bUJyo$nc_BjQ!QZ*#T#` z{U$rkuWPppyz>7Y!!ZV%Wr~lMEY}D-b@)l7(VYA9XU48ryYA}D>pSa;f2--O?(R4d z5h|V%s-(DUg~Fai>*Tn0McIqQJV>eWeVS0Kzhusq?hov*lvqD*p6BuLFV9jf&n*Hg zQm2YsVd2z_UncTjL~G6$hl9V5wlDb+z<2t+y13|~C#4flJrcM%HRX?go7Sp199J0^ zxU_BVUlI_y@lRTi=H#ZyQ@FPrS5K0=rX7-}o|@6HXJ3<^`q78k8XMQG`C=)wZp922 z^%M(7t|D>99wceE)g0GlEdZ&q=xqRWNN?}h@-HWm?rE-SK+U;6= zq6*K`HwA@ir7dN02s?UwA?KbYUB{KQ+;hc`3qC9U`z%oQ^Wj?63hBMJ33@>~6E z)+N+^DKYau$1sJ}X*D;C<6qXTTJ2wwZmNE8j_2It?Qm|}oCiHYT3^|YhpSb6j?i-5 zY`Jnno=LmcT~0I6`#Fc|9%z&dfcm58U?NC{wmSo8iWwq`>=tA@V?SgT@?| z35*uoP9FWkp-}H)U)AD&{EzU(y}f^~?AjZ#o{5d&v3pI=@z1lmpPOFdXJmUEv&hD1 zqn$AG(ZU@O=jXn^F}?4Ht=#+GW7{;gmi)Y&YTS_%bHML=ne+9pys_ox^#^VTC!d}? z^J{Z+zTcI1@8()qR9JsD|Guv2|F2)u*`NDeDu3r2vTXkEuW#0R{aSo~-yf}wCZ8WZ zo3ye1>F>S%`{w?#JX^ab=Y{yb*;Tt*4o5#c_wMgo*?<0Djy}&%Ep2&sQ+;Xb@0j;5PHvsGR`S=4lI&v! z`c_vKD!N|$qUs#@L)HDg#_Syn=e=7m$s4@-a>3DUx@%wE*dm!Du_}1^LARx8l4mz~ z`F3B^G7D|KvMT(`Q{~fF!giU>)jr&HX^ZaiG;OaV{EL@Z_bzY^UGBEgZ~fQEEsH*F zTNq-xH)*ZfKlwLv)?S_cH_bKFv{thB!`0B)TE1_sj!vzzEjkfev*o$=Wa-qIJ!$U0 zr9?MNmiSCPT5&EwvwHIRo3jO8^vSjH+D)LSIwm6Yord#*g)mbC873yH<8 zZyP1`}V-!1TRT6kqf-&U1d=>{{c^15 z{}*dGU;Ix#+t1g6_m6cezxo^~|KRtNnt8JeO3q&|-Z$q{spgOJCoNy5q*a^A8{Wyy zy?*NHr=XpGo~)gJ@z~ylD<@w(+T1MfKV{#)mq!jBRKIxq&T^q&kK1iuKmPGrJbcx| zkKeu@PLH>pdA{cR*_B^oo}5~HRqcM*art*|?$zztx^u(6g7o;z{My3o;GO&bKYi5p z`|91l=E=FOXYyn3alPk#pX=Th9=!6r(8`|+*%{LR)qFH>&|F;hGT_|*qcI6{8`kRI zSy4Spdk)u+tl;+?R*m=1*K7RQRr&l)_48}<_uJLk?AR2P)jQkg=NsPhCVtlcw_SQI zb?WLJE}u-R-};G6RnA{t_q8Oyu6d;B|3gCmKtb&4G@kv2DOz{kre2Kh%A9fPcAT>G zV%>&%2G4BMS(vQ{2AB2U*0M4%tYBqeP-S3XfQ}+!mIJ|wxdl0?#*h}p3f6F>>E_hX z+??Ak5_Zog9V@dh+d6sKUG@s!WBaa{#YWvY=5XCC_NYc|hFRxE!{eQI&rKDM?!4Te zDSY?X5r#&sBhwRCt4)|NgQ1l9;F7;DwJxwQW#)J&ObBRo_|x1Z!E{igywBZy!rpCJj3|&w7iTxNw-~Xqql7h3cVL{x-D#K zc#-SoBdceHUMcEYyD~KDVgT3S(`}~F>yGSj{;96=q&iz33eG&iL69#@$*f%?sybN<^JA>n`f#S-W~`+BTKdX>k|1kE~ia zE%g=a_0UD9FLEDt(Y!Th)#N)blA2bTEt$f5JN}ADX=P$eSWINuVbh=YcYLUS?GmiZ znj0LJV|;$1-D>eEk6c6(Q{42d6SIpv?N=B||Ns6fFWTJ8(K|475`PHK;$O>FwM0!j zb?MqYr>=WK`5&K)t_}&^782ygP~|gqOM&!Fdls$pCI^d{`W*Q_aGvd$_$aZ$oX7o{ zd1={qzIW%cj()oM_`F>DqaFMX6A$i)KEhOTpn+wEh+K@3k;U$mh?dSXD&HoSopQ8G zU7cBF zc|qM#Wae|_3HctM67A)=J|^|J%N93H5Sq#Qe2f2s*#d`^eoMJ9*o8&&-?Y$}%vZ?4 zIMJwcvOq_W*m505$z^NWH;MFm2T!wXayTcO7@O8qvOq00apIcvogKvj-+vTZwJer3 z**kNANO@Ymr~Z>=)1MsWlRK*Qa^qL`hql)x!V2^zn}km^IXZbO(;T&qGv}vP7#?@O zRI{R;U%f&^RrO|s_7s-#u>IEAQjan?)RT5BXMR5ETus2Cb53>#)~Tqw-I>l<&1CXZ z;N^*|NmJJ?zI0T}?*BZ7NYOjfPg&?#iA)GmD!K9NFr(&W*Kad&=Fd3dendEhd(joa zDLU<8GH}Hy13{h)(c&Rc~`sK#J#GJn*Zv8{XeiSI$&4k1jh@8)L|PBi)cRCkrU(gWKN;U8KX7xXa* zT@mJxi+9{`se9ocv;NEF`RWNadX>Mjnx4&0TD5nE^^5;A`fggkd$vGm1B>B#`HvR0 zhgeqI#HwU`viK{n^V#C;)?<%NVFpTAst`*dq;+`lVvkAKeE6Y=19anSy}%QL!F zTIB72o;WaVmt{=y=KlHpzURZ@BR_nMUn-_HU;MVw`*$y+FSgrHJ$`*36O-)vzcmRN z(lb6i+`snMKKU8l^Y?7ooBn=gS=q+lT;GrVj=!^Y(W{m5@&4_Xji;~izRxMXY`X2< zpZ)4b%#Y7A44-E`XaB#O?sMb!fB3ll{U1}S>a>zA>1l`Cqrc7j7+yA2Z^?1bkN&gw;gy81^OtS4-%+Pl{PKaneOCB-?S8wT-(DWuTKqh9 z-`jiJgZ58!{W5!JY~0+u$b9jg$2|7Go8f57UZd5pcxh9%h`^5n7G z8)w$o+`*X0R@eOZ#DkfE!I>FlM~!D*aY1TwiC#!)K@PO3=ghnZsi~J5?3fQ7;mJ;) zZ1vjLNrT-ynnfl;#zSDm^qVTPbCIeKLe_8i@oVA2kxwbBU-}kehXQ%H!`+eJV!C5;tU(4BZ z*<;5J0 z#E%{k70^H0q<-gdqUCzk)rrq;9#U)GsH0M#$j(w;WgLU&y>;#)L0dfukAd)z+Jv!_2Q3SjG+#UuQh|T zysgaES{8Ml(Y2|nEnT~_cIoLOA2uJ3Zojce*M-ODTFZxlv!S`t7rW9 z=yQBYN~@IVDoM87mo3-Kg0e4N(%d;CP}=mCk*AmOsw$^QmA4E_%fI+u&dB_{R?B6! zTenn6Ye2GUsaoj?8^^8Bd8!yUg>wc>b2Y!W;Hs4B>k_6Z)?bt^sJ53CXuj?#lY4mS z&5t^(2~!PMKAz-w^5kC5Tb@VO<-7^ApSF!xgURH8h{MU>w(JU)`xy@G+&|-U(C0rh zH}9}KRTT7d@00p@^Now**F`$0)}Oa@__^|q*E{XE3KJ{uv9m1Bw>e%o<8et7`;V~d zjeos6QAOS#byQr@gD& z`s?lK)u*TP+1Gx&I{lr#@4`=eUp-%O)^=vSlh~DpjZB}v_6FR0GwYFZkwM23Hg~I4 zv3cv(yWE$(x%+$Hx;VZ6mnSoAJ-u!xJEMG;&$GqG$&)*m{g>X5^KD7M#4jiB{Cywt zElVOm-qv{1UOA_;7u|HKE1a`d>2%*(o}4eQ^F2U1jmzZpi;|Z=zaJF+YW}tS>%w2{ zzn1?JpT*hyF8gQ7^_v_l|Czo%%9dM!G0>VRzBs&@iGiVxiGe{H(cJRMEG~hrjdv`` zhYqpUF*TwrjStMe93ZmKyl|`Ua_`G|&fD5IsJC7$J>>FH_3fLKfaQMQl{_Y?y__}a z$+6qF*YYbpy3zdLu*4fhg%|z;N7(O|Ja{c}$f0I}V#oSx@eg)pZWBv;aC+zSIq&CH zpF2B!>F(8^IXbPUX8rV=xi;3FLzag>|?vbdc0d5iO^Ts!Qi_$(?B-4i z?OS|URaWY9PDz-l;rm0Ko01L|t~+pedDHLv$96CZEb47*y`Hr&>w6f7=(24Viys`Z ztW_whP2Sg#V)AWjByWjyXxY4vC%#=iBJ3umx=C1F z(!J|#=l>@f_Wu-9bZ$4{+Vfh^H0p46*%#f$|ujYN}}D*jo9X2 z*kq~87bPSbr6=j_A8WpJ=EX%kfhXM;x6M3#{A@;vc)jnrQQ{9bXj!h z!lRibOh=PFb?0_Gowl?jM>%ntTGtuLWz&invfWP43>2>Ee|CA7=EaW+wcAZ)Zboxk zE$+M#Q(`2pJj3OzPl&nEjC6sFB+IwE^!My&`rup3^haMZ>CJ;nRhw#zrkQNnq`6rq zb6RG*_DWuvjLp93pLTXlna9?0I(X(5%iP(QlXh-8qv*2qRoJVSt25Wl=uqX$EY5H; z=Su(J`auC8Cn+J)C>ewkU0D`2D8Tdyn!~wyqN@r>9Nn zT%Een@7As81zgcJy4yl@vO}J1?o{2u^QtX!;)O}GRvP*kOm0{{xp|k^%t!Z~FT86B zJKrT1{47R))24jZ>u1*%?BAN{y&(VoiYSU*E&5L!yu2^+NzhXn zW#c;%w`x{tXbN(>irTl`*sfF9kZiy0iIMvLptOlAKIQqh`WI|E5t#BT;z^8v%pJ*$ zyq`_`Jqv!!)opT)TKDT5htTt|pRW|+AHNq-sTVh~d?0Sltg-3|Q|zQ$C;7xJOkZrR znY!?)`B&##BP#MXwy&N-YqS82oU zuEs;Muiu0Qli8;kwvfA79Kmga?kb~YXg7sx@Ju+m6KV|emq>CWA%ZLp)8s9@sBN) zuN1xeTyr>cU*WuPp1R~Yt@E`) zf8X74hTE)dv8!F%FR5BS>1~Ou=e+)~Xq!7!PMC3(`HuC%eNsAGgqkk$9n4s=bpJ$G zuBT3K^J4@=PS~|x+xMvbs6*qW;00gw98PIErcdQEp2+lN)x5LmEowoF>Q2mk>%`qX zOROdBy66H%k-Q{7V>VtZiS=R}kD4y9b!-a9^A=@bdfhTd*(fZ`Humw9&3p6 zy|p`c$4I8nU}|TbEiHfKmdO3^mL>Z>w%^zms-m#EgMIb^xjp|DUR_cZ({SvS*UvYP ztU9w^GWacslGt=%R`SzFmeS!d>VLHMw`xD!c+)9cF)!LxEI06HRKJweB!xwDb#8gN zE1bLi!DHg>#D%3ZxHZblxMTw=WS4wMFnr%|bkE`U&0IWA>Kt4vZe=QAJs>g^L6xn*u&!%Fjj;Ktp}MF&H+dbM~ko4ihJW!&AIP+st|N$?1_+XNj6 z+YJdU5m%Zow#158uDem@DLyZLV}eO+?ZFkfe=N`P?`d-s*rT!`PN<7voynUFfj38{ zUdvv{`@n(S;tqSBr|2euq(J+pO`8|$I@xdEqcHoBA79xUjUVrDx+Eq@zWe=( ztEb*GzO~MP|HuW-iJb}OEH=7NO<;c)YOB1;ej(f0v_yp@`AnfQw>Jjo8GJ6B7Eu>) zY;+MxJ6Th@^yjAO%auQiY%OvuZ8^Mp>+#jUs;@riXnm!9^z&SWi>>$dTp7|nwH&+3 zazah#N&&B+u!|43X`J(s*7PkCig*QMW^Pc++0*8s-{HVAYn@_KEOY-X?W5h^t~nOf zx>tfU_ib&__4+WaMU*{seY|Yvgu~u3rm7ul8P^@Y^}47+c5k1c`yocXZMV1%t>x!@ zc>3#xnOB?}T@%w8FQqJ(I#twiD|MlnN9WrK8qFqw-!^2JKXqdG9x_q(yp!Dx1LwJi z3?xkVnEY=q{Kr4}&wcKn{5N-OpYs20__KQJlmEZ1KT)4*_+OtdMEBW$hqF_E?(cl^ zUv1KV=cRw-*{loyz27`RUSP z9n}`BTim{8n!x?FKO!u%7Um}OaAmxU7jR2#mTrA~mDPTZ&g_;z`}@p3H=ekuCb&JQ z;XcD5-;gfg7jItlu5ce?NmJc_$%hS3^d5W;?LDYnAjB+V)nU23!;-DQVTRnn4=fQ` z$sfGkH!zF6l4sWadX_<(uZ;(m4c(z34^ddu>peOg7c1{S~ z9>yzL&?vQm{q~g|-n|u972fb(4__s3ui}!)<@O=+?9+CakX*+Yg^6*qwkYh{Tg1`X zy-BFYdDpx418#Gj6IACo<~7`Gny_ReYoZK;%=0_xoM8-h2Y4@-b3PVa-dZML(sn1k zFyx1v4rAiY7mT{XVnL4H`Wc=29Kv^+FWgazk3HI9@mco@vm~GUC6*Wd6O8lcXiZ zs!TE8$Q9zniu}vOTU2}|>(s@YaBi>s5v8$MWlG@w1)mii(A6qURf$Dekn`*z04NUX!U+$!2NklMH^mhJ1xcEc+6SHswG>} zE~aDq-y63dxmog-sjZfnc_p+XB=g1T<5M3VJtQM_Bj}fq3v>8SkvGwwG;f$+a6PXg z#x;2s@8ns&ks0d8v~m~5Hh4+ooY3LO{9ti*r{Iy)dHDuEj#5uBj*?_1aJ zs~$058{W&6-V69S_p_LkO;3MXB==jksk+%>eas2kU%f3#`ZZ*$ROX^+Bl z_kTO?9P50L;9Dp-XX^!xj2qm~&N;5x$rZR`uB*H57Wv|Xk8aOyv9{Q^t*eoLIrCH= znGZi}h161?RC!piSr-&%roIvBJGlIoVL!kA#0!nhw+w3nnzvp$?DF8SuH1(M56_%G zbJ+2%iOdzbZ5MivoG(ssWy+9mvpCcha`yV9P|ih_Qv0|(_ugULn!a$hmg|;7PV5sq z!kW$=*C{b(dsO~`m3zOu#qsXzDoRD0R|Gt15?B9lEohg$qxwf(&U%J`+3!XC4t=Vd zG`Vp?!-OYVk$fIK>I$8Dd-gRH%iOzs#7ln8l8?S!E#-CnujK++mi1)4Xv);C^XL*f z?sDc1$Kffm`uCJh#%{7)dvfy36r*AnfB%#1TP&UZp7I9wsrL5REIb+ft7qD@exJk1 zO6%2yBedkamm4qhd2g2bvm?*%^jaHxqqWjgR!d7(XixT6+b}62%W3|p^iORv#vgjB z{PxwDYx*BgGpT&Jcj=stvnMTcF855E94uKgH+##ciEG5Jt3Q7d5p^vsIl*S1`()$E zE_)2~6HXuVzMlTLO=-5I<#NfHDl5!4rn+jE6!-0a!?4qI>c%CKr+qV@rEY$bR$rfbEXAvbiTo{nS=RS?hA&)sqeinADi^CMuq zzwwiTB{zHTdnDd??WpyYZN36xHTwAuc29#0h0k-El}y!3eCp%(PDy;@StDV;R7L)q zuO-_eS$o^J@kB|QFy1Q4__6PkRaN@QAW8P6su}B4|Ay&qm%bYelatG|1bBf2ox#XU0$a8@$iB6MW@W!-{pt3%Yc6DeWvYvX=BU9%x$J*9Q zMq8@99@j*9XU<92e!1JvE7|VcMXgo)yUf~-ZRq)Q`q$@7nfp<<>>Z9jTd5O#uWV0h zfO(;hV%weFo4MD!&%5<`&-tnSHXlw{`-|+0{Ck7{*gWf>$2x_diso5I-x3YP5x#jh*zv|E2t$%TTpV+M6^4Ri~8{Zzi!5%vQO}15H z?eqQDWxii!kvSS`+wKf^=tmeJyrkN=k8ax>(Acz{}V6$ zzp>gp%YV-Yec!9)ns4o2)ouCk-hH|uzx=-6=QQ3tFkbur*O#B{SyBG?w){C_A7}CF z=h83lg!}(pUVm}=1bbqC+YJ4k9|9T`2OG5@BG8|{`zs^@%H)Iv;R)M`249eyZH^7ZOc;En%mcZ zX>acRwf(wX)$3Q&=k2-vZ2i73YmdMGKYy*i-HpFn5B3+`uG{`QQTg|g_41jq-+$^p zyOy36XI=4V`u!WFnfYh)`G4Kdsa|~H*Ta0dpXYqb_x%n(Jm2cyiwky-zn9xa-TU{Z zyFRj=*>*-}|Ajpd_6e*v=uK>UCNJ0jWkwp`>GSFzUz9{PJxuE{dAY>(*vh{Cs`%eG z8&=0f|Gr^uU;ky>?O$otn=SS~+qlA8dH)?3@p`S^b6f5o<9U_In_`zD_Frs9(%;Q~ zzxjTve>?wX|EB-9{>^__{80T+z4X7?AIcBgbN)B^llf!%L+@SA<*%+*^}Y)W)5=@w z7P&M;J9}zrm&~~q{`TUB)rahIVdNZuq}R*H)c1 z`p=!v$n|xK90S@?b2%#omF<=c4DmJ$3{r>zTIka9(7ep#{1nJ2mr8hPL`b~!lHFoU zor9JXznqXaAxf%jx{%leH-}xjqn$iYE_L+U<(=?y!X&p{F8j~_eJlUo?)kauv~x3! zzuSG^*KyR*@!ti1yQ=yN<_q@y)tvAwJVj#VM)_8~+2XCTyE=@-T3AJydDbj82%5d{ z;fZTg&mQ>pl=Jf;qbDH}kw(vQrTl#NtT-`c{!WpDKDKA~Uf(A7Z_cxw%RPJ&zuf=z ze`-yP)G4|6ud~0roMrX-WVyYvz@Ch02eP8$CKm=tlun#xvudTubIXrsWS9?KbuSN* zj?3C7P|I1Zk>;=RrlD8t@vDtfL)`_U+76b;h^+msj{5U(9P;^rPY3 z>7&Z*>u&Svm7i(iz5mwcn0?pocW+}xi5rk!@qfDcF_-X z9)J1W#r=0~TiC%?K@F#h$=;h9?!R2LV^!X6MZLut)(W=qw@!X+th~MMM)YG=pHs@( z>b8-)E@v&jxOYoM))mEkC#?rb?H(KEt*_HyuywwbmOW9+H2!Mzv!cA;f7Gs>{aCpD zp+#^)aazrj5|`WCG{2V@x^-R=KYKa;ce!%Y{p~OBsXnt5V^~+}c>L9^(|hKfotDx2 zEpNrig-cV7BHC}C`NV$Te9IQ^t=vuG%kFsC+@90ylf7q;e~RkTthWDL+h@=HHCJCM zCTYg}9NS+Uk-Gx#e5y>h%1)d0+4!y6mdmTfJh-dw$LP%5(Rlfo-?NKNjIY;Qt9-kv zcUvmGN!90HP|zOzeSTZ&_nXJ}*L})9>=w5B@40|WPuL@$u8Q5gWX;m}M?u~%{1enV zLJrS-l;5;z{$GRtZ%yJ(&rDtvd*f7%!;MpBf?=V7SMRUeTP%O~>9?)Lzivsp2;5t{ z>VC<~XZx;KK3MvfjdA~#m&-Yy^BTSspFHbt?v52(g<9xK`)qX6_9vg(D;I7)&FswF?-axs$Ug6g184A8?#G6AzH8s$y}C)! zJNVxIchmOf@2N|je>wY}Q0?W!$LaH~9DDe3_i@*&HpSj?MMXEGq&WX=s}x>yAXJWH zzS#HfD5ck*KbeK@Z(H}R*(z>h*v30An zt{?7NzC`QawB_^qQ)(j}1zUF1q(uw;oTq7}WhkZpPfXtau2$%viyY@$7wE&MSXKP({H}xxZdBCufDsw zf31>fp7`5@v)nebw$HdedC}4wPWGK<{HwprtUGu!@J4#oHY43Vrfp}FONwv5P>cTR z^4hjaNXNxoXIq?>lzHxwACK0`p85CM>Y|(PtgF(z%h$fP{JCiJ@pNa`|J`SQb|uF5aY3R_f-cN?dg8eK?Z>y%_I!t>u5C>lopQR8z4jcM!>FEb_H>hCagWKbciT9kuEcJMb%{-hIuf;I#qGou zK@UH%TTR>PU?A)J`{bGxtCrp6TM)fVpCcup;>-meP2&`gW4q?{ujGwOa11e4e6#S6 z`tHc+3aNQ(1n*YVihZG26Wcs=8` z*PX4kRh_cu$XShBT<4C&zt|}g5Z)~QXV<9$G3&3B^)H_jNd9KlXP(Nn&W7EkwKYMr zsj%_#UJCEMcRp8J(6~&+*~Ruq2PZS%)u%beJ1#k;%vnB%@v8uL!~W(E zr>oRv_h`OR7ldKn^` zLWO;`nV$l9!m2MDbj`5)-e~?Vv`Wy+ey+l=hV&UynzX9C)qfN&Tbq1QoN<29 zCa&)vwtH;0wK9DtasKiL*+|Bc4ee^Rkt|nlI2fgi7W=L-nNlXY{pr)gTTOTW|1@Wz zC2#4)bc+dd7Oofe$q6@FI^_-XqIGp*O#k!b{TGEe8Lc=rLr5#V)icCg+;;n+OYgHn z{u~Y$`>){D&9Y#kpV7IGnoR!J&bm7`Rv$QO=i^v=MPs*X*{;a&XH%OD=kl%G(bVLq zB0QD3{&NH8Uyicf$BRvG*=cWA`ukjFDu0Sl7Qf3WWoetN<{qmPezGm0oe#bmz{f-Kj7afI7=UZN`Iv>Y%hS%r)?!M}a zdZ*VckMVcnmr=cQc$ZOPcD z7bX8aiaZ?d|Dxd6wC2##?4FA&SL}bpcl`BD<7t}~zFl25-Boi#yU*&B3;FVsetSjS>htB8O6L+4is`()KXw~0O)2CfG*YvosSL@0e2D6VEUtHe54t#vr`bMHf zwU~acQFU8=#=QDt<{u~Lr5`vT$;FqsebskQy}K9ME*)4~Bhuyjc>1)xIk&EF^CU4BEXii>yEAD?pX80#-ge(2Rf1}YzkOi5Eu_3*f&BB0 z@8lE@e*W>~U!u!4BD zSJI~2)vY%dPTjowytHxnspFSB4eqFW9bkUPvvTs`OCK|qJ`ebHJ?O1PW_(nJD`}W4X zD~spkmRFk)KL7oXxLf&u%y!qkc55!rDp3`EY$dwb?4~7Gl>0>M#asc>%k-H}=1-Db zT~oSIl|##!XHlJu)2s)4-97dXJ6BFD}$muFqX1yFJHz{>eb@WvAS;8-E7Oox5k}o_~9e?MVugHY+wuHe=o<8J{C-7HZ~d z7P-x@s69vVPHdf8^_#3X?7sk(yPNl?Rs{^0{@{ve#R_ zbJpGc2Y9D!k&&<0!+CnIN~NPi^glUChXlB-j5XP6v3Xt1iuZ5*IbR>|vzvSU?V5;fffSv(WwZmrq9*FSK|3mx}M3@-{oCN8@m zD?cG;amtb%cZxrJ-F4{X$?c!gT-JVm{3c%4J^%QgW#x;!x1Y9gi*%mv5K~)mX7W|CH}Cbm$Da1PJ}k5sC@qQ7(~UQDk4m}uX`=h=H5WBv);BF_R$e1< zeu3cm<2;kE25dXP;3|HZlST5`jY5^;>_gUTI6YUWSU$V4QC<75kWt)3k?aY|b5|&K z=to9c*stmLILt2p)qIz8!r$-?%X5s3i{i`;)I!#<=G5#{%UOzgazEwyCkNzNM_n z>m0pNE#;Q2>ehNA9bHpZla*7p-%#(rUbZChZcO91Bc6M&c!b;u*y0#D#ecSU#X>!A z9ZB&Q=l?IgVo=gKWx>lzcef34SuL7@o?p#v0#1mqJF1t)^Jb-7R$K7@`bBeX&7D;t zOoVK(xAfST3yzP84ph#tFoxS#V5W*deS#73#Y#`&M2HZ(6T?MgZ>-I~xZY&Z#JcBD{ebNN`yFTlJmpa(}WO#92>srW# zm%o=DvuV8O%ecfWLz(?x*aVrgtBvLewrN>$7q3;Zc-PbYa$R_zf#*GgF2O+EEf+4Q zH*+5Ky0v=p&WA;jp;snfiS^cTkXP?*j#~dv)BN(RI6hHo)DI4-cGgP4g*OWq7ja z)is;d2YWW2yXmba_f5A&?bYfj3r}D4-Ya&eT)uOi0E%#UVt>Aq+^U+4>`lYLHn}%whYO$TPG5w{P>CV`K&-brScJF$a z|I5teuh8N1>a#PPC(MXa)S0>X$W7kDol}l~*PfA?mwI#ioFCO%9ua+h>nvW2TzBAC zeo*-_Ts!Jq>Psf)FUCF5C4W|`xnFv4%a6I?wo7u4#q7oBHcV^Sx==H8Nz(z1$BMBt zkG!9$UHos;d_yMoNnI{0Ki!g6?`9IRNf%_T*UM^Knz(w;q^XgwziLmoobV|+n{AfE%k|{XSC8lXjSEdH zo@2ds>CC{lKZDfY{ye$yL!yy#*;&RdZ2q~k7rL!$UvZB|&#ye%d1K|Bt&**Gb#JyW z_03?O#T2-KDSL6?pD%}^%|NKUc%$Q(3pjaId<_z}5Nw-Nh+~bLX8j zQRdrR>K-q5h@WTf4L>Q}`^}I3I-2j9Sj#y?MpYot%KWYjS~v!s9XuUT6+evkSz z_x8eV-`PKhFa~eh>>hT>MeJH*vh`a3W6RT?=c-@$ytJ-evA%k~xRCUD-Nb!ws;3t1 zj;@tG8>@WVt#s*KsrUDIh0n*d?aeOsHk3)Lv{Doke7W}0Jb}(j^Nz4TZ|Yar(0JmI zp+UOE$JXEHJ9U5csc$^(^{-c_aF>Mbi@DE#tlfV5NWtl%jSC`}DlYy$X0)xh@c(yL zuT?pJ4$i&(UAfMC_mZY*^EU>s7TM9b+rDsn!m~T9CP{bs&5nFGIo;5j@pe!0tn;Uo z6IU*aEe~*-_HOsu`{8~cZ+}|y{d>HAW{F{S{GI73n{LSkM(EgAWPF^#sv&w{7IPKb zhpzhFE5n0r-%h$;f3jMv@cY8*@OghUww!C*@?W-kO4lJn(GA8wE-0wzW++S!dvS91 zeub9pg|~aZ%{6M{z56nMa}l@R>4s@Vs%?ea0vT@InaWXeE=%S7w(32$j~_o4zIy(u zc)(WAEmQv2@88va^1!eCS?R`mynh~4wm*L7IPd#3rQYq2`_FZr_F5gBFgsl#*_+*P z%Fp9=Pd5FXr?5Fj$3OhtqTNsQ4!xPXsP|5W@+-ZWS>Gc1r@i~r6zS?#vVP`^32Vh) za2Wg9NryKSrB9J#IkutLH?Fd-!BpZ0zvJ#t5tGy-3*$4F=Wm!1bzI>3iRvHzR(oP9 zqN}$}dwMKr`|mS{+gC;^l$+-SP1$bw$XThe|ND&#@4G|FzwYghEKi=^&)h6-EOSUa ztnvG*_{*vKY3nVd*3GY!imu<{;;Xg7z|~uC#iSS0l8^O2eRQMh*;2_LRk?lfB1dp*n>JTrM%1xur3}VmE%BH4`>pY$DvqQS{OGVz|L^ zrPYV$D3$Mj*ywxkmCw$geEYzcR!LKDFFK$UQ53j(h0i^{IA0f5t~{ohrRs5oek{7j zuAUcE@%7Q>5Yrb<@nz9H&UtXdvfSo5Y_E$0XRnAUVr1WzDY{MLoZuYc{GDn6j~?gF z-?_=VXPUwH@ZkKJ8+Oez+;MH`Rw?b-o|$XHj6X_D+S21H8~fdUW{b$P23OI0OEmNM zuXB(*$jhHB^n22mwx_J>r$0|@nz~ygwx@uV?X!ETH?-XExc&mJ#nRB5!+$kHf2 zRq0jtcb)sQ{|Bx;`S%z-O85*cAjV|h}@~g z>Am87nR(iVKNrvBYn5%O+L+`o{YK=Ntl@v&i^9wY*_R(~EGVv!wh@`fXWv+FkhI<@ z^Vk`Oc*S)*rCbmBmUXY_v*CQkXZ*tPOV!u6Uke(v4+=eK`@di5&7su~)4Er<1@$%c zy$OACcEwo>1*bpvBtEFB2*q$tY}nCU{xGFLea8~PJW)@!ho>TZ0ySBFq7@W95&@ciZfn{%Im(=7O-Q?EUBM+EX^{lX-;7H4?o@gU0-7wvg$YP z(o0!)8m?SVXTLs-K-3>Uny(b<*-56PE9p9h8{+ zCg67<_XELT{Yxvvd;BWytvr59W97HS4&9|I!?mnWtYb=2Vt8mD|4l`zj31 z_uhNP>p53R!E}cL2^LEh3&d~R%9d&#DRh0dl^>Ja^Y^7e61=k@JZC;DrZ?#`way|Xn{c~5}ufN@gS?7pUzq{#UsJ1=7;m0|a z-(I^epQ*a=bz|(;j7?Epn-gU_-W;jjd~EB6V~elmTI|?k{v$sze);U8{ktCL=3IEQ zEC0M{u;TPP)oQ*++NM1({$x7${<+UqZgQud>$a6V?7DZJdE@g^4gR=`k{Zrk-8Lnf z|B7sLa<7EN^{o2A@SH6rY4^Ll_I-b(Jy%+v4b}^Nl37;UX7q_Y|8mn&jS!Xi`sG4* zx#SL{AFTZ=8~jvz)+UZ03J)ji&xl>6ckYnKYuW4DRKg_-+g6txE1Un0dCDu!e}Rpw zUDOWqnjW5NcvyDA#@4@cTNh3)>fu_dCEO}3EBNQu^x9vlzZjhp)f>6Yew}=09)CuK z`;MFQmBl~vA4zCU%uug+;HR@V>dbq0NArCNPE(Y{4P>)6a-8z~^IB$$+p~oJ=a-je zu+Limmu2#`i&D`tOkbXPPL**xBBN8?zPs$qtO~_z)1s}o7Zs-6mQ&bu;z>*T*0`;O z$HnG~-mbYH>-n`^;plBStN))i=OpYq`&`U^^-IsyD(jxwzW1tB&|3OFIrvWDV*kgh zRcfww=InFYwBvd4m19eFJ*-do^Sb}}*^o2MEv%k7W%|AS>#lr~3O}{&VDl{*yR@^` zCMte`6E7?`y0Yw(zv@Aul~bFZbj@WKGSu%_q#Sz2u3Omfva8bhrgbqtw9XaIzs}$p z`IUczx09db?Cul(YBfyCoyOV|j}-maP`^C!_UenSd3VMbBHkNjq{38WxCLOGiI%Tnq=X`JBFOx%Np@+BKIDFtq(Q_xy zNiUp&Gm_uwvD+ocU->R3VcFNJyuvc=f#8=v{H-@zoZdN_+3Y`dvG`rQ`2vZFi+v46 zrNZx?S5n>(XLai2p4T@!z9b)IsC+4GfA`G5Epf z_!OH7znV^X{7hFk`ET~5_5a^j=wF^Nz32aBedCI>=pOChmYAJ}lj>jD8*!^h+8G5+ z`*p%2QhBoFqGlh@r}oN?w@y6xWROq3VU;%({vT&HOu<1sJrXk{Ih>|1JlZ+ zKlfuBs@KnwOlZ0AbkicOr;aCY*2eaKEp63F=uf~{H3JxX4hPT zx;PZue3DO2E7iF<=~v|DRDX|-oR4QMWAEGFFRiVg^LWqs;``@6?K?m7^v>_EHz}X~ z&$p;%_Nr8;?2^KNua3;W_4vES|IgAD{dYe4Z|-=iA=z;xc<%Ot!4D<>e?PS=$Ta!L z&CNd~zAXv&adC1H+F{h=zA(}$^rKtS-W?^AkJt7$zFs_+B`e)H(0#{~jcX<*EjXFA zXGBHqWQ!8rQX;E1R_DKlQ3Ky7D>i z0MApOnHLuQy#Mj{`e$jMJ{oP6)E1EUnjbUqOX{T+Trr<=-cB#sqBW6AQd#&dxBsSv z#~lo2yY(s_DPP&GA7S9_rqce=VM$ZY9R2AVWu@EFKFz*(fzdGjNYc`?ZnEpngn4_d zpRcfs_tF`km0p~a;T{v;lDDE(PWLWI#d!$M z&0cxVJpYA~zAtZV_0Q+Y+of$;wgh_E7haLMDtX|_SB_TCA4Pkf%#us2S4%nZ;YQlu z_A9@Z=3I{ytvx&a&PDd1sVmjwmKGGBY|;_#SF+HLi;v!E4&7?<;4eO^~ zJ~N|Y%0c0!2`&t)g8O!BM*h5fWHFnZLg+_d^=~gHbkA6%d)PfY@zG_)EfOcY4vGZ+ z4~Z)ZJ@soA{jy!gm*&Nf5vuuz1bc#`Ps=KSMYUrj#f-P*+R=%v$zjh0WX zzPq=~+j@1+wso6N`F=PmsPp^v6z7e?`@bm)ot?rp(L{c$jlav2nKE{NPMqidyHDv@ z{fYxpj=#@#ZaX&f*Xr^s`KDd_OQ!rb(p#Qpom^IBQZ!dbw$7#S>b#Jkyu<@dM<(wM zvray-nYG?(`GimNb-&bm?%{frApYW#`=Jl5=dY`tTd6+%Q~6zoQ~nVzf=f+a9?ut@ zCUxX-VcFH0OXT(Rt(e!u_U@T_r$avW_}(Yk_a+u~Hoj0R0Q3KTdh%<`MJuwCu^brrUq&hV#U!^9U7gTR+dSb3R*Ey3-WC z;(XrPjR^F%mEgr5aiQaMRbIP0F>C4WYXJ2B|ermS+{o@ilF3If@ zwEE{a!Hwrquc-gu{kw0e&8#;S{$gr7OZZ-gi|+N$)#uzC^|mg(e6@b*kF!h{7ngll zY_ah7v-Pou)90P~ePV)ZwV9XSL?+L_uY>C5PZF@6|I<4zgC}<1{%=b0A;zkQnjcL# zceMV+4At5ru4|rhJyf`~H|0r?s#c84*}jdNvdpHHOiS`jd*Jf(S?!Z^FHb+^Yu{V! z8vWd3`SWA5Kd>*qRb|QFwCz62*()qhf>-ul&!qb}jg7 zb?oGWsatHn*7f&s1{H5VyTeGT@y?CyM*<(-WZ{1KVy(xTB}U?Yi>_NR-7P4OyZ1XP zZtH`)XJR|0Gb5Mv9KW=5b)SDh{etNFglO&ytiR@&?)>|^({`eXA-}4H@7dcDD*HNT zmDFqOm6GH?zb)YMm6O(wJnp4S-FwbyvsZWH0hNsnlWt6u>dU+8YVleztyLmDo>Ojf zh}@=GGAp#@V)*7>Z@T8XFUqEG*3JD28;xfFP&BIl`DN;5!2(@8Mx<|DJZ83~N0lQ+=>}j^-I*SD|UOk7PbPIjYp~ zvF%L97RAr`#~!xue`(CP_9S#Qi)Ev=<-s!M1}%lfb5u{NMIT7vc6fX8!uv;cJ)X-Y z5`Ho@Ol3HjbMlL?U(uh<4BxaKTv1H+?>c!dSmCm!fVGf=0%N+|%*vQA99mt|RrT#B zYiplUo&D-$!=kiLhh-P+4k=orf9#1t`3Z?iBeyF(dnER_m4!}joE!F}{kB6eU&?CI_9UG*8q z?PokSG4i!Jw`bF_W7j5|XYXOO_c+A+%dGd!Emq4Pr}xZWGHd3Y9ip3rYZOA#qC zIe*trw?Zq^Cx7pEl)wM{@6Gz@Un<;7D&y_)q8j^R=Wf6ED&SE3y^Oa$#u~T0)8_F$ zz7@0Jcfcn3N9Cf6r~c8nlDX0M^Q&{`OctK4SX=S@+n0#4dE)z2>!0mSt~j@R(xbio zpC1L!I)5ncJ=fjv`IpyUj!l^LbIbo9t71<7d2-mF{oY+3b={fq_A6&f6{m>RVf0SEzhnCoEAA0^{3~~lns$wKO;GECz>^L zB;8}GivHxuV10aT_pgUOa~}rDyk*tw>im7GQ?biGZ)GvdrPtXPc5d5XY-TXiR71DWZ+kT(uNF5c#Ul=xJchs0ixP75+x!f-JZIXz z{J^LGd!IUWehlm~G38Jc^NR17IL}P}s`!fw_LI4}m#QCeUf&xoZtMJ8Rio-+;Imj2 z<2jn^f-U}@%@wz4KVr+4S#|3Pua-#M?n?(!lQ#Fh;V3$I_;1fV&qqJ56l$1XI?NSh zX!?4y&oL#ty2TOK%sEYJcZS^Ky~Vps&&Z(UE8E7-m2TDn?ULt;ukW4ADPCrN^|1Uq z^#XtUJ$eb*Pp?|HSKU5)nU?W$~9&Bx}zUkITzf0mdUs2*rmI(*1Wyve&3^C*mU3N{AFf0LbnNgcq$hA zvTS~#NAbj!D?*;^{3)a#}AV2?pcVwzgO)P z`Htg_eO6>v`^R>+)yub~)!qw@Z_f)CoueC+EGixmD6w_gLo50ZEa@l?7!E)xEt)dzyHTm zhWYau+Dx{Y)Xpo^`@<^n>9hS5-3~7X-%qK&jo0Uxtvwb}BqO%0>!8`+17`pK+;)t< z|9+LQ^Jdir&+T3$GTRD1Tj2is{<-osRV>FMRD&<4RS z>#m8#@$1>=ELWRxEa8%s)|}hgRm|r$3;QfKoL0QJ>+AkZnYazt?&hqXur54BJtl2M zwPf?Ef8W0DZwQ_D%Qmw;AnZ`;Hml_tXIK+wac+^+etD63rz^{6#wB;zpP%xYWZhgl zk3rVKtANwc_+S!`LhL#Vakjf&e2aa&g1r`%FnMX6@_ln%des}jw^>&Gfm$La6;nPX zXZ&MnHHqJ1{d)p`?6!5MS*Eh7{d(5zW6E8ySX%Y|GaE_grnQ;-*!sQm4||p_y7}go z{1mU|&tUw(Va?e|5nTnjs|9)0-DPGam zE9cc$7pnYAWjlSJ=R;3i9J9X^tK7T#|2|?5Br~U^pYELfZgtZ{`>D*9&Ls{vm<7UE zukDO^bvEB^hbTu{9Ec=O>1<2U`a%b7u^G1chtRE}q?9kO@tT~x`YRQH)mYARWT`e8fGrU}wG2uu4YZr${x9lJ3 z?I_rn=6$L#A%nF&Z~EuCUk~+uvh|#MeE!2N6;kt}ZFDL){5j5FE?;^7cXgFepz^BL z?y$gVp3Sx#>$eqp2kPhH`{jl|@blv9#iV~;Z z^p%!X+J$=`EMMVa%wEb8<2A8kcJb3`z3rkWEDgFe64q}h&!2q3?@rani%DlL-Eh~r z!gfb!NzJ`?T9eF7;(A-QJ(N@5T6i>k;w_6u@e?n6-y!VzEbPwTHCLwHG1&H+{YH(O zZE%ilPoKi~F8w%J?ISL^$KQ&J*Y-sUMxXaIPoDAK+c@jT+4g0(Kg>4oE&6Nt-f>gs zwSXB1>X(=N=PVP9e!L{&6aUqv{Siz4|Eb(}{MxqCbBm{LJHJ0!x#Nf1>94EBwtckv zl|AEc|JF^58nUmy6TjB={6~zhZn2cXzJj2KOD8V=G3nj9a}N4%uPwV;b0Ast|D|)5 z7o)%5KO}5q#h)Zy>$!KIhy;BvZ3cb$m|fDvcYp1<5QkG zueUp&zxsbzH+E4;O1oBY(v5n}z*`&bgBEQ`GY?<1C81rb@{#$(&3(0PBAHiSf0?k7 zrR(mOGXcEL%MN<#{N!GEeEX)vLXovM4@|1Gt~PqG?tRE7?j`rz`=7r$#=KFrSLem* zx-E;BJmv8@^LAeEcd5pwn_jhlFEZvo`Cny)-QLBYweDUz5*8Bi^{(unt=fGYFM`#g zC+jf=ISZfZ(BJi}n0wj0?bnLDPB#AW{1`l;;j^tU%jVqGzE$(D^dBv;EDP*S|K=lo zrQhLI#p3%6!P9a&s#%?Xyj1_>Dx7e?GO?ex&~*co{okN{8A?2d*iV}s*}KhQ#hsSif#n@cExF@t!=$_ zZi)8$9TnVb;-0L2>usK+pttni#`HN;{spN;aIehuFTZ>6mBMK|x9~gKMFL+sg>SAs z^zHea``aIU{;C%-rCF{yUH<&jpA3)G+Lp;H;t#5Y-+jJ7<;<$8>&NmN zf1Wv9z1=`i|JD7i5&o8ExqgSvGyJl8-RkMvUNlSfcg8-R=yR#=tH?w}#aE{mPh0X| zOZec!K;xWr@!K4{&2_qX>a>&;yMKR5UE%HCSW z`)dAb2!Td66Y)d*@0^NS~|H zethrKWNoh_JH2yuU*&$Eqfq?i+M@q1Z`0=2`#g^HUOr{g&DnXz-PODW-)9^9tME-c zz3lS-`}SunzP+Dj#TWg-E;0In_0E8bfA;s6r3=q*eyXqM%U7V&vN)X0qR&4w?w#BF zZ?Ts5mu|QG@Z{#T2`7#wahVksG%#^z8%xhMI^6n}3^KyZyA`M@ZjE zt6A12&DLKw^)Kk>Sy1FPe`V>8O9!0iS$aL)9Aok6z)}6Jx3^4O{=((D!&P5qUichuwQ zDnXgwUEk(UoxxM}ql&A}Ztktjz}c%-{cCi+$C7?n;B@`>36rXe;=~WP9Ca3av{1=t z{)+hQZ#FkP<1hXB_W0nH@~Wvzwq4x3LX7LG!;u}vy{`hFZhY}5O8krU(MSFp-+j^k z=*!gUdsZgN$tEK2PNDJVMl18@MGAQn3qM}J9ON3^vS_VGRMUm*y(L@w+~*%_`E*ap z=L-L{Z|0nB94>FwMcS5U-1c{u-5&Afw4`Wg@~=m0t_FqvdbD=&-XGJ~*&84C?Djdf z@@~LffiQ>kqG|1YE53c@UgjUpyQcW7ZQQPz3G-Cf_8Bgy-SWlJsNOvEVO!~L+n`XP zb3d+}-F#$E%&%8V?9|V!eP6hGaqA%)1$~d>4_iVFJD3)0A7B=`ps=P_te`sV(w`V_reJTGKQQv&!DX1OqIJj|%4aL6M?dGHVT^;^N)jms~EkNOiEPAKs9% zt;zBLo5)=06mEY(%NLv1CHwSnynJ_R=1~!S^4Glg>=O_1o^y z_3nRZ0WDe*9@u@t@wAr3c?k zV4wVGqG{67oFgpr&)Nol+;aa({A86=+g}-TUj5e;s1)ujZ{6kcXUh$TuHN_=ou1!! zKR+AnzPpn3UFj>jyL{EACEs@5%1}VNE~Y zadJNGJ=t5X>A0oRX-`G+rbn@H%0jk^>-HF)f3#mzImSludPTC)qy3_aDmIF0dkkNG z)N|$NPHKsa=!ACE#>WQ){=j5*{_N=ysx{bHqmXj>5TJVrtJ*Y>L{y=I&KzyXWjm| z+TWs^_Q(CXJ#V3~?bREf%~w12^0Dl{dr~z1(y zcdO8D-}JEjZR{I&Zn^ixIyXx;_;dMvHft_1G5(O+E`#hKIj&UC8KRY)nln4Cr{3mD zx7(Rp)BN+i+lIEVgv+;BAD#XwkZyAOT1dw0=7jyvPTWdx{22Hq_5XscN}2g$){9EF zZaVz??}BK>y*$(Xc%uEePyM+%OIrMyf&01Nw>M6{y6JZ@cXrdNU~YRm6E@x-H}m=} z&ac{%(6{H9`mFM(Innq2zsOsjCcmR(Rb1hPm^sfT{LK5h!D;oPy1?$Y6`zX_F8lm2 zYrW~R{dvz$J`6D4d;Q}4W3!Jt-`$6$@#-aS*=zpo%zAq>@#qWBF3WxV+_5jU zrFCPk%d^k#;QTk&zJNc^=#s@#zsq-K-Lkuypq(7S6in z-MTgJ{DZ99)5~p3_{DoypPxA|bb9}fU%$-{JAD=Zc%|dOLRKcXJ6C=*+vp~K`I0zi zu{d}BJ=xd#%Ex5;IgiCv9`5)&Yp;IW(eQrPx$@qU@mEa3gyZLGEi$Q7(>k{Ow(PaD z*Lz>Nm8~vcfAqZ9303<$FYLr|#-_3;tgG`CMaH42$^n^&VXR79Tx*@LPz;f93#hW){$OX&elJ+!a1- z|g@oQBonlrS$y*{EUN-NebaXyjzhpHF+h=nH zMzLdV3ewSs+R{0r|0E~R-p`T}DRtZElE=J>JbbfR3^FI(H7Rh~lxW7Hkup1X(_2qJ zze{h*Zf}@9-}l_J_7-PzUtuS6?-TVm<-ff#^S$-kM7bctzvkT4>i6gN-rxIvU-kRh zjo*4_iy5EU^1C^A?d#~xw!S>UHqD7M?y6=PZB$<7q^Uq** zGUGkH#%jT<)P^-j7gyO#x~}iF#QWFfDy>;h*%qJ7nYgZKV@Zr?=-Cr5vs7mG9lN^Y z#$Le}CV3q%_6!Z!TA{Qmb5-JrjU`%>JytbT_L@JOHP_`<;)FNs=Lr4fES)neMz2qQzqStnJ54L?|~4ZZQOOKtMRJqNzdSnJL2aK(Gl zPse5HGd$gdZ*84yaw_-W)>Z3Pp4@d|*VPy<7O&7;6()7zK=(x_o2DO_o#FqOQ(5@z zF}9^UZpoij-8_Ni@XnTXQcqdk>ixO{pZ9EHQWx!gnYs9g)Q{}*g^pX7=u7##8q2ge z==!Pkta*52Y2P>XEft0JCYdW9L>zP2!0v7`v!TUUCexNLojan+N_3%=hqp>0A9u6qIE>KYnV%Oqc&FCe-^CUpVnz((gjYQsb=1#Pd}~MnYG;Gqo;lUi$J7 zf(rsU9&JcG(x}klE-p01h$1-PJc$?W>&o;P(sjhytqR8$mPs|C2 zlTnw|r!_Jav6(G-b@b=LKf89Rn*5$s8@1_J%bqD)AFNlcQQWpOM=7&lXHI>vB=3c< zT@w@=AA6eScOAOiwPr(>yqEf_Yi*hvM2idy1j-f-8EY;)&-Qm|DyNlT2=PKi;n9Yi+LQ^8lO3m zYcN&f^?Q~JvJO+%qm7ECj{7G2Ew zQbk8`n!pn^_6aXbJ%s)S{;}1#kgOT<{lp%hlq-ui$*Qb5vE+p7JtwuoioHu@X3Fw) zZrICW*~zdvuKkK5i@K4q!N+*l#?@jT0-k#BLn~+2Y@Nh0x#*W{&a!8#ybEUimf_JT zewN5SIkJNNkxFak;{#4Nw5JzeIp?|I@H^$ah-te!4OnOTTTH1nUDT5x?XpZM{ZkLu zmGpU~2JMO3ru+(tR-EM`HG4e_`GgbMjMg{AtJigiBnExupSwr+kCU$dX7x@3;f?-J z$_=?^Ml{WsD{y+^C3o3;q0_Nz_2N0);#ccm3Duug-spVfEmOafLfpF#^QIKEURie{ zG12eFO||ywr)5V)#nM^>XM4NlKICbdU;E{`!KB-V)Kf#=nH07&KF#;6Cw@u* z!;7i|E}TkgUQ?I6cl~Qq?WT0EOrPs}Yiwe9Yk1muAr?OcCFX|9igQ~nQ{zphns)3> zN_!yg-Qat?=*AT-3GFk+B@@o7zhKbw(OM<2N>$9?=;IUKOOay!W`(LBY~sY46IC`> zbagIgNM}6!XysBKXRU+vMqcg#ZNKH`{MW1Ovhbau z61H(i7e}dr)mg>Z#E&Mz{^bT&_P>lc^t8cNHekhSV}`j_=9iBcA9y2FkQXL7kznS&*T@~Ju&2_wu$1w^PG(8E2nph7V;%- zRH~d=#@gDNnyB?+!iN;G^JX7qvV0Oh&e>GUHG@D<;OkMxv67^~N@nu|Y} zCiY<3DJRj^Y=P-f1)n2q#BZ=#tTI2}G~4~cDW({K8=rM|{A9Qu(U3i*RiQOoL|S)8 zme_grGRD4Fq0t3b1@17#Fx-&iKF3_(%eq4?k|UzPSLECgZiS-Y=p(E;-ws@6(h6O_ zF+lvnx}?Rw8uwmsTFV$x&v;dzyN>&6zU9sdM@o2{I^G2yyjP3ze zt^&r$j7<-i#A0N-vm0X@7FTua?6C5f$#h30p;~u`E5r6R4BLIg)FP+uIMdX;D1s%2 zOMQ!DUlr#kQHNWC9~~FzY%7??;8)DJJW}kIW?y{F8>VgEoVz6Ehv$4_jSX56mc!?H zk8jqbeFZCA*Q9?;3Mk~v)m_wo)H{LiTg02qKG)=hqc^F=eF`WP6O!>x2=tDMg8oK!=@LfcfQmMH0?3s#4Gy!5%qvAXG0&I6S~ zv4;y1VvlroIWkXaKJN0!ijSq-Dbyq5n`svdI0unxov#QcG}lGDvnbt_xKTk2)>t>n*= zE%`G+G4%e1i+PV2ZbWdK9qN6wN;*ep_eHkQ$WShE6%mFL4vS7+STRen=CV%2EVH~2 zpChIXJ8m7(6_KgBJS)iQ!M*0JDS~%5o}QUscKYDLmvtM3i&hC&_IJvjJ0vUU!o`w$ z)cL5NOM<&n+rPx_a1(!tUvGQqp#{mcu#2Ou%+L5z|*v0h6RUp=n}q_ zb5A^A<+|QJH$ngXDK_Eu7kf7dmbo17d%cr$c1+^aGr^i1w}d^_jsjx(P9I2vbh zDehLvi$bH0tV_#ZKh*p(i^HZ@;l`d2i{f9u6xw%g(0=dEqVh7Yim|^hP&l8#dGCbw z+O0_;c>;^qb>8E=E=k|5p5Wps~3Et0Qg zKlt%b(Dq+(_FwkaO`rbF_SWYny``u1S1n|iAjD&+Au-9~(o+??6a9QAX6`lVv|PPs z-CD)tnX*r>Upo2cwU~I>w(5tq9NStmj9zRo&x&sq-^>*EocGtyty3o6+G}!r4y&u> z`WyV#OXlVuI5Jy7qE_JRL8H}^9!`jvuqd`*-gZZ|)C{NH?3RX^kMb;b?BqPB&*yxm z{rLVvyqC8xNc<%><%MwhMhiwGf$oP+I#VXZ%Y0&O*yAXF`Kk(Edz7=`Vow>PsNRjr zUmsl%6mm}CS;QRua;Kt+?fn~E<+<9rNwbbRi$#R<9E-?4Be7b%{kVs?65I6t)6b+2 znAJ<)&^!5hX?Df$GwUZi$Mmjn{5nU#H1cBeCKl<74;;2m5a-*XEfJJdXLwbV;S<|# zi5nBy{CZ?Fg@WCq`sdWGiu18`JtkmzR*-ql&7x-G=zV8rPGL>!IcRass_aS0-W7Az z*p%GY8079{%z3@NF|(|2x$u^o_m&>e&NM&X!R}}ouQpRr{q5mRzx6yf$~Uq-Px)w| zQ|`F#h1xx~or|0vN_;W;ShyhJ(c6O1uRo6ldURg=sA=dGoHS*b%QpWr8!fnvCI~J5 zImKaukX=S=fNDUFd1BLZNB$ERt(o(L&V};b-YF^dYeD=S<^$EonBOlvP_6O7WB!iW zN0k+r4^;n?e;{l2AQJjD;)1BPby0pF4%h&ui@rv(M6G`UhvY%KV+hiT!ahR3; z{I7NP)|2;3w(^{E*Jtz1VKg}P@YBVEH8%U(_|zpf1R1V5@ZhZ)^Rmq!es1Wmn{MzR znYa6Ubj*jp3}>7EFtZ+B{ZwL(gpDNQ&*m?|Y&|n~$aihO)+F1IKQW5!V0%e;*!yMj z*H#BhN_2}I+QQetti_hpSfOxmo4=+{dXmCndeu& z3t6DFKx;v!Sv=#c+~i#icg)VkEU4VZ^Yr}opQaAn$Io`ZvHV&;QTIJ-RL9m)QuF<#4C(%9hz@ zmS<;^H&0r9zVz|?zCUxTGtL>jH@Y@6J@+Q_w>kgbG6&`UW(%1A_OU|1`m@XT%+C4e zR~?t~zI3hb=411MXBu44s@``gPWiXZp>6ND7N~x!>9bwF?<`YP!f&_F=aXMw+OuX` zs@lz}%BbllF74uIl1{DedukPZ>D_)F0WI^=+-Z?7bs3 zc3*ydtk3&#*V;5+<#pZP6L;-b{pX`-_O+gxc24teLwGhH+y^U>gNZO|L;hB@TcCR z+V0n<7YAF9-)UduI{kh9@1xp>+?PMIH#Pe6Twiac?Eaai-(NqjJ(PTS>1X{n71MT? zZ?mv2+32ye=)7#>;>IIafAkg0E*JUq+2Q5C#cOYS*K6%{(389K|KP(*f9>bimz-N( z|NG_P=zzQDzm@0K%$!}g=g+B!j^}Od^>;nBEPp)f-s1kfvNr-}%ij5y@!{yXv#-*d z-%ow{JKp-w-E(a*FPr!8_;TtP|Nl={7jaLN-}A@q{G9oJUVS>^|GsSBw)f`i*YDe# z`-JJ0vvvLSgU|n6y|d@>S7Y(>Hg%Py-&gl4zF&Dk`?Fk}#rvO!!?*9b6!`ba>aTs; z`SWT_cGhpL%Xqxz`~2v-2b%)xeyO+H2;a}V@u7WXe$G9EkB7sL_vQZn(R%CRf6LMj zr|!$wJhFYeJ$Tx^pSK;lw|!a5?^plpTkO8{eE09u1HwJo9 zKEJHZuf_U)XEW^V->;90dRFyp?!wLfzW>hrt=?*1{$lO=*!U~ws@FQkzpwn*y7fEn z+7;E$x~r4Vy_f&QG55Uw|Gm2s-7jnF&$s>%dH7Mx*Ogz?SD%}= z>#WDsKC`pt_PaLcC7upHDz6vI9l1|WcJWuIH@DtCJ#r|b6D+r=bsJ#%>6U!-f{B}y*0x7 zd*V-S|Cm_Ed;h5W&pNw<{2!&(e`cHiIRD}IPwYPo|M~r^{$u+m{`2=w?LU71Q2xXG zC-5KppTmDR|B2Mks&A>E^56R3@jvz-{6C%lVE;J&LH)`7Xa0BDpWc6R{^S3>_3ic3 z>-+Z~*nd9$iTyMChxU)`pVy!N-}(R2;i@iMRk=40ZJW{;xqq^-oopLy8#?dhyqWVN z=iQuVIWMdK_~s+Qk0YOCe$e@9Qpq$g&A&T+XL{@Lw=+*qk*vy@__hCfsTRwVdd8*G zXLihyMn5j>-Sxkr>Rb#AN?a)SIfNt@XX^!*7Ni!HWftc{$CH$}?tVp_ieDODpCer^ zy8pA~$LrH$ioWgiytVAD>T{F5%RKeA-d20Nt9J6{+T2;o^5(32+hDb>)^m>4Hp}_5 z**bL%+RS>pj4o!Hw?$d8G#|WT@4)lTIr!#}Zr@~{gG&zX`gA;jCvb@&ry=9}>VIj4 zfg8WPyZ62J{_j2CYv0%IkB@(J;)=lGEq2yo^0qcv^3rJ%=V$XAR=M@4_0P^drUr9Y z&XM#8J-w{dEM7BGW9~Webnlm2>bFImx%D$><00K=Wj{A9JzBIc;gjU;ZJ(B}-WHh| zqw{nX>*hkk$y>cd?b4^|8qeN%HfC|AsL|1#*DNplT`!3_(dLu7b4KooDW_Xg^A+Q5 z9MIrVJs?IK7 z{nM%M)Q>lNtTkpw?n_G7vGxs-in*4pKizlVrfJUM*NwVmO(xtZGTc1l{JPYgshM{3 z=lZXIwU$jrKTKR&*Esy_EZ)|dqw9X?d|s0o>f=)6Iq62$%wmJYrXVSUvtn5@mq;pF z-AmF;XPUl!XVKNXRj#jhF^N>@md+u^g)P2@{fz|ZGvR%K}e}_-Ewz$H7_Uiht+rM64UQsYz zTkh5V+-#@#t5@=}mwbJ_A*1c%UxBLVl&t;U4&NDCBQGY}9L$?Ib!+JzyS&)nU+3;V z_9*zc-|Voj-TXG67*;1XKU5KWY{?kYi!0pzT8^CGv~;<1b!QRi$&@c zzG?c&{g+nkJrq}ZTl@w6S=BPUDO!4W#Kf}3At-dD)i6Y{$%2PC*hqZD`iDe z8t(SkPIS|0xU6d`$#7CMv0G?|>R->q2G`hgrHdFh+`TcGX^BDilvzAkLf2Sa8N1#H zio_l<=G4vBa659L@S3aV%lQmnEzA$D;J%Z;cIW+Li|)$h*e5Ek) zg#*LwY{oYUf%2kGEY>Cq`ITNYbNu?}&oV#ZtF;D0+T8}z3v+xX`Au51q&@7n({ls& zU#A=$FG*bF;M2BP_E@0AovV7MrP_>^Tf9~tOfs%16V=Xj6rMY*YI2f8s^-Q68Ih({ z*>mpn{gmsxf9iYF(Fx7h8jqT7xN%ddi!bYQr}2Yqzaw%7!We!#PE<@<%j2)I&OoQj zVufrE+d++6I$blaJ6wIefNy@y!$}+1cLgiol@u)#{QdV(p4i(aw$zJxFWmm8YAR~C zr4%S|bqO4n+aS|^Gm(8Q`wfi)eLWn9@5Cuo_9UxFco9jO zHE^@%x@`-^wAIB$5wk@J}C z#)=EJl*j2-lpgl*|Jh#mF+6dh?~j}JU;tOI%3+pR3dCqUdHND{)Zkn9Amfl zuls%Ng^2ksQH!h+Gw0zr9OPAsYpay-NL>TBk8_m;vHcXveZ7P!(~ z+#IKGyTT%4wspP0GyYwleCDy7Tu*rzV?uosh9K5iUCNfQQrl z&SAM+cPXKTXV?!+m=n&jRT7gKiT)tJ<)RV z)@Wy!0yf!|o{6>_eqUQBTp}_{PVE>&T9%84V}T~$V zuua;2jX9!SnQ@h&L$=KKT}!V%6l-+n$nsg`(ARcLRj4hSh4)%s&*W@#w=Mn_g1a5x zaWB~^evC6P*`se^{2k{fDiN{|EOyKZ-e&5n_qw?|_0vBA4VEL!ri_&u_r2bibe<~? zxyN)QC~nQ=_Y3xLZJGP(3**}b+x|K*hw%OHpEHCT-^fe z*FSc@`f@nLuOg=XzDr?}L*asBKP)$um9hQsby!?t=UMq__L{>p7Tj<-zQD$LV;Qq) zfc2s3OrPDalkWHye!IFhlO;p?si&UEsceT>6}MTt^}6=Y{S=pV-Sy*krjsG|K9!5^ z`&R1Lo(`!uR$X-2vvSeq#!oKGn?I>MGh2|hNzxaEU`OE!=S;nq!CA9nl_8)&~*2}y*^ij_ z@_QbalFQ;S+k5l#^hXBQcYkX7d20H1xqk@@C)B*#dRX~i|Gd37etvr+fB*OGPkXH` z(}e#W-?!e#e183|=kC|{SsMQMcSJ7znx@^)r1z_zoB#c>_I=#jNQwH&Kd;|M&epfP z_N{g2^y6>mt>*uqd7|RS72|6AALrKpPq6=b>-@c+p98Zse;x|HtIl+z=5f-$ljrR> zeTll97C2 zSH*GjpC?AS8`*n&mRnluoWq{s>lx4L{!n?!m%D#i7g@ePX?W+R*F{+o*R*?e&t6`z zwXgkhW$L?r{kdJX+cpT;i)$Z#a_o0!)`^n+dY7{0*Sy^9%zegXAIE+EGnHv?%{_n5 z=l%8k@BHBOtCgnf^;V=`Y1*|y>~H_?{qO(p{=e(i!e3X;ubQ_1_WBjKLqh*At?qA`4Rnv5-T)6)){-5f9+W&g~4-5_49TL5ERr*!cWoswc+j{KV z!*<0n_b=68=#1(V>-BcBcjF7!rDlCpuqEa0dqLnB4i>7M!u>aLwYiB7T#602C zs#QwcTtXzav82|kFz7n2_n&xpihP^M3unncVk=no1l?{jTft>)w{gl8g_cm&N5QO$ z=N<&8x;BZi{!+G^@gP`%ORT2IC1|0Lxai-_MtoNd^bXDy3*>TkH9a}EXI@n3o}e4j zi#0mxX5U|3w*)3DSTDViKlteH~4*XX7ITksGn)@nMa|3 zqflZkqwwYl$1L29_Obc<&DDv1xhm_%EvBg_ZrjR#OW?c5TOjthQv9^>4z^bo4Oc(& z7JC`rH(K5@;YiZmJ)D2PWp3$|WV<1F&sX_tiO|Rf{$-@Pi zIWdoY&b()s?X%#b((z9sz1e))$3Nz=e=^D5Gqv~vU%=Uid0HoP(=x!@y{t?5bi%uGPyhk_gnZ5lu z)9%+YUnM3k`MOY)ZLNyB$>JW#6}jy%>vqX3!*eE!-!93REPbIYXlvo$ zsmla;l=inA?kKr+FtoVLeCngLO6%2vIkL|WbnxuGdnKqraHg^E;{F7eYdwOwmeI=% zZ~G{@=SzlmYw5Og_8Ulxr;H4Xu2<_DqS)G|{pdzpyEI?|JCk{Jy=VXf zx5j*DpZ|Wk{+hp!7v{Uy|6Ew~S-O9@xR1xOWl=ZHtG_LKey*;u@DHb7iQ~)K`{Ea@ zujGAV-lMH^RHT#b%=yLdb6);=_Ug-T;d%D!m)N{}v7&3;^>mHm`R``2ByVppD4)q0 zyHs|8SnZFA=lKl2>zJgmCx1FI-E&^aq}3ayR^>(EB)@DFEWcD=~%KP7VbMg2-Kv6~w(u5>Ls5V7ty69dCFCI$vov^v7I z0(@<^M`B(|PHK@JQY~?fNfYUI$zc6xB-c-2idJ)nnW^mVs#I<~ zy}hF9uj=)L=J_U%_oWi=LntxHv!mJxi ztIMB$H+Q@7!a-*Bv85lSABS|jWVqmL=yT-ojzqC9KNw1zI;XWbs>KP36nzd5mzeeD z`sR)@ea2UMmm52F1PBOjjc62L&h>P-BFA>k&}@clpMcX9?afo)H=Gi`tSJ9=>jK9q z%`Q{AEMn5M)Hhn+Y1fY3F!MpTgGiqPgQUN#@VAsee+`zC%)d*v-mfW`zGTL8#-`o} zDpQ0roKzFp8S^d%vrM;SUN-NnNRp(JUQ7P?Tdb#^O}F>?bor5KcB3c@&E7h zkF$S;{J6>efT3|`b9kBcvfFZd;xh|tA02t#zwWGQt>oM@>p8#QtNXJd=y%G`)a$RK zgIC75{oYt!s=O;d>qE%7osZwoG2i~P_|daN$1W>BPoM2(-fX+TdhUzuzkaVjA7*s! z$r^cKU+=DGj4~IWG$|+-e7pYWkLUU;GJAJf-rf26)5eF1W|x-*r5p2jt-iF%d#8SrRA2{t>8JlqJAVA%G36^`A=@8j+26{Kb}*y2puSwq z6Wq(hz_5dffk6_HJ75%Tgzc7{(H7Bjy9|JcDnOktT6cK z@Q3w*enHyqpaY)!W>)6!`+2W=vU&G*tzw1~zqyw_>Q#!~&BEZr$YAtXRlMBbtp4w{ z2R263T~t-`mSX$%`iKGB>)iWEGk)ma-29wf=Mn3TQjy7qUD>(Mwg|~>yuEJn2cPaq z(~_(iOLZn&mK|ES?xne#G@tRVIUYe8)s5K_oM}OI(|9xzk0`B+Q7?H?p||ozPmt7s z*yjdnZQa>y%d2fJ{<7_wnQRi`6LTktP1%k2x7yzMA8yB;T_~)6N_!XYa<{W}auY8s zRPKyb-FEnX@_c93yMjeM#b2CFStgmSK9r-V8$a>#EzgYQX^KCIiYBc zbcn0L%F?-iBHTYE8hz4Qyxn!<={SWjl~+##EZQto_-yOgC4OK2SU2ghNPA5SzxUi^ ztC=%aAFnn%X0aurdWv$XOLW)wAW7qe#f^`Bo-Yku8}>G9Z^?IPQOs zx3j4Dr8B!eV1M!U8%s`aUM>9KtG;Ti+O!?F)tf{sSATkXbeXnM?DQOk{4F(!4)Lts z{`=zR+}fKLB3EDW`RVg>9>D@^+2?lcZ1kM%W+FZ3#pw={KkEw@*NM4!?%@CZbANPT z#=Wg(7T2zyRbyCsZfgU#Pd?YV9l~>^_O4IKIb-T5`FoN5`KEgkec3HhdXs0L@_n9m zfaAZ^^GEY1m0SJ!pkIBACHe9*p-;;S8(rw?qQ(|*OLPd_hpSwHq+v=k7wCQ<^k2xw#?7urV$WV*(&bnBx zg<@+3f}H2fy}MI4xIF#ZP05Hy+#3}yzHpA_v09>O=kTCWz&_6OqFPoDC!?z3j2REQ zRUa-@{h^aymVd+V)9&x{{#&NM+i6_@#PF7T z1wIlV?JSgC?`CsKFg%*U<}oRLNyODnDf%bB-M_zgdB{AAu&3+(U)|_|2Xz5LJOT$n>d2E zZBSHQ@OYM+K-KZ${x<&i0gENi=`#x|Fsd+ezF?lmc*07SdFF@TTwjZJPtImCbP)UJ zo7C%)6ZK+KMS8>;X;+^e?p`l68HK97oTl^rDBE$p_)u7jzIu|NUG?X^hNroc?O5wF z^si}6?FwAEW?#LqevG`t?Ri!kw|)D(?bFoz+t#kFom8bzyX;Z@{{4^Z=2-su=3{DT z^!@I&zrVNfi|*gKt-8*j^3x&qwLj)q7FB0P)>eG{^W@Kyr=qp*9*K*edu<-J&t{`Q z%&uKUf997ze_X$!Tx@dm`6os5(~BoIa(TbKwei>gf018ux&HpGxUQdZW~FRw`Mc$% z{LbwM|6Izk{W52YkBi^dH+_l0A?NEOR@~WL{(Y)_{j5J&weiaX;!r`FSNVwhq_2_-fQu<+s-{)AMhae z3k%cLH2wgLJHN}5rrxw*VPG(3VPH_fk{f+Xb4oG`5{pXo0!vd%p`CbRmNUN*lSRHE zu|6`PBKtO%N2alD+xzBC8TSo~O{Pvk37V>bBC>U2R{|U~rm~;iU|qb>xx9SYXXb8B zPQ8OGaJSuefJ}~;3Gce7QQ>PPf4*J&Xe4hQKyxCZmZ1KP3sbu z=5Bwg**kk#zTC@vwd%Ir(+P`KS*?4Uxo%b1PtPTv;`CC2?_SPmO?wsc z*ngu;h%^7|d2`d2udZy*y82&RpeZ_FW$x^4%1oOt-ST{8Wpm+-O-g9$%x$Zk?(l5Y zE)CwA8MS`POP#H#1!_N;}Eo~0BUOy4$VLi)=9Zl}Ey#J;?<`qH=H z?N+88>URUczCf)zc8U^ zhhS=nyN}jP*+j<4J6u&6&KIi+b*xc(r<>Z)uqR|%%kCK8j;7|z9A_pq33~}l=a{e3 z@_U&IyHdT-yIIwu!YzAseOP8HaCV&WVDbnG;K*WMWN?W)rDdX_!sIjV%t?li&g3VC zc&!WJIZSIq&PM0G)&U`e@o8e(J z>DggH&Z$!rnYNvXiI|kKuuxEST7z!bY0njoo(Zm9)7+Y#uub&jV&5naD_dUTDcTQj2RSWCkE$KN+@>n`nh{P^}+WzW9*`*T_DM?I_FzOqI(du{}O z;A?rV>ks`J?lgN=^Se!OaLSzSmBJy^#4TI=Hu+7{{1cIH!WlytTpUCd7&w@}F>E;h zoq0#B-JJiqG6{Aooh;8OD6p*f{5`w&T-crKXXKbee($-({wbNgrh38s)AFCsC+xa+ zfA#m;z^CcH`TSCPpDV>bWIsH#Iequht16y*i}$>5Hn&?_o_}Os<<5XT`q%5z-afd% zENox4_eY6)@Zo0*-R&d)n|Hb~O;6W1t*zX>zWk?p;$Mr-zs0*AmR!ybzxV&gqvz`9 z_tyPB`&mDyX6vWTTMzs<`f@mVzi!PG&9r;fPp`5iKe{IFKX2ykH*-4Y&WN}nVX?*J zuB`dt<$3?En*Mp4cJ}Dk*}nHLcQ${za=r88^e$t0Igx$0Bf8`H?_1t}&o@iz^!AJI zU%#96$ZkF3o3GjTF7q09{M1jK`P9Y1z2e%*>8|q@Txfm%U#>du^>_DcOxxGVMD8;- zo~-pHQsZ7d}ZydB_{ zlV2WOkeck9SdyFpX$|+X75zjMK)qqUInu{P=RKY9=-mwOev2h5>`!Uy>Yg-Gxu|$q zt&pcj#jA)ZI!)(w0^`vxrEoLee^YmFdAWr(~bD*8P#|SD)`Rw4qVWy z{6vUFgXzjU{w^`MMg?~HSKA7LMeM$uEt1^X$shaR?Y{5NZkyk)&Mzwy^P6MSGU3zS zn?GkhHvV?Xn8Pz)xrMLi+;s1?LCLRN?ykw3k}bY0jjL>B*xaZr-G;3(=G@0WmpxpQ zBeC?A+gXDty=hlnBX$JUSVgP|eYJFx=cjFVHP)KvUVgReO4r#CV<$Gd8E~%P=drxQTrp$LuJLnNO1rus&W@$r$dPde!-y2-mXI#0A$vO|N-x z6TNX_x^8f$v|DcTfj1v+Z19X@CjY{(la zmetRs#ixGl-j*4$boyD_{;P^sJEJ>ZI!_LGcJj)S{gc*5$DXYc(vnJgS-Yk)!Ow zq_!n8_B*rJeEb@_m``f5*-rj<*JWOHzfs7Xtsi7?ztC6fE??lOjsJO=%pXTQuX__< z>ALbu=i;AA{})`^D_#EaTCB`-YpaEC9>m&CsygB1@tDVYrNY&WF1?JLz^Ee+Iu+Dx z1u}X!lyDRld^9Pp2=K1l@lL#?J}kH^vavbKe)778r9183#&&(-Ua%z1EHJ8K zhm+6*d2LsqD;X1dLhm7T?YP+$v;$&mGaCjOp&^BO=jnBYsM)5 zq@2~wk{)lGtWG58eSDxXNj4RdPDlIFR4gB!unqXHa;5%7+pGi83Wd4sUGowq z7WCzc-gaq8e7kB&sVnL2FoePS;wP};SQO(gKcwy7^m+=`$4TYqfdm;cA=iY_F&%T*X^ zpH!akE->QOE@l3%^q@CRDI%Vq{(XycTJQNO<2Glitnk}cZwx=}xZ!z1;z|93$WobC zrh2bjF9#TFOw+Tg-!r93mbLTlZ@;i+URBTM8RlgN4ljDJ<81%boW`G;C+efj z_kH{|@q?dMiRnhGj|cOU_TJ|z`lYfh=ef;$0_5RwAzh)o&{ob}_ zi)!v~^YiBKYW_W6%75?2^Qrs&_a%DPbJhI0b?)zpsyRZ`OX#tNs6Qeck`*@k`zv+5dZM{ePpt(=wHnMGr2W&u?9~{FtA9n|;l` z@9mP4PRFWP+gE*m;hpdQr}XKswdb39=jW{9ui5hbzTyyu-#GlGuKRvsG zjZb3HyY*9ApRZZp@u70{E2+?~xYx6PJ$`0Bd+D)P&rkmgKRkb)wfX&1*AfdJh%)xh zdiVb34ci`x=lpY>k9&pwn7>Hu)?PKMuf06l%cqE@hA#BlE)$dSEN{h*?=;^AK zF^vAty3Uqm=M+x#s_m!`L(3dy28QX(3=E36t2Xzd{L%tQg*Kfz1F44_8WP(f9V%jH zZ0@Ok^T@q3D$?cKAKh3Qu=3{FU9lh7zvgA_>dKD3rdyI)YIM@)TI8geiAI-~7u#=oWmU4N@T@%Rs?gF>-DTFL zS7UDNT(a9sf0_2ofGNvfSA>QKvKU^zM|0l z=@X}EdptJnY(MWO>Rs%vz3cH@tJ`*`MQ`v3bSAISFr4_Kz=Q1_$L!bEsbMdz&7y?1 zAF5arUVExr_JLiRvB+KhU3yR7sc5FEY+~{7?&;aY@o{Cqf+@S7pWgGs{Hffp*4`IA zJ{f(g8ZSG9zE3}2VK|Y^Z-I_oN0QSm`=%ZbOUIJnOsB>bISq! z@}*B4yttOIycDU_ocgS#Cx3BM3)junD|vRdo!EFzqwTGCs+(*5Qo&e8$rDP=tr_70 z?EGxYr=ED=RJ7MNc&4!2`-v+|j(GgnX?d7%yi>=m$7a5in!dslrG=ASnOZy-=LCIv zxzMR`&r_?<72<*tEDyi7$=>+A+wLBJ62n2|ppN4LPXy;V^RVSxJn&xoE<#$+J>}59 zg%{ck{J4JVIPtt);M;C^Q2M6ir-L;oFZi`J6x7*0`Pn?rQhwj^|0T`)CK`y9?Fs&0 zc&(s$hH=OFsPNTKi*|k+p#8cjV&#x-X~3Z$_Q>zGXc>H|zdBZog+YR2om8e{%kO)YYE_^Z!S` zH(6%1t=j*3^StjByeB>`yIJbP_59wC2lJzU%Sn`czI}x8|Fik0PcOZ;`e0=L96l%G zD)ZK@7V;9+Nh;r z`8m>_qW8c4IDg}%TK4+OD!I4(lFaL8S!@h;|GWHfV@Sgvl9o4d{sLOhvGzG4wj~1O(%{*`CErL4<76^ ze_i_M+Yzt)?`LO!uYP~FIzO$f>RXYYKvHyYEBE=Bf_3x6SU%ovQs`0R-nDIEWO8uU z&!(l8*SV*qM(v2W(xbXLqDSv)@1(^u+j>_$J?U|B%{7y`M?;Qp+;Vi+;Wq_Wb*`6+ zty`sfdP{O@*yae&(g@2lIRG=-qx9U)BmEJLd^Q9imL-Qbf-z$9bC_#&E5N)?}F!o85+Bt6fWv_tK9B9 zxA5G$YT?Pjt*67qXNKzKBSfKnx56|K2dl%~5vsE}P!m7>GA=(vW(h(z>o*H?l)KlSON5^-gNlY6( zyu?NC_j+&o(Qr;+L&H;MrkPV7afM!})Kq+AVUTC0BrK?YM@Mw3xU))kP@0G5Nv=Kj zCr!=rXmr*7T;7_!aKuT2)@O+(5ooOt7)g7pnB}9o3C0vs4Z>wJoPkI z$&SNkkLAs$K~k~L=4D*IeEQDuvff!oyU{Q5i0$$vc~|DOUR@q*sZld` zQ3@*?m+xW=52ciMJy*KVF-_D|S+ab>oKxQILC=>SRSUwmCzstVFFWMeV(w%M)EeVXmxqPQC(#@+y_l zUQhGA|5iFoX0y_LRHv8gDC}_ErlhNAt4&vP!)y;{29s-RFIb!@Q7ku})EaiK%69hm zw)M`enL09y~kGnDt+^sZJ zdH+oGowuXO$|I9&j6D9?X!0^8cpPco_in;&=f(E5Yc9<>U%!F-V|DM6=g-UAmFi?G zt%EX-Czf|F|HX3u+eXHSXCBF)Pknc>xh3HA&v?)AS+k}3D|Kx+414BJ+;m}KE4_(T5)W`#xKX%cyCEC1CBG5VgihliJ_Sp@iT*_rp+R4kFg?)eQ?_V%aG_4_ z-L#ax1C5dEjSee1AFkfx+jee}VNw(eU(L29l07|JW%`d8c5Xg%V$#aj&#Yv=rFbS+ z-DI;-E=|5_aGiJ4zB4>YD`%a3{=8(_t)36XGjEh$VB%i3FkW3Ww|64jf$bk9)~&gz zdR6PaWbTWei7$5@UTc!avFVu3?D)Er*vqB-rw{xRRyaTPplmhQW8s!~+atRrI5ZEc z9zDf-@$yxvCvHD%1DfVvcgS^MRN!18%klGhm5j%iQX$=%OV4-ybCkc$dgblu>IS}_ zHq+(5m0Em@=$>nLUwr%J_n%|tTZ<(@RQk&bKm?1eSV+oym|KTPrLnj-?Cnt#sB|j#TvHSpF8@6 zcmA35tl*8=h4p6J?EkesPcqNg^kHq|r(*d!Z3F-P<=P9r*1YTu-v9TFHvjad_iO*Y z+*9=U{{8i(cbr(R#_j#qUY_S)^L^#M`Df35x&Pzum91t1n6a zj9+fOZ^!IE-kVH*_n$jozWeg~Gy6VuUSBuY%G&1NC;fN#|EE1#-@1Bbx>(%rhgs+M zuj0S=wl?weaoO;FADj2r|5}~@eqX_xXTSgN{Bc^F|M2{1+wb30H-FhP-~Y>zUs2bu zJpBAt{6RR+zRl*PdXaonShu7}*CmFqu<_2{KvWFa$Dz0)&766R&<>}Y|80Z zv(m2V`mfbFan^q0GryQfK`v?ks}=w6Jn2yREYkIRfBDg^ce)BVzv;i2Bm8atoOpf1 zg}hmoxlYzAd9&5G2I&PZ-rlimHeT3T+~z7O`kOPS}+;6iT&pA`Q8VjVLB!%Ak9 zsegaiM2}l)Vo51ztyoZMVQFeHbgpM5a~MiXI56k7i-g^8r-C_cac`GIPFJu|i(vJd zz0SvA!ZA~w&Sj;snr{V|9;$s*EuMDc*v8!=iiV|KD_A^|4Gvsg@t~5asqNdL2@4)E z2Kq9YI52UFO7Kjb-k7kUll|ODPfhjO^6&Nk%m4rVf3tk&VdG>57Q2%v>U_Qy#orkj z%rqZ)ZChquTK!~$YfsTx8@ox$tjD){Ca1L7Je}00ykb?}6yH$Zsj-WnyjXUzp)~jW zS=G&XFHM!iWR73!Ugs=zaM#M6rb27Py|&#HFipC-B&482Or>mXLdY-eBZ0eT`FR9} zO832;CKJlLS!YvQH2*D^Vs93yCl)!cW+hD1y&Ad2SamRqCDwbfCc}pbD=*Ho_R4l!a63#hZz)Id3FbW|){mx?@YpdVf9$2^tP;Uq5N0Hmc#{28oJ3lgxMFgx0pEHc|PxucRfQMbFo2(QpfIViUR5i`g@Cl z;~F*kTv@KG%oJv`Iph6^;QEV;BM*zf zY3q+W-Yi^MB4zjJ#^!&D@++eMRq(quIDI)kyJ>kxz#S*X?H^>0F8Ck+t7hBH$lpAl z-DlQY&;Fe&-MS}r?vZEv>Kig^*3JLeahCD+?*5OpJPmJ;8qYhwvu(cfq??A%yp41@ zuI>9-5ma7twmM`|#ynf&(ts$h@2=C#)_JW~Z~Jw>YEO~i`(I@@a`(sXU;X!Vzxb{A zeIIWeWxl_DeS7AtYq|Gz&(F2LDi>ibUsp2W|HG~ROQ-kcPWyV+nm0*ir`dY`uAdLN zc222&H1*Ej&)27yhpg9GzyEjRmJfF(?zj7$b^V}p{Q5nc{Wq=E-rrk)LVurC?4PZr zQ4{n$oHe8O{Yqb5b$m^xz2%pru-zZ){@iF@JAZ!d-{s4{zpMV4U$N6>?|e1e%xP&i zcm7$wkF#p+ON*LK&#L=ANi}fuE>4!y z%8iK5xqVGitu$kDyVzajz>l_^xmx14H~PB!)%f=kcDp)7ICUwna1Z+;CXa@OguG<5xxP3$alN9BprxGIxDjcsMK}+hBq8uIXD}U(2h$ z@lAfxwwqVk{3`wx|NH;@z5Pm^JKqmz9H030&7V7ai{CX1%ZUFrJzn_Pb92N2mW6Jfm=WACp zF$emb&b;xgXlt0x?5EcR>K2FPh4`}1T-U7gIqG!PMXm6&smnZdrpe`oFh4UQwb-;lz^tp(Jz{M{YDoT$v&&+2qp~+8?@GIR$3S=c z&x>t04Nm)Ri(GG~wxjHD4xdh@QSRnhzHW^}e_b8g<1b+ag5pVa+oqM>9`ZBK8{GL5S&3(&3q1%Qh`BK)NYkHz#y>oMzcJ|5K z6;Bs!eq;T*8Ac|Cf&w)Q^jVtx9kOWIVUcb0D6^Tj;U4ZI6yJgZi? zc3N;(v4+sTiI1JH2L0+jcH+eZ!;P{+ z|M_X_=4j5XQuMqU62|^QX3;K(z*(!3@8t$+`|RspHid5q<0PvUB`dTUmizBpdU)Zw z)Y69Qo_R4ZPsoN_87&Ez;s3PAWOCGnD>Z8K7bjc!E_Sgpe0Eo`*E=LY^HP3;_}&xM z&L*#x_(?3kGf`ZO$5y}m*;N~fhDmLw_A|aJ+1VYCchKRD*SmAv3F`IYdK2zU_NXj( zeP?A@!!f1bb;{klp>CGT-qbGZnZ)Y+bs}Gb*wy~Kzt^(4EZ|m}aCPN-J0S(J5dUf+ z!L73y9D}t)53IVvyQnhA=}KPUl#l$SRrXBj`2vqTPx@yFFtNHkRTWZaot5m_e4=WS zR@V0Np8;_oisDx+d|8hAQR;ug_*(~M(>Wto?m`|afj#XPLr1Qz9!AwYF8)byT6Z3k?Rmo zV&Yjo_u+xZAJvM~rv`K~)=o}ZuyM~T76ix#Mayt`if^03yzik7m#k7H z^FP)*o2!rQn0tkH{{z0K6Xjd&SDh&v+YNikys%d&SkEH~&SOz`cF#Pv`yr96Y1NK7rB}H0jZon;W7Q*z zT0Zs3FZiqD;3$6Lc$xOn^;0kDrUvjzK_;hSxc&yxVwlwtsWDajT1O zudTq_w~Q~|Hu7Zm@?3fEIB^gET2^14YK_Y>Q+J2V`V_4{wP8)x?!D%Z_Q`(X<^5xCeUf#H1@lrds*NsCw12Aw}&?4QfF%~ z)?Gp`Y@{Ts8B_k>o8VsCl4u|6G(*o{O2AZeqxShba(ANl&A(zACR&uXe9PB8T;j|V z^1^O6J5H4Q-2ElOf#>Iu00*AmM+zKzejMpgKUnQ8*BsTxXxPQ|sq?K@+r`P!A#TN= zPFlFvM!WxA_%-vi+ro{96AY{SsxK8DkYPW=6YE&Obo1X=^C)OhxzCq20ubi8mu>7vN%}bRN-B(M@Y$v5 zKa1_C=ZC^7@0m?YjwSBcxJOEBcAeIjlRfvFjW0BBzHMvq`ul?ab<@)hw(-qqG7s7w##URhw5J-f`umw~0k-!MWD^ zig#FjUp-o?^Tp`SlLPnev>5SUjyvz8m7X9p=b|a=K9dBg9}iD)Qjmr__S`^ z%<+DX>;Jiv8(+0wiaK*Lw$SNe_JR4<2LsqG7+Fuow#-hmc)YYY*-LI^M|9EK?l;Xj zg^T(8=e?~?P>k64;p8-dg|nTc4o^z_xZy!|cd(P3XqeWf`{Jjz9?a^0^S%9kQ_u5+ zmU!;(JjR0WB=^fb^lY!X?NoiUj^kRv4s$0NyC?Q?2WNJj@N++xuC(ur+c|&NZ_Aap zT|7}~)-5nfYrK z?N2vKBzYy)-H0!qp>8+Hep~{HpxVbDLZ1@6~?vIG7(=c zZGVbNzoy$)Jv_g@=H=Sw`PGw8UKiW^w9LQi>)C6c+wa%?J}kd)!k>@j_7%k|@}G%r z{JwMgEBlOJ-zKk5z3(|UZclyL-=)`on~B}M{#*Xn+3oZGF#Y^~{QbZ5m;1TvE8j1d z-}~XSy{P_qyU!7CR+gVTq~G>=>!qUx_s(qpd7*#vhN6Y>Z)Wm**IB+dF-Pt2(I3a3 z$6RlF{zy3V*iJd^;uouu4V$)ee80Hbe7@bromKx7s|y|fekhNfF3B8cc)dGZdu!X< z%~N-t$bWx&`okABN1N`O-RZvb?*8G#vmZ|K8yxgp^Qi1jPj<=dHu?1Ozk@_u{L}x;&2Im; z6$qerHu(+oOD9M%Fmy{XFbE^HJ#!LEi&KjslPuj*B~KBn23n)ID}+P8`CDnu?c8he zw#EF=fh`FdOPM*hX!0-jy!~N{SC_*2ElV;xw|II6$!T?kv4}G2c5GhY8W{dD^yA+M z*TC=^-8H-Rg|U{D$KHLfzxwsN?fw5Bm#62QTC(By`|o$po!R+(PW8R-cWz%h7jJMe zP5bYG_+ZoZT6;GfN;}PaJ7-(pzl+yy{E9G=$;^G@;TpU%xA*0VReL;?=Jl=fx9(m4 z;nuN~t}W_|)Az}I5f|^@`YrnAjVsq~+|!?xliRcXiQD9pE|apm%!H4b?b^53@ZOd4 zHBYvcn5fN`c`Y^hW8JeV%caKW4#z0XG`nebJZdrD2k%1Ni(ec*@TpIFxGh3Uw0nDs z%bQf+>t2?#g-@D&zPnND7RS5Qi$2!0U7Fapb=jPi->yYi-MKZxYkvAE!91UBOMYxS zcJ*RGvAfOkB`FG`nl-K_GdZ@qJ=T30n&=$#yXB8m^Tn3R-iawr9_oiymIy8S(#>eN zZIgWLZo{+AVKv7VTV9ZTQ!KPvcA}fc^(iNEdzW4+S^4dmOLp*TW96!v-2K}!HmUA1 zQx-g`|Dxluwv?y)!m?_sX&u%N+Ly(N?0K%bcw$CQuF3X|ma@Lz(v5jES`KSR3s1cI z@WYC2eb-9X>@}4#`(QMU`}iZtnB%)5szURMle4vEJ@i+!yzKS%vh2$g_bvNbrSiO< ztnJJ5IGnIETRPf$(V|i#E;+tm-QMcvU%cIdC-!CJwX|`u@14sdTKseGuB5&%r~E#$ zA9<7@bGpW;u{f!)dPjWpqLqtHwT`V^8=V~PBFT6CMwW|w&CEz;!G5tc&oq~74BQ$Q zC#GGf>r7drDz?1J^!Ov+!{<9i%qA{57NXU){#eNl-HA*la-Fxl*`ytlHH70NT{Tiv zPMaS-#B=JY_A)1FrQ zysQ`6l2fF~v@1kcHm+)$?5SlNJeLbrxgFZ+b$-UWGpC=mPy4pD^rnm1RB`WvT%FtE ztafZYlkDTCWy>C5D_)SKsrk1&zU1q^gIBd9{s(crJ}MJ4OZNZX3f85P9%-HX1Wt87 z3)XkKwmiye!IeqVr+w}9=RP~re`|=2>D}wES)vs#ifp~l*tCvxXbicO8+{dc12!YPN7 z4FQ|>-&wc1x>$5+ZNT9r7rB}K$@p>^9Aa2rDHC;M>FVN?`%deM6xDxQ&VKUTB{jZx zQMSV4kO`JzDu49?r)HpK*G&#*g>%1I#m+r>Q~JxUIftA# z?Y%4hRBQ2^Yw=UR*K*7=W392R^W*zrX}ZMx&Dp6-*qoQioOtqZ%LHfc9VSaX@=r{5 zz5hDq>1(mVc|l*k^;j%dILAM0NyeNdI~97p^UF`9w7j=l_sNd?WS;B&lu0KhPvW`D zSkk_)Q*Xn})pyREOq%P#nPsXMAyUFB>$oyt>YLeLd@rOdcRlDEa`11J^#NP40&7I)IBV1W76z}`JpY{+M* zeQ233Vlz=`k&$71_H~Q(YNDBfoNd>b_|87g+89{A(v7z)`Gd#4Dj#l-DD~yC1sU!!QA<4T^Y0fD5AQ5Irsop$MJ`PueWCZ{m}RNkx;EW& z)^+A`4CJ`|r9*O&lxF+t!h)E!TC8(f4Mf~Sr#5l%vg}icUw7G}peTTGj?i-XbCXj8 zMYB`=FIXD9Simjf#OU*3me%c`PJ7wa9;DE@BB5)R%nnl7F_VMb_pC;r0{Nv+Rsyw0O*?lsVK*f40Zr zlAGqO)pP2s{MTOY5;?0VYVqYV-_{%MZH$#Ot~xo3&J{P_SgfUerQ%|TsS%5bbAqyE zvNC&)%S5h*;0fyH0sRMKCtFBe5>a}u+$>vhd-m%GE+3vAnY$?Q{6fe44IM$zpS+tR z*`J;^GJY-j@u!p4e&%CSimoO}WNcV*%SSrsS({+HgICtkg<_vSEiyFqvG+}Hd#(QB zG1J`XMO^-kr#teDCM|xp^+_hHq~r>&mV{>$C$92Xb=ljk?AhtrTLhLb%?Nq5=k%7N z8aI-rB-&r-bemSGaE75c?AV$=?-yrg+|Q7@89%MGZh>IuVn^#)MPiw(4_9bftg+^p z(>>#!M^rP93G)iS#R_7zeQf-{T3$5^dK*pA`8083g2lApwOM78lUS~4uuKySQC(so zv?X-H#tXJrUn(D6>BVz({-#pK&D}}Mo-qH@yDj`r?G?6d zp@i*e&Cix^`vT)sPgtw?=ZKqbY&#v8WpU-+0xoyHv(36^XQ>>FI{fL#T(@b`k|`~> zHrPlpOnR6r!1MU(-wTUwOzHiVs`L5ujdLrxB&|J)`&5(Xh)*&stK%sa;hDy?{Na@Z zj>-=wk4o?8xjHA0FKu_gbNz&9meU!gqGeJ~g z$%c&Q$EQvf%CwTqfA0P3LeG{9S7w?h`_B6KbJeL=yk62COBRU*ZkF~cmOC5C6moOn zM0PI^$>$~Sm4y>#1ezq~Yzcd+urMm+?9vR&pvKhi#&>aFXv~&J1^2{6o3v*Y&so?kt|j8!x;JX#@q+XNGYVTWuI4F}H$7Jg z)3aT+oH?SNXYF*UQ;e^k9$8R4XKTu*%%`#7&f2g^(fBCVQ^H!PIJZ%gH<-t=eLth(6?KMN}^7O&fV!h6fUy4fio z52{^epX?)_@pK7S-RIjsSJ|1!ai8^@d@T9WkI$xC4c)n*QDQu5TJgMYZ#S zP5vAeo1GKzmZu+IXs*q-e0E{Q#&hQzG;2E!|NJX0;Q#dld#J<8Yd^bt)~)1=TVuTa zc2Q9KjonAiT{l#(Y_=@>CnO)qGw0}D%{RF>PyUIPEeTW7%UGA~=Apy%XpwQOC<7Nm zSI;|Lhu&vXH$2sfvNds#2(=8k^g?-h(~F>Hjf5>iSG%5lO)2=ZAz^d>gC$Nn?xIsx za&2;08D!Dler3>)uS?xqeP%b&b=nXqVZkY`dMI{!E9n zDqCcF(BcK_wI6NEdSS5Y;*CUYb1CurGmjm8zLylFb?v&ar%mTxI{F)2@K^JG^Cw8S{~PP+3;Sc*=0vKU{P{|4N$--SJ3$Z0c7D-dWo1WeQugDjLkV?;duLkL26)H2qDS<23KkN$L+&PcxT9J8WHO z`!HwLC-oz<7RjtUrs}y>OUJTTZcWAnhf9)IQo1V7J(@oAQ!Lw0mnBKO`NAbWx-*SUB_3bo>r7IeZ`gGJU=nua(eT*^Q{y0owk@iJFRtA{HUY# zPo|T1_C9;M@_0>(>a`4`UydtVOgztN?ea0t3`$Aoob%9e!=+fRS-GNt=k?53x*IeO zU1aVnEK5;zSQ^QxCi98m_biQB7tQYnt^Pi{(&qk|#~eQb17|Co$+1~%)xUW2<^H4# zbK;jP+U(4dku!3-eA#M>gIr8f$3Cv0BSwpk`&=x%F>&u>&+gNfiw`UfO=`^s!K(V#f3^l}lB$c0b&Trec%_yrcjLDAEv8zt zUEaFKtaGH^2VJgMd*I#fsd}3dsn5!8Cf@ZjP3N`HHB7&B(|GHQ$gFcqHl;Joh)g@Dver0Saq~uFeRK8e7d+by zU#|Hg;abO!UlnyxQc0PpvrbWj7&C}MDvz=f| z=iSK^{b}lrS?f1_ew}IWDJ-Cy8hF~UZ0^G~X77xeu~**qQmO7q4617MJm0~G-w>6bWy$p0* zcew2VXQgL)N8Zh@7AN})=i=EN4sgAABYgX8dQ``QOyg~{&)(P<_dI&TnH-(XJDYM6 zOKZFz^a#m)4%x-E^uLixUeCeG7gc(@XZG^wg~!)Un=P!b#D1bs`Bqs{k-GZt z6J`0TZLQw%Da=o$QDl$qhwG_7>;63*DqHYqu?zx!W>9JeS@QS5c ziA`_ZDEsvAp6pwRGkE|0KRTx{`J!L)_YDz`PXzY-nA`4j?rMd|(HCaV9-ZExro&MD zuH}eHMSg44m&)1a);F$ycDOe7Zi=|noXQUZdp38QJ=`6#X}0rbN7uv8-~C$@;rDJq z`x&t~mp?CRjQs7^3eBB;N>Fz1U!J1fn81 z0KP871m)sVM@(WR{6+GK|7hr}C7=AT>MD4ez7bNR0mcK)huZ@%A9 zs_UPjrCz@Dg2hGehzT08Q=F$Sab-FtJHwk>Wb234FH=sR+bHYy_%`qU()m}nOUMee zoB0~L?9-0Vbzi#ZqL7id8%tWe_t|?4u}lZbH@?2ipe?o(px(^Iny?b`L+#`>Q{BAME8=|NZBQy&KN#ZAEpd;xf0z`>S@N&Tcy|9&j*9wyw+>t}U*9uP%c}nA|K{FYBiVO}ty%WjH;^(?2Dv<%>c< z_KC$e{>p9s+&^uCTz@g!+M-Dx#o6nGci%1EaNCyoh?QYSV$A;A8a{XZ#m!u5c^1B3 zSjuL-d+z&}wbggL<(|!`XfOE})v;Y<<+ql8oAbVpPb_rgHL46!_0N7{Y5jl6O?kOV zbC}Nt-w3v7wOyK!98~yc(cjz!$C{-C4OBE+QrGQ{xR>&LzEJ8_i=Ql&vjXO_eRz3K z=*Y!O#U8V?5-rc}?6G+~$79~s&*vZTaBGEdHOt<8vCdp${?g^q{VRW@l}W!kai{GQ zbHnzT)6E^bpI1GN%~P4MbK3l1a3_WYh1YZpv9qo{A8eWJVUAL|01 zMPgNg`pqv->z7FDY-K8asLES)OHb^lllA{ioJUW!_gX9#Hsb#hUQ)-MCKVtNr)su! zVOXC+rL)cjRTis7S*sVnQ(MTb=s|XX&xCIv!`jWaIZ}ThDxP?>Ae-g|BrhKi}RE z$D*`9(y>n|?4|3`iy`}bul|faV=ws0J@eU2T`paI#V_r*GjCn(+$!}cx6npRx>Q$^Y)wY2D0ue&2a9n+La9Tl~!0lZJDb z*Kp{j`@W zr+t09GS9er)%^z#m_A*t_1yZlOZUl4un_uN zyvzNIm*|2_^ULSz7k-X)@NihA|J!4$cJ!(@ucyZ@3sEnrvpI45;QuE#|17`xGuXIZd$RX1m?HmrG5Yu7e?Z+wek=icBO7T?|-lQ?uu{=~oQ3e#^s zvSB-)?B6v%__pBkf4^q^T+;mWp|x6g-`~|1vD#-p{6CR$=0GsdfBv3yws|S{_a~Q_ zvBe+Ww?C;w@=w{f+DRv8H>ykgn=bKJ=Yz%J|2B*Ho40Q)wLkno_w_cTAL?iHC4XeG z{Xda*ER(IyGWz@Bp1Tq@#~!>lj!%2ATH?=+vppYq=8GNsA1?7%_d{CSf4$GXZ1;}5 z`EB+&mQ614*Z$5xhtl*9Zp;3^mT0|F#&hq$hxnrtj6Uq#Xi_tmS+;2P_4m7%*2Ou^ z(kR&NDaopN}%bi!7*L-}`CV#&o%I52widQdIFOS=i^ZL`j>2~EFcW(dms(k&We%jYH z|5Cm;=kG0g{@TB`#^TRD$`&6IquEH)#dlM zWZ$a&J-fZ1Pj=7a@4M&E{5|`68SA|GorQ6q_dVuS@2|4IU-|EPc<8AGkCW_{Vc-RA4p z2N$+KcDMN#cd)5{e|@_9C=YuwyZUUPWE`TZyB|L!QR zIKEqY{hG`6Ia#Npr&s+*e?7lGzq;;$fByZ)^)=`F)mQn|RhPV;m~7p)_wv8x%C@=x zBaGgzzg=%*r)|3b$u804ZTr6eU8DZ}&W+DAr~G@L?O&vS?uF9b?|T1!FK4d5`0eGN zxx4>aew!S8QSytEd+ zdOUC6qoPl<-X8w8-zHk3ruY;4<>^oVoVU$>{-#*nEj%hJw@C6?c;=EJio%7?Gjm~Pqd zkBWg zcvPKPIB#~xg^O{xCTG?w=iE9O~?Mhh7dhO5lXM0S~uicq@Ha(j! z`t!9}tFQgp@@$DI|Jt3~TTXuCIF|J7AHy-F*^$rR9bS?CzC5`_;_X}sZi#bF+@dvpAS=(h{g5ABw>uL}}rWm*_8p{4PVK;nvmMuVUUt$~L!90j{t zHy&zjik8ry!nMl4^8?4zCjSd9T8au+IE-5(59K7TGw@#_{HnF}kkUh=#F-mnA{0$H zdYvY3@ao`P-CCB|J3%DKDJnucM_`@MI>B|KX57&&Zx8V$F5lpNq4jo)bj#jDu}<4J z#P4w5;lCrk!lA;WBBa8nBB~;+0(6dqZ$)T@b47H8dqsGK|BtB!J{Ed=^lP+gRB9CF zi0l*EC%8{^pYT5MI)OT&e_DUm-+orM-PI~rRR&T@6<1BzcYQ8ezAVBf8q4qqU95J->|)6d&k!4N6?R$ieL+^Eu4jkodMcB zn|OUxl~Zs2UvH~8D`)Fp`EuXjxI4P&6Ih-x)?&@885kmuplmD%E-A{)OV@KwECx|Y zsfoF3u2%+Jdiz`d!}8@v)z9jPpRp~zeq~bu?-dtc5!S#vrqfRqoV68NdnTlSd;1+x zbAjns&KONU^)}F3+0c0V6`vzT+%X1U9=xCbdG`C6mg(na9ya)X=Igxc&yByA8rSTY zaPa@<>-Bs2ev5yy5jDG;#tQla=s&ZMRTg&Q~BrTrya?@YcKR` z`koKli__0{%2pQmY>L-6U+_9JYD5`?9!T4bIuC4}7}%ySnhlvTvPN3+)$oyf!wB_%iLu;agROe?L9UUvTWOL}PsN z_Dg@-?>_FEyJB{g?Vn>3-HD!ct8Ps?CYP++|NeR6u^CD8@5HINT3xl~+2706{#L&0 z@Q(g#{Lu`;ejW8|f7x)|dK!Q7LP1Xb-mNt!pGR%nDD>g{zQ1Cvdv~sK|39(u_T67k zH^|SHX?`krL3w)q-RU<(YkT%@-}z?4ob;a5-Bv!w_IaNFT~y{&slH5L^Uk85y8_iE zwn%H1-TZ#`=Eb94%n?=Q}&o(mv{M_AYm>{h~L_{=ZL4%s#kx`?nj(e}cp>N~`SdY_z&8S9g2O8mGeD z=V!UTOVr@HxGq(p8%Sd9}V9Qa+agUx0`=1cDLGnvz=XUrUb|TPwu(zS&kZd>m~<9x*m+*&zpX<{>1OJs`~+l zx`j^syYVn~l5SM+&V8#VN<_ERYwt~I3gJ$+pDpcJE1Wbxgxdv9Fl$4DLUk-GnABje1})H98D&)r%3 z$31UG+w5JY2cqZeKPpgNS$96Y?!mOpf?0hkb4^M+%?q-3*Zy$X^m9v5(d=hh&u`AM zOFZLzgj)U(_}VG3cmVW=yTVWw$A+9*|EvbUEWr| zv7Ec}?VnDOnB0|J#-+kbc(^vr={@<#R_W+wj}7x*@0dH~_)PQ23-O)01xxLwFAvN# zsNS4>*wbcKi<8jN>?xm${JuQ@J|`|9Zfn(_=2W}G$M2aLH=AFs`t0amw<_b<$uCDm z|3|m1d$hLifxQ0xnCn)`$$w7oiobaFcU!`a$lkF1M=ng>&VOzH#$Wpn9#vV&8nj+E z`{(znFI%4_t?oFxVOF`FQ&s-Hb@IRdJiahbV(A9=6i@9c9iKIa|C_D%Y@OtBX8TF5 z7YF9-dRjA+?Zt}IU4N$woNem#uc=Yn`Toqiud~-aU-k7#RA1(=+okL5SFf3{J0aY4 z*M{=kYkMBwjP<*cU#r?sW*fBheA&cjD+;0{k_Jr(^cEU{qyGw^H`-n-sk)&QZjc-k=4G_-+d;;cF#_a%#Qzmq3HdY zZqen(9))d{KDa?!U%&5n%e6fd&hK9&@VxSAW{Do#vFNvbJ$!nxYi_(s4OCid<9Q?U zb4z*X>Q{R2j2z3)np@dOA1U^{!uF9TXzSw;!_1Y8Rg)rAI17!w)`SJW&X;}|JUJ_I z`hyh@E`IquG5KMz&^_xrO%c;`=Ci$gSa_wa!Si3g-;zfaXP%cguDUok?Y5!$o5$YA zAEzd{THBmj#MW=gvH!#W7h)fOruG;bOsF=g_uLY3DbDmow$h_L4ZAwNF5Z`Wt0Og^ zHRp2f`?*u6JW`SCmy2A`wq))0*G2DFy?Ffc^P~+wR~Q}b`Z2{ODs0KOOFwsfopU?t zSJJvBroA=S-L%Ttr)+UNrIG&j_KZ)$d$-TKZziu_KC8atU(J$=Wi`3%+qQjH^#8K) z*v}Ud3ky>XzB#uzM_dqemi=%Gmg>QR&>s7T)f8V)@ z3bvf_=T_D*zW8Ebrug=d2)pCND%GzXl~)}!zZPFS+wkMp`TFWBv)ZaJiQl*Uanh?` zN%IWOT+#4v`*MHi)<|+F?KvB3zk2iHHlrOAC%!fMZ&ux;?-#4a{p`=-*-u^P_(}VE ztn!oX^-0oR_^mKwqKrS6nrFP6sDg!7dzLsC|9Z=By^~aLJzR7Bp-<)a0-vqTdoOsr z?b9lLRX%gK$*CRf9y6Q{3X9~ewZHvOb91G|qNB%*i@sMaIq{IYY#vc z`Ge01=f1Sb)MB{s;C8ZhXXTBV-@87kf9w3TI?C8TFxn|zI%SP@W@EA1hFNb|I+n5Z znSS3{FRqYqHG0MzGkHO!&inP3g);X4-5FNX%_qK0bG}8{`r8*K2bW%%^lY1@;TsjPytW@m2XJkd4t$6qJE-qQCG|F_Ll$@)1ry?DXvkKuRn zBb_${=CbWq{GKcPanjGTfyM_|es}1(&pXh3ly_P5ytB!wZIA1`xaRout$l2_>gc!I zS=R-2cRA*9Ip)RYZC&)1-{9w$`1lCJk9n*4cG+u%{eEo&W3 zllp%(pYvX%Y;P_-dhhn@t9w_z_nYT)ZKKf4#`>KJ^1ahdLf;C!`uJj@sdHHG-7|+B zc=CfKRp))lTC?_ATwYO5V3A*||Lm*WzRVBq-uUR0#I#B7!n`ic#fR0}1Cxxi9#lUm z+w$kg`VS?$I3sy4C0X7z`gT_RSLUbp_h+B;lHd#z-grZ;=kn9H+vdMWJ~1m`hPnKm zeJg%Xb3amb)*|ji>3-|1wXg3MZus$9-=zd7%2?Dy~u zKD1_EiTl0P>u1=t2WIDW^SL~x!Qv>_I=SOGkROe)6KQA}24d-vmmivQDZvh~gStl9ke_1;&KOfR-o{YngK-B2;PU|wr$-}gdUm5cnA zKOAl=?B3d=H^orqgQP}U#=X@)ZI#qFDoX}Cs9$zdFf&X1AgDQQx*mF;r7PSXz6 z&kvUNItHYPMEScF2To`fz8AcDQo8rks&(sJ&)P)Z+h`^-Z%S`{MgSjwoUh+APlgZc zmi#VxYIdsZU$(-87jZ7rv*t{K$smsfWGsv#6p zCn8bSd$?ppr>aEB9G>IT)oa3zTRw>LRc2Q@G;!H&p^XvkOrd+9OGg$TKhdHW>fCGp zP5k!T;2Ui>MK7!`QQP`F!}99gvyq#Mk7{zA-fnsGVZ`hty|t6>sO^3vWPi=_f%(iI zcMQ0Gw*U3Dt;hhxu9>3quWgyY zzVc{)T7G&K`<^D*$p=4+#9!XGx=Si7Da2yi^NA&UA{vi$UVm(vtsi$S z_xHK4H{+lEy~%l3Z$IAtCU%r3t{Qk2F2cmXMp072URdemtqNdY-on;ok zpZDdo!rqLGt4Jje5 z4>u1!&-&Ld#vi*ZFK_iukqfq++eHjt%ksVpf4A8%BtJ-g-wBSEgtCiQ6uxH8RrqeN z^|?8|`Z>F4f_{wM?3R-Tasts)ZhUn){x@yjhr?Gy7JggyVU|Z`agOrc^!anP@9Spa z3*U8m(_2a134e8V{rhz{d$HfLIWD_;E==jK_O1I8{A9foj+Hd%_e>2_(zROp|q77$y5{uTi(4+1{LK#X&sR+HU#CDSF*_#o=drpSrlf$Cl89(!j=84i?W@=evm$Y&ET%q|Q%H>_3`A^Tb2$o7dth4Osw~dLHx_>IN zI_jS~aLz~lmPpEKuc)8eO1>Uk>ogBp9u`s(i#95j*&S4E%Ox#m{+yxymsRP~XMXz) z9@p%Bcw?@XYeZp*>&@7UHx^5XFUku2D5R15bl2z7s<*x7$Ja;gogrTS!)C3SskQvH zg!G996Ruyj`Jj1VC(|sikL5kPs_)q%BKs>YbD3mr!<{-kyI6T?_Z#3|o`%*C;ci z(6#KT!Kodl_br8U_^PH*9SX z-?;wMCF^y5qGdOhMYvgI_{>}qk$>}Kuf+s^{hf9emonsR_@mT|Ri{H-H*d!0(&l95$ z1@qrrb%iy`I^}B0Db+KR7}eed-_o4;q=w;Tqx>HOh97|<5^MVYE%P@vS|VBKE%~5z ze}lLmW3%In*UK+l*I%$ce)T^+JGpn;KZ%;py&UELe8MZ&TfC7ET^c8es%{fp+$QIK z>uCKl*|rlAjSqYxZ%N3_>GWJ)KC$9Msp_TYQ9mQ)@2+uwzu@rAiEyG*UmGw}JKd)UoQa@JXhso+_Me#NJf8}{jx)Yshr#>S^|BkK_NS-!TZZq{x?dx=Bwb!e{b%JjuiloI|0FB|1gcxU zHqHpUmw3wMLR3NHvl}(P)Yh`{pG%&<)>UUjn5}ZLE7P`pf9O?8p(UUdd;~yJ5!4OY2swdKQ1nwW9aHUN?b9(;qZV7ue%l;2^A| zA|fZ$#qpDuUr?hx>|eC`pS^jZD>iKLm|8n;V|Ek|^T8P#UtL`LCE(Ipmjb?Jlm5*v z(dLZlsdRjB~gigyMVKTfIbn8hqVFLFb6op;%u{uul0yTw}>(pPCW?O*)s z`)|J1iI*Pi+i>jPiPQF*q<&AoV1CnY@}&ip-YQvUl{+)^w~6Pujq6HQ3s(v_DlJ%kXJh;{ zvqY@`LFU9ymoF8_`ri;=X~p5p`^~paDCL>f@;UFE^u&&MZE)*w3|03{@3A~t!d$eG zBc07!?v(5XUQf_$G z3*;`xPfl0UKGlA2>-!xV!BOvcHu*bEzpHk>R4peVCF)nI*RF$iMUDA&zR$OK{(f%c z4V!+$ZREy{lCDEo|>9W_Zr!g#G&(VA( zwkNpv?#|!I+)2(o9}|8VuqYT$n-jE)>pTCA9?R#PbmuA z{?R#UO9fpg{{Hk!wc5|ev5nnjbJF>FE04}-lyI_G|8kO!8FCP)(jE82s60-h#T-MytNML~N0oBevm;w{V2_n-j&$qz<0gr{dZ^e{0u@ z)7zX@J(_Yot8$68(gvNb{G~Gwr?Oj`reDgO$hsw&Um`clYQ{=OgTk{DRHD>(M(>(w z@KrMIO6n&0yjv&EPRcyS;Mml0+2fgSo5I!BDX#06n$BjMn%S{MeA}{LP27^_Up{-1 z8sk%xEx5PehQrV#@BBjTiPJwC?zqPOMQanwJC#cA1D1k%?y}piG8U*DZe4NMLw4e! zRSvG-0*~=8a<5#F6L+g-wRZ7@ZL=kW??*Y7EA0NWN%g>km0K52-NCWNyMJEMOU0-B zo*9$nGM=%UKHZokr6F!I>#rT>+rQ1`cOJTDr6v4MIB-YqSDrt6=B(uK=c3u$%Q)U% z`P^V4?yme{nZv<8`K(e!`Bx=x?XJ9tI{3=7pn4*Mg=OO=J)wpbrs}30GlchETHt+y zHNL-6>-$Cda}Q>eNhQC(TzZ1(kPO4=85Y4mXIf`A%FbM$$=PX`nPX8Zs=4@t-I)hV zPuXxqPo^k%O*YQB5bKTCB zdg7NbvC_dX_cSmlaXG{Vjqk`B8%(KQ^=8tOUH63N9@nbTD|;)%{i@2Z zWL}7b=#K>r+y93CdAZ8NVt>iE(qMD#((oe@mJcLP@*jNev76mKB4=ke)7yLpPBWeT*+ zTvT)^M)j&ks@1(PqmyBib}zf(HH}fG2@tAFJ$rjtkjPES&*028a zGd^I3*;5UfyDyg1-4E+%v70h8J>`73lXXz9;?IA%>*M&Iw{7~!zL3kjWM9}S4*9*O zxN8Dk>P&-UBTrMm)bq9r{+j4*UGXG{do8n9E}u>F~Nz>U+dLIvLmt?#Z&Uw^MxV3qUfWzSe|f4;lWc9DhpG|gRoaZ_2& zg)u+#h^asIr+%S;wcb>9xeJ%sO*%uCR0?p(wY*fl^WmXj9>)arNQYVHr4FA9mACB^ zb6FAJoUXE{SN>nQ*OJ1QFS+!5gX|Uv7)_toysh3zNTYy>)$v2K@E?JeREtDO#|aI> zDgrGv7K-wY1&zWn0xg^tiA;_j+7qW6Ze_4nbMe-CBh#1{ky7Wb^*vmXW77rRjc4Bb zp8DHeaHwGBpZuqFffkOFHQpCqU9|YY>W!abEJ#P`D`*KRoia&ym9c{MRwn!_0P?I+H0zO zuD`4@olB7~W6|6Hoka!P(~@2C-gGdB9TTW@yq=!iB38!GFjb7DaB6hNy##OOux$cY zjjpGE_tV`X_w2S&xSW4BM_=uY6N1lf?^UKl+ zy5g4C^F`(7a6R9DSvW=S>#k!lTgA*PscYijibhFN;KhJ!lw?sZo z{+J|sWsg|Fm+q;*ny&5r^t^y0r$MHkFE@LwUe5^*$LVU%Qk8!CWIkm*cgp1`>#eJo z9j0nA^rbAk{zraFpvRUA-j>y@W(B`byvdEu-dUexxH{&(gvW^Ky_&pIBr^G_FC7`5Tz{IKZ_S;~_vuezJQiGM2EJNq!( z{M)&Ai_|6;+Z$WmD_^kCocC~Z-Z7@HPVb+mzB-urUiEbNuiC~<3(v1&SLxJto@8`!rVR{GqsZ{6}q z%eZQ%5=TWS=MI-=LEN*MyBz=5FWD}-<6-fEMiGk%7Yo+S>9w9<#9p+!Mf?6onL8pr zkM5P6m1z4Z&%nU&|NoJzl_pny|7ZW8!#Oeakm#r0lImjp<&U%vs+Vir>!qtDo}Ji>0>zq2JmL*TWrN z_oTJOyEp8=xM@e$hRX9IIYsAN8_$}BU-0j3{dLxOfxH7#?KyFd3*T2hzs($&yymsh zQjc9}Nhap&7Vuiw~FbzrsY z<<&n|YxO#BRSwFbJ@%D7(zx|nRdnN`UcW_B*| zPW8h^KQ6n;hctdTM2QvNGGy&iU+nl~ZyH0?k)jKW)^j#YE7Hh#IjbS;SVzi&$p>Ew zUD@fpF8ar(LeKf<75xPtf7$S9kK|+9uYtGLo#&fse?yq_ens1bed5zp=Cn_3_pj|= zv9EvY(+2-erB#{{w=bT|iOgnoo7(X+^|#+kn>Tqsrc8dmXV-I?bH!rsrn`Q&H1^;5 zXYx{Cg}!S3KQ9GYAA3Gr{Gsv6n$4H@{1$M&{zm!aC(#ZQy@T7gDqXsfGu6g{zxD6S zkcrY=j}|IylWYz-u8#>oc+A=gTHBb#LOAb+xAQ^yXEH)z;C$C znA>&6!alRg6mDsYzTbPFY?*)NvaD=h-*~OYwK=Cg+zqj1ZYyDm zzCC5)j+MW5xhZNsWsBa|zL7t^BvmLk@&1e{%l2=M@6^@RZAqCyUb+u00fr~PE!@}_i7 z)c@ta|J1K6x+Hw2EjY^LtE&6-!kbd-12!K!!pFJl#-UglyLU3mFQe=CTwNgj)8Oas zCktmyUA*e?RE-Y@C(dV(mpRz7Yqed#lN$920o#RRKJENzQ~WXTwf+(He3xJId-sSg zlKj7>>Gvw<$-jR-_*~uI8FZg1HJ&fB)BKlYV3+e@={Q}MOIvDqn~V?qezmS3Bj^WL zjp&~BK5>di0($>OZrZa(QFhOIUZqVpa@zNpKe&4AZ|k%92N-_6HrX$bwDt)1g@1ky zHeEFHgQVWYYUXdRbX#TQ#6ZhiCl|amub)2g_J=VBfCiwx6 zsL<_GU1xJ+kEP6h)zreqy)th@-{mts(^ubK6TGkOR;T1eZmz2LoN`>U>u20tF2rjp z{E_FMKif9dxXDvL+0M<3XI-X|^xkRn*60)MxgQdD@An8(HZ@ak`D++x#EL3%XMO`}jw{w`!^=-M{_lo8$@S z915nZTScT#xb$?od7(|INf*=pe_ai8ww;vzX&`dv?Ek=5eknIBv)-TmJdx9U51aL~ zdZAd2l8L|L_I;c`O(|{Z9;M!Vhll^8*4)oHW!|;4^^{qVCAAkJI*sz zPr*Qs=g@(tFV=mMU*sdV&P`Zu-FMHaB_F0ADagv7Q9kwXBA<}3#j)~hzI#qN@*z8a z&U-C)*O`0jcErWiOb+&2y(Kg!{Dw!+X+8HRws-Qpd6rK8>A|7gn8Wjrtdmy;*9@8r211{9;?w-ZF$}Q|t>?`ZgDA+kJ24giQ-%4^ND%shswI_qhdW z>#I|i%4f{6iJBB*82hHf|FnI;#b5#68H`hYTe--WGg$?&Ys?pPxqXPixA=19cU77JKKd#>J#%_x6+ynLh zyd``s<>VC8gMVCEply0mqr#m3nVOe#`KE>zPhZ`-lTXB7@T~gIrJz0YqQ%ZFVp2!< z9q!w)?apSGV-Nr7)?VR!anj1+Cy#;`5A&3FJS#FdTuRe*^1OF?h|QfY%RAw1#opf+ zV>$PpKmPKU^{=Cc@8x!t>#yCvRJ5x#!Cfc1BzJ<%4j<2+eY1~6dkd87%P|)g?NJb& zEETSE;K}QS6x=WC-(LJXEm2q_qVS7dFK5guH}-o z=MGPZ+T&(DFC_UFd$^munU8$IVwrViUfcNoRo#4Zs`JF8?NT~txtn|UpE`1QQub`+ z+==HN&g7cm(9`y1$_t(})7>*Hj=#TPdF5ql%-Rt5#oZY%w{{3_P%hJL$a>7sTgJXc zL3~l2kHhsjgvl+_enps+;$zz1u%CX6etfa^F(d_-b=R0Pd@6tV?Tbw@3ZIj$$_FPB42Nme#Lh@?rKZL#hk+#a>gM(zW3R8w}gMy zdG&~K>5eJ$Zr`5P${l9Wuk_n(W?IsB*y!_IhTDhwsRu%VF@Af?S zc8>F9ky{I2T|atk^^5R#nS3FFUz|4`5Z!&r!hQ19DUW>B?$7hPT@{|ZX;P-nT<7dn zg4_6KKlbX{;<76*wIFfP+>j;e+yToPruj4P3gfs~WwSs{#KHZ_KZds_AN;D?pdoz5 zBjcOt*_*6KPjCtZ3wKXno@)0k_}PvN#!8#FryRNGYM3-VUbZ7&$zqQ97FA9=9k+!M z`!5_ld;3ikXZa7!=RCbWYoccPx_aceM#WEl!DYRoW7k~A2#aN|rgz`opV#|bA6P==^kK$Ag?JJIy*dFmzZq44E(Q9_RyvC%*R2%SXV&|*r65su$L*J{(Z1Y>< z^PM|*()w>-^4Z1ef`k6HmHdy|bZgn)f~)qXd*i=6+H>ZY@Vc++8@{F(y6tCK{9j>l ze8)%L@E>ObOf9GXwVHlcf8RWv!!PU)SnwRve#89pVoB}VZ_*ncZI}}gG~0O7Pgges zwF~jC#cp3(Gy8Z%)R#N>sEYhP^ZB85vsKJVQ?KjEmZ@9BW+l$PF;QUBQszr9Shs6F zduHgseN3S$>z#aNsPcjuJ%OWn%vrrne!9&sRD>_|Sam4rCN9qsyx-n09za@$5LwHi>D+>F`TgrUrM~7OR{*d!}*Y%#9h368T?*)^>>b zF-BEOE;{(*{u1`dUID(rufILHIMwHE^P+!(zpJNh{E@f!?2Foeg0pN-AN%oc`|OJ! zO&7md6)1js(mmUMg7?4cXg}KRZnoGytbbR;)Gx2r6c^84X1V%=Zx_qVq9sC4(tcEY zyt8!Ow3OCF8};Qqp|U;|lf&mvNqRY9|5x|SBiqV6o2UF(d;V>%UUP}^%)Q=BvzGJR zx_#_LiSo+5-j`-APdZ@W@qdH&pKE>RlRq|0?mSdIeT}LA(`{QbZ5K4(G}rySW!AU8 zee%DPo_P3NpLJm~)7*A1^`MF6uHKW5Y$-3O%Q?Jm>xUH+e_n06*6!sWG@;zpYf{23 zYsI>pwd+jpub6my$|p1RcK+Lv8JB0@(>lJ!F~lX0PhS1x&4v@YoNxL3s6N*Gt>TTUNTOqhN9#uVYDMfqMW8C)-H z)fU_-6}Z}~yP;uGvT4V)MMf)oH!!W3n$I02*%ja|n04~G!&>F~rHk$-Ipioe&iLk4 zwM4Eg>+r=Uk%VUl?mdb#(zIChsp3!8q?ZmdiMeg>!+MfbUCKYyFT9x~kb7^7!kf3V zQfpuL%?*87wQ2MJ#gR{6wf4?vUZdk~;Y|r^6#uNW)yii~DKV35H z&)xR;W33L(Pr5!oyt+DiVf&Z;ODwOa zk3#Lo4Vt$@pI$$?`s1>CE8h()6Mwr43*7H%4$HE+@~WG0?R*ce^{flLA5W|AezI98 zeWscA^%cFRvUeO?mU=VyS`n+>%eLHmD`qX6xIKS+yz(EZt;gz;rJh$lX4^9*>U@%j zX;;bp#C;#F^H*5ruQW}3p}FPb!PU3@AMU-~Tjo<(QTp-oCbgyS1imIZzrQ1)o3=IH zyluym1V5g9k@&+Qn!m1T)NQdow5tB!pXPF(FZ0@3^VZ#bmZ}}op&h!(y7t@MO*VXo zi@JT&UP?(-g>Q7re7E97_mv|vE^p30tz^MJZSlgmJ>pk zX#a8iM0L++^RoX_P8?4?HE-J#W$hOnMaBR6BD5J6nLh4!|I}r+iao5|zvSMki^{Q* z2jqq3XFgw5E#|6n;>F3B$`1P@hM%up`}xo`){)ot;9`COed9xN-skq*d~dVD_slB( zmg9Hb6&x%$*DJKleY!GQS2fMkiofgymmkAyQSq($AM4D+k`{Bt*15iIn!0Plk82yW zub*7qGQFd|?`7AG_<1E_lU9pNXzIFgHj#hb6^W*03pac`cHcc*qv=@w5sz4b%I|+x z8rJ5wJo~i%42QhF|NdW>y?NxEd;1^S9sjj2v{czX;QbNy<@R@_j)(^LC9VH%R`OTi z=&HJaE4!BcU0wJ`@awv5FP>~?ndDj=bNcLZ|HO+zUo&^|%uBkIH2LbEq`37JH8Y}l z+mBuo-*xoK;=S$SuP+tdUa$PU&N}N>Z_Tfx=aUn^)tovgeDu$)@9wQdfy@UF>h{e0 z`+4~tF(rO=%Pddr|0^DuDLy)K_-EWMEsuBpUCJj9&!4|xs>8R2ZBLt@{?nT^!^*-- z>z2@`udL*`5 zczwR(*7Cdi#ckg$?^M%ne$tN7$(HxE-6t2U75aSZw68v9GiEEkTYYR1(^c8lr&kVrda7f)?CF}Itn9Uy z%497kojV&CaNzLql`B*^8BgD3AcjCr3Ep(6CYolI$JE@%_IT7#|!sA_)~Z0 z)0+LMYgO`J$0>v*D_&gA>vHT*-tA3S%xyLM*bW{4UtYs@D7#YN-DTxRhZo_R{Ug=RA15 z&v;w@&u!}#vdxH=vPgS<%6Cgu_@-nQy{@RgU#iZWSsL>-PVS(0(ALaEv&APa#@wIm zZ+Bd8`?u0tw%R`e_kQ}-ySvBm%8_E(?~A49OFz5G<#WUDW!c&FitiWS@6Su$#Qosa zEUQG`g{sZ zKdsBDu#mEQ|8q&oy+fb+o_{>wx$#7c!E(|4k0oY{o_{>!8HexOX~#H1=ZbkP@lR_0 zW2L$=GhyzcmSXRWFEjKtXMTPp<*Jk(eB<&m-;0ZYDddl}RH%h%ed{e6a z)%RTZ+ZT1W!p@1eYZ5yrE-&}wT>VtoL$2IyV$hYxuSLYwq;y_zek(aqW_^3{pM%%* zYahMIS1Zn!ev#O}I?t9r^TI2=qeopt4qrTOyCiK<&#SsgW$)hS@l;HhUU{Kd@6e8& z3s?XA@=4&w=c=Q7y4f#OPcZV*RBuV0{il{Xh28h6pYZK?>>8W( z>7ujC=CV4S`J}RBp<7X6WUF)E&JQzBo%^M?nEyNL^2r&`w(I)$&q|jr;c8EEJ*Lil zf9o47`E%bh&n@1gSaCUg#j7*Fp5)$Scdy|r&zz$r=r#Y@ESpmPXq&Dd(>!FXz3ns(Z?D#{L?4lVCwMFJ-=?oN zoktt(KP;?1zr|kLZN;wX`x;+gv^Xzyp!&Ve?VNi*KJEukK3thzYccun|Npxu`zJcB z&DimZOMK_aw;Yr2luYgQc6q0LrQqycu`KE7DtCA647hmW&bm9=ZN43?6SfI<>8?HM zCfVD=``2&Z@sHMh=j*S(|7}@!{Nvw$8^3@4Td{B6`|szzSMEDsY`uE*C0YOQ+gah( z8sV4cnXignn)CMmgB8+q!l!q=3d@=DO8)DGuhRV`nc=2W|9Bg$yw@7fo8#x%(D5vW z%eQ0w2chj2&n>3*Y<14kKGJ9ES$k(UOTaO%xvLW1S4+ z{+#gR?84)cZ~QZUs_Xj1B}aPe`=2%2HZO0kDueLG>YGmX|=a%Xvu4L zCX+Ruoo5fX$1jXiPAcF0{qdpi!MrSK&ujF0w%+3rl$?0Oy*N2bzjy0BEOPxNo`q z`JG4qmrk7jv1e)l^R{Jw1Dw_;t1j_hclt$_h@2M>gLP$*txvc~VPnv3*}uX5dxUP^ z*|H{WuUf8a)ZV+>)-8D^_Fm#+u$J@gy*vIsUpw!&u+O{m(;I7D{y8xnFEC09ob%xw z>*~4sn|in`qN`>;zs94hzssgH-$JxKVd@@hYn3B*Q*{!wPlb!DXZtZddwtK@nj=$% zKV^HHsJA=mZ@n6@l_^GGeeo&nu)pan7JD>v9=Sx>U$HG?N(pFSQ z?>x1bW6}ctS3g$=yuWMHx9C%WQ?`6xzT}zc9ro7#H_t~@pZ)wlR>b3o)`r-ECcU1s z{9b&?Z+XxE@8R%0qP3Z~pvm*hdY3gKw;A*+8ZyO$Ze0GjIYTFT?dR$clbvzq%f zzW!|M)A%O&sJ6x2cFA@%?kfpO{90jWq@LTyr1kp07T9>{jp2+FGo#qLZ4cP-b(^M} zUkJ^eo%Lev_d{ztr$s*Of3b>hBb$r2lf}G@O6?l!kJ|ThCJI^T&T##15Ey^K)_Fz# z3(HGCwCAx4n>JrdTCnUwvFl@!{AQP*&9Uw6M+wD6*LtQuEG$^K{DSS_g*MOnPqohbbaHCRTp8*5y>*Y` zo3C-DMjSQv?Ml6oG1tCw((>5V{?ijKIOQaSmRBVFpPWy;^i@L&_UODdlWpZ{Y^uH9EwyfA*r zTJPys-o(xfIA)t}-TZjf+M1h3laim=D(^ci+V}R!{BG5Ue}eb;&(Yr(w)3a|E?d{w z+tuMeI{vl>?7uB<$aTGa*QwX9o-4VxOgwjAGybQcgK?}ubDL3GW7-7~vDHW94sq%Q z&-aL^lK5qFG<{cs)ZHU85ou?WUO0KN9j=#zfjq*P}|F>23)Z93hHUIyb zr{WoRUSCWVE4ejWgK_ii1SOHukTYFRe}BFpocg#V?zZNO@GnOuf8>n&(|Ke6i|l%C zKY{2;EcOYD;#VHH_U536cZ6Qeg>tUKfKs;H;3nTi>z{I8N>Gy2Drqf^Hee9844geJ za_gUd;}>N@X8rrhybZXsl{i8YmE^P5%rHI9t{b4%7Q*_X#p$%S@7|mZmp0k`2~K-= zdQx)da;EL6C2u-T)=Fl~xKUgf?biRdyE)**&NRPw@$8p3PBd|5)liWZNY!xp@-yr~ z-bR7>zb~CNC|u{h?s!UJmJ~(aW9esAxVD@Lt<-2MG-+@_pw z*PTB|bv)D1y}teaghz#nyMA32fBz(IlULb{InP#@?|GKB?tQp(*Ui?9E35D7Z~5M7 zzdGJ!2i4V` z_UuYI(bMYPL$8UU;Y0_lb8$>uS?nAxboonyVpHl`*L$9mSpd`^~mp|L)_VoPwmiJ}Ed9~kfd0gk7cPZ@ zc27S|+Iot;@(JtgM@P2Kww~T|xcjlS!27J(?>n9@Sob#OtYoF|Cf>;#>n=q+G7b>k z&n)%fbLI&zref1&i)wBtUirA{oZZI<_nc!sD>1h;1@5|J-;~`YJz?!vd$uq$#n1Z_ ze$;+7{@(TQ;ksw*x{CPwFK%|%cB#L!T4d+_)df}8C8L&wg$DDMonILtx$jrr?}Cm@ z-K}l6{6d2*e|AJw-#xIU=ayUUA~m6!oAnA>cE1!>9rE6NccGl>)Jp%@cEQ8z)Ai0@ zIpfoMD}JhBt>f3JtN&=+6|jgs*WRrjiF8-2v`H@*todsbOp_VElcpC*~tx@Ajz|2ozw z+I?HF=~@53u8+&54r{7j=H)Z#&SehXKU=`--o~^fr{r}>k>v_kRs6rI2t|b-jl8AO zf7(Ir{~J^J@^>=-+PoO{MI2>c?@@3qpz?ONNj1-vc`|%6{+pf%^wvl%{$%t_zEQbI zI4|p;vjv}xjlb5~6i2?lS(7p^to|V1F#Dj|pMXE+JG7P@h<t}hvmdCE0 zO_E>qM&qI0g^A8SNj`EC#&_=D+Wl2*+H>{Ei_UVSFHqyZ(D3v@nqvQqBQtF#B<^^9 zdw1Z!)){+JJ{{m>U^nm5Niw?i7GynL!)Ki{0~8{?b5 zWpSQf<8S5mcYnjgXKWd4|BeT|X}NsfEM?lkqN?JbZb$wpShP9un_XH`_Niye)IZBE z_6NNWP~GPckgNRZ%gMBj`HO!zdrZvTl7IBI`pca{e?Ohe=!ob4yw%;iTl-_NV-0VX z`T2~%kIb_R`0OJUHT}NqEnoFQ`*7Tu!rE2qilpWg7WH!8|9Mih(lPzn>mBzd2u|iX z%~^TG-tSF*!~Vsed%jF*`uNM)Z5^j{+sV_s&Uq7+yf?|u?bejjK6a{H?}J`K8sA6u zxBHgI-S+!CPwe}Lqk3!er+arEiEP7i0qcHN9@U8N@k8jJZ zf0ev1{a9nj*dMcDme>7n&l>kc zP1=31^UV(14)6AmdopGx-#RsI7uqFa_{T_P$9%z+Ws|N&r=(Bj%G!FXJtF98pi%0! zLq+cdCX~NCcyw0CgIznd6zc*Wyno|*U>&C}-%Xt$^DdSLS8VncNA5|i&b`%Zo4M2f zy^n6b#T4J=3Cb0JpB~fE{cZXc(gL@= z+ux-xYTg|^=bz`qoz?69U6%BDZCMwiT3oi|!nRY4wRTH)`|%bQRyT@#*&Mz^NFipu zhvd|7IW*GIYkT|UXez;9Zea`S7`{oSeArS6}9$4w1)_PDT+NmD6c9s4PT zU%XYFuf?AU{ulNvxH-Ds|ceziR$gs&MVJb_uk|2uGrqcg{&*x*7Qj6 zY4~jE4~`CXsj)oe^zf2QYZ23CF10;tyYI?&|H*BVJ~!1&Gg$uId_|+8$F{Gug>o)F zDrWY-{`5&&lJDxHpVXe3-JdP~Z8eX-$@8@y{AXA;uz&n{EiNm(MuF!~E}-@Q?7$zw83t+g}`9IqQ0Rgji#jzkXxa9rkX= zWwtx+A-_APZrjOnMrE2t;P>d48M~agw8GfU`UEetPFwu9qrUY zmja?@JK3MwCCtCa|I00Po!?A_v(Hu>NV7>Yp0`m_{%*&4rv6oN!nwH*+0S3q_Al_} zud6fY{kW5@$d+r)#)qnjVv1Lu-^#Q#`sq5sX6GB%3EVS3ExIDN^l_%n=4avarpZjW z8}r1v;9K_o%QF*K8N9C0=yF;iw{w25m5uV5MQ!sI3wYVcJ=;_K`u|F~X$#EE|LilJ zC|$HL?9!@Ii}%s@@7K$3+Ffn`b%N5N6CW==yv*n6|6bl{LY;L{#e8PX)g}3h7v7!n ztx59lm$ela`KO(Dt*!U}cvN;YQ=i4Dt()Xzx6E+f|MlN1{avrj-Q#{N&3PI%_nSlg zC5HODuTuSTJq`M|TNw*yZOCJil6{q9TK|!Ur`qSVb5YM(MUQjt9nao0w={OEp4>Xe zgZ-rV9!r%e$vKRi53kI2K4kG#?579cEgSnO&D$yh`NX~yvq?Q$@0gV}`Q@IUnwOsk zGP>I}*XQ5=`Dq?W|JOR=xF|b!blT@3Qd4 z8(Me1kD8kr6+h|g_Ugy#8#ia4PSIFgT%R&$vRt=EHA~*_+0TRz-Q@cDLRa`hSMh1< zchje+rKL}*tdm&IEEG2@$3RqaMf>fkOgcXWo-A(f`SYZvr~FRilw}wFW*zNTDY#uL z)nxNu|H7TiDa?6GHp-n-QZ3u?IlNo^Yr=5>wn>Rdgm_GfCFmSkUFZEYVl zn{}7$trxo2{eLC=zIRz)c8Th&;NxC8iss(S^i1w->#kwn$=z*bU@*ZY`ITIH&1R$f z%TE2B`@!K?h#*(+txX>|cg~PIv3}#D+sFM%ZK5a5(msB>La*ah@d5tCx4sEm<@oj` z&g0?K_kZVjob^Sd>ZMiE&)z!vD$l+7OuyqnlldIePk%x^ED9D@evaNdy}W5woAttr zXZsK4mS@|5}X zXZN-fXY1*CmmYpKo_|b9;AZdcU3hy3(-!!Lg6!m+rW|#qhY#L)U;Cf$jT(pM`F& z-g;I2wbWhj!j)SL`_H^^)=c^I@Z{kI25X$-`(3BXMO52YNv_+zOSyH;D!Y{&T*Xaa zGb2M5hSiOF4vz_*>vg>TyVq5xTu3^lSt^ET2H{O2Rp|VG9vzfBJ+?U(M>;B|iICyt+c%9zf z5X;JUUWbdLpR}qx^ZxrT{6*}Q@6`{&%FgVIJ`=K<`Q=B$TO3uBn2b!?J9mj?7S{YJ zKji#U=$QL)#l6`RgRV!ib3|Th^r`cGd9c;rYR0rvnHxht`|aiMc-L#NY(@2psiE)U zgD;%Pi#slO=5(BJ?N2G|jM-7I|M=Y(fB3CTzsz^T_oQ8)7A=WdvTAV&i+H=lE5Rq7 z$AeGaGJ1Vx!s(rdvKMUl5VKH3Lq{$A1p`<2dC{zfLwQfRnjI5g?mSexU_<-Pi@$?> zVjpN*X)eqAAUahf*#F0=k0QbHKTe$!xy)eE>Yyw!soC*1zwSBbG1#_TKAr{{XOf`*2FsdlqbFCQutC@mKwk0l>E6&_)c}>$t+o`KLbnn?~O@+_vI|!P=uLPeqh-CY_B+ z4ZN<)qO5l(XnptFw!?wkUYx?BlY_$@rm|KtJ_~L6@Z;nz$yZ)dYm>^f#6oUPb8o9y z;wY?iKK^gS(Y4&Uu_u>>cIZVfpA#*?(NM8rA?J(FPdYSdlB20%}YNms_DJYa`VyU!*X_)YUiIXuc)|AO6hIIRzG zzc~2kPUY{j2Ai~#&MfE=`uV3Rc#oN5ocA#e+cTE&-~YzyCGMZ*^El*lNcsET=m{4? zi`QH2l)D^!?t%VcTNbRtaC_KmK=3&rUStZap|r7hi4q$B8QO&QtO z*4&-Wzg*Ac@UD{+i`cVb;@lp+KUK3Y>0ZX=!}F#$YO~jr#oTlE3cYjBcGl|a*9!mb zp7-qT&g6oawxj7Uuh^PRU#I`xYthBxsgJE^J@3y_^Ov#SG;3ecLMeM2wyG7s|M~q6 zoYEuww zn@jp_cP%@6{p&8L$oY)*(j87WnVhnhunP20ks=-WQg%hh*dEYEqi zZ{5$_cQXVg#Jrm!F=6(djk)|KXWtheFJqo`N;a59Wxn;YPY%=nf75vep^XDB0m{hxy?TMX+2u9CIKA5UyN;GSvo zZ~LyBQ?B>Ea%;GKb;Z7Fv-B%HGFKLE(8~F6`E!@eC1*FrP21ahu76HSDLTGO{?#nu z9m)BOJCDjrSFSi+FWTBwz_i;w%Sg&3n`_hFNpja7ef~4KvPxp_=I?CR7rDL9dCyy$ zaQ3O=ws|iT|GqoV&Hrt}`d1Z(a>d8}n7m(fI8B|)_xtFZ}-x4ri^Z7!m&zjHtA~OwJKbCx}od2~R zJmGd^dbQ2v+8_U4=XnQtJ@JvAdP?(oaTK@q@?}rT%Ch?;E$=(s^)+ps6%^%XsWNHK z@B=B8%Yq0+>pN_4xXjY9F+4upAhbO{l2=uuSw`1dqdLp)yo;TSRaweIFws<_UMsAZ&Xh& z%?sbX+S2^DTvCwM0rQhbd-kyZ?eMBd4cPR)>`L*Q1-i@coJ&y&oB4XJkf_9-89}j& z41^LstgkA`oyu8W+}7XWS)6Z{FC+Z>*7PWE;{>_K7rEu8S?kBl>E&!W+?{o;pND<< z>dM|Xyb^`$|LEUa*&zM*p?+bXAz_Oy_z33irq?i>UPZ;aAth#jxo zl|@%7*(?+y9^BI^wPIj@qEqmGiq&~W_9KEj!j_)D}I-=l2gV7P9rNyYiAr zMj-rwokr;{#_x*#f-^E-$2|Cx|Fr(yIaWi559e4@6h59~4N=fM$Eu?cd5(3pgV)^O zYO_T?ozdIQ@p)ZD-of<;YW%K0`F&&g+Iy42w%Z5JntW%oisHxJ5)vi<&zt`kBR}KD4NoTQKNuzfrw! zmAi7V1mo?0E2ym8auQwvVX5$p5&Vv*^>OY2G_p!>v66udXWCU)U|3 zA#B2tbR;@PeD(s*wOqF!ud?i%=a#i@L)ppeQ^lS-CS|P?Je4?~Pb{aexYgHV-@2_o zy3*fv744S0T(|U(fsaP*U+ttjN?wU~TCR)C&AGknn_0%rOz-Dc*Y=xlTJlv)TV3v> zpIzwZOH;UBSY7`%y{@8vznpSJ^U5FcH%d2q#TfP!e-=&PJg{l%j;I6o7{6ILl~J(h3XLgpejrXNsS=6GDrT=a%pLO%29z(-*Wry0y7Bi0FRXTMRH@Sb7!f+K&~ z-Y{)YNf19+>fgxDf1~<99qV=0u#m|sXRy67FNjSLZLH;ZBeX+rgVe0jKF!d%o>w;q z+0DK#Ww4QDO-iMI(AN1|&Zh}WseLos@V9|Un){7yLS)0KA35um{}k6tycgJeEa*s` z)~kJ`bxFH9ez=x2-JCZpHC4>zMktG#h;v+3@SYHHPZ2nbHYoIRvCGnE1Sb3bawt*<1-nW zR)^!iHLFUmJ3hm*N^&-nk&VoPf?v%+iuc}p`WEilclWK_hq?Px(%aapf2o_jT{HP@ z^yv>$vnw`#^1ffBw`+Iequ8V2M~~ZI=P~V0B4&X=7}rXoMFeF$dALIHt%u*qhJR)Ax0`RB5y-TV zqeWeyR*+Gc!&Ski@x^-qgT?@sL&6>Z{9r=eTr(Urm>zPv)K72VY*7-J=kTSR8!Yru zH9mGF@0kllPf}*hh*`}W*Uy-A=WT8$*Sc2y_UxQ9F>>ik9z5S_yz71MA&v0VPxk`1 zYHUAo`|9nSzi$^Gi7eZ9ZzHc{zNXdpc2~L0zbbZxrWmiw{c|GVc+a|>jjt~>t$%aD zL$BM@a8JN8-_)%idsby_dSLcK(68NY{iXdjJ44Oa+wCduNV-;CyKwDfr=OnBipt7Z zTTj>~?#<&lBD`^$jO^Tpwm(lg*X_IdYMPN~tn5GW$y@gppRl#boWK2e$)~GZZwo!H z)pak(Ov;u$Ic1ykDVt3^cCi5~bTy7}b3OlGzp!UPKzP6le$lcsENM>9Iu@L+bez7) z_>bPboqzP^s;6ib-TLdDmr*@?`FDry-@4rP?Fxy0v}^X$4mOP?YQZ)Ri92}qaf+PF z;um&EobLbC&t!kV*JTR|+7|?{Y3a<_9aSG^T=qEQC&y2}EAh8f=PX;rJvU6uS>##Y zT;u6anks##|9K)dqxBkhV5aigt&cVd^ZeiOOa1(p`*0SE;Y&U23vI#p3 zR#^1Z^+6E{6N(r~qUD`QC@n zTd}Cyx?56oogQxATYl$T*zUhSw;ZuP?!4W5gHW?S-_~bxRhf;x+Ya55Ieu%-ky~>P z+zL2z`P{enlg~!H;ovy)rbo|b`q$`lMn(QBqf%3zZQJzdk6!a`D@QxFgObu8ju~;* zpYSjAUFr4e3`_Onx%!6-Cp}&(d_OetDqCTKQ2xR%7R?^$@&=ZCs>XPO_JeJ=2ay=mY21qNSbu9*GgDLGqG zmukIVmu=M*#`Ujn_C5JLSw>k_z2Laf$*9JeX?6SbQ~vHc5yH-~t1&!j$9wO(QwsK~ zB3?5KFD9qFeo?U`W`Z|LM~bA4JZ7O_{* zOgQ|f9b4G?*0U}l`!4eo|5aO`uHF%UZA0?-hKAXSJy}9bR#R}nh`mf99`>@`A@7&I7!MdfC=fGU0$*~9P zjjgQCIci#M6OgIBck0kjw)w3=SwA(4ALwh}mkg}qnl(R&XQ_^bgQnHrfWH#hSKdngRundT!ER#}wfDB|aV@L5&o}tHI}U!yEB8PB;zxOJe}0YT z$H=-TF&9o5eGM&{#S?VJlga*7ZM(&dBjPm&??u&#FmH46X`iLc^7i2Oecs<5I%~=A za``bkL`-57YcN*MxXz*jWOb`$KRgv-=-gby==3dFm1W?$GrV#X3x)C z(72~^SK-08*B`h)Yuj+F>RkG>Hl7Kd&)8y;?+WkOEA^WF02fPH)*AnY1}1?A+cMV7 z=J%e;x;v}*QOUjlzAXkkpA;XTGk;w5|FBN;^2(2f-jZ^)Arvs`gqzC+sT(dy_X*K1N=*&np|%{`tLdGE}jw{x%Z{+X7X@#&$5ul*Ov zo5%k@SzrI*deH-(JVS*Pb_*crW=XF)ehtZqTZ#xHpw@tNz~z z?38C%FY^EM@&mWGeD>J5CVt=M^N|{No>lL9`1soAqp9^ul{@=>`AwE7@f5IFQ7ywf z&tbmKhK--S-!&E+9-8>b;AEmbzt+>>vc%o(r}uUpJ#^~a`eUs-UkexJ$uZ`g_glHX z;^exx-v2=-C%-;l$Q`+OE$=iF*WD(Sm+DsT{$jiTm0z*sP4m8J;i=yi=J=fX<*{rtdIX^qd z)_$rfW`5+3AF-#>T2_@zk-lCq;rsE|wYxgyc&<*2YnG5-dHTdAi__~~vj1jM63q|G zet#-q)sa8izSS8^FD;f@JiGSxmyNL}eEPpXZ7x6l`R(+Ziqp@&vod^t?2njUuJHNU zvBxB1!nT^fTiKPU)^quN{K~LR_9wRVN8WO&{{0~bl4V`>piOZW^pP%E${gB-{%*}iuq=G@>Je}Y_W9O5OGplA!>rI{4uN8jKUe}B}z*KUb!Rps1^{OA7wvIb>u+;Fj#2!~Y zPkV(g`AXW!8`fx=tXybx`q4&1KNeBZ#+yx5IW3#rTw4?QE=ew0b6Ms4fn(QZ%vc^G z_4m$)4|~1_Pd%r1_2$B|==s)L4i?|My7h@!_Ps@MZ?6iREGU%w@p)co(&NV|k1F(5 zy;{@iUlJ4f=6J-Ptw%ko1s;Vq1#oPskhMLw<@RrddDVQ<4?e&7_Og3_`R4_HR`37L zUu|oivoGk4-87STk3QN)m^=`ub)1swX*7Ai`k|U;pT><<6EzFMtS^ZjlXC3KS-+=y zS%}XVwdi*AE z(Zdq{raw%bZ1aD6bhS(FbzF70cgBgWZxb^!#1qr_H;Lq#^u!4BdAogH!u4)(`}Wqo z4&CqcRygtJ^PhI%=WaJ$;Ht_s>B3>9Gj&TQ7;F~SR5>K$EVSJ)dcu*ni7Z=IR5ZqS z9eEKplSB1w?q^2#kSW4@w%MQGv-fbta-aDfJ}n!~s@dQ9aJ)NX7h^v2-;%DDPtq^D z&PQ{rY_pzO1 zVe@jvi#bwrb+S_2S54y2cK9(S=c19% z-@X}ATTd*j42|}QcpdhE{p#bc&nGTvv~MF6K%*&Q3V=@Je8cO-x$f7M{s7)pQT1?pUJDvfW=raK;&x zm;8#Blj@oun7QBbDVVLMdT_BbH3S7~<=*_t-c!^-d8G%hdWnKUJN2;Ma9%}P136q;~|7(nBWaWh?`Ts6I zO#gqV*(grxR``-l%#wV?aSDN)%VI7`o%t!8$@|X6y-RV1-=r|kWuH|PBY2p1X76rR zd?&T&bP8AJryVZ&iY6O8!#2!#bZy;<7x#JP*2aFWt~a0a-{kk>6Qcb_3dfc&FI#qz z`SJ_N_Mq(Z8WZhgBfnJrULW#g(xd4sfADqI#?92eTUKp1)l*y6`e2v$x)Q$?F|9v! z#E&bRzP?=P%6rxClXt&>>Zhsc9)4U?B`z)ab4lA}Cg(aP!6XaS9;^JO-K`vk^Q>B> z_!nC*IJWpjIw$XB6?vz_Hkq?77b@yjp5c_%Ntxq0QwvNf;P9$9to%D+of-))t__xkUfvrqHoGFPiw z$M+=lzhz!nms#}w(wk4;J&jkg-zQx&^Y-=Yx4*nteDH$$W^udCd%RDnO+6x&xAl=j z>(A$(rbpW9PG9U(E*WsKg2vYJ`Pjzfvx7hKxWgu8>b(rG745&SAM&Gc;s@XQRtO z*^v}8D%5|+Wa!sg_ksW8g$EBVu3r-9|3&EJS#I0PHTUNg_|>iC`snXv z^lyIL+9wK+XNzuo-ye3yQ~Z_o0^j5bi)CBI|IO3b^Pwy2_rFOe>e_XcAM3vQaU<5s z!Mi!B{A$pSG{=9+8`lSY=>E^re}pf0w)CWV(p9Iu_dMzPE#oS<)6VEnaLk8KTjqY6 zaUtf{Q{O+eET3nZFj_C&5Ym6b;Mi0pqn>4D3-`@0)?O_vTHonE;_nOqA+Q`#kr{x zOWgW}Qs=4^ihj%a9rLA^fx95EPxf8ntiEM-bAGuWSDl=3=+LUZO%-2vzrV(ru5sNh zlBGDmk54-wyXBDAgGBMS%h_yREZq`v-PC1mcaZLRr5(>)3VKrwrdwF;>A$sqs?<)+ zH7>SmToiNu{kZb;#!5z!y(WrRr{3^2Fnyc-rOI9_^(F`V{OZ6@59eLyY%}idG`MD+ z!n!EgHV z(mFm~1;wakjDh~;9cI@YP4t8UzMYyb{Ko$7{a*{qCaW%p_4c0i;SGCmaAnb+NArHL zZN4{c#w+LD`zp8AZ0$O$V#eD!`+uJOi)i0ae~nY7zo+d#7iyT9aaWMv`kBs^>uSe7 z->LilcFo0abGV)!dp6HGQv8PKvN^9~KW3$U|0mA?It6ggv`UMRw~zlvuf3|%B5_z} z)0ZhzHuWx@X`HNcbj7?$0UDQ-CgtjA&SFh&>+(tw$aPtAG)OXS)$^5YlQiU#&o2;k zcCCAU=K1&I&v(_G|Gd|3|NGL~{r~6eJoo!=-QM#%pYJkJV~QQcSG=_q-OL{-QB%nrCJi2kYGV{~phL{p5P=`+G8)+8>Nh=2<`L zJLiATeAC_+w{2(KTCh9TFYCj>_G?wXXU(jn8f8T^~E zy~drrT-z`4ui{?O{)(kAK(<(Fm2=Osf4m!<7++{Ga65Bw6m&hDpm4Xueo3`0OQ{oU z>zeuZGSdrQ*uMF)DgN6fpWDX^lux_9_gB2q#2YW3w>eGY>q5ELJbO*Gv=7Y?5u5f@ z-sI$K(~IjiZ*oy}w~12xQ+(hTi|eNX->mzI-si5C&O6sQ?})>!zw`VqSw)+={mz?v zjq6my`<)E2$2R}0wU7S#a-R?1p|F1v>-xK=%sY0+-t>;)srM!!GJ#!-`6^W0mhE5r zXTq++Jq8<`{ytEjbix1kPiMB}b5$g7Sd z7EOQq`}5=_22=L@w0-$^?cs2V?Rx(z#Y@&?C7=HGwN6T(XYY=m#xM82zMwT>=Qg$y zj*Eq`MRpRx#o7)}TzJAU~d@=3c4Avym-zw>f&#xW* z{yTBanycG>OFj5}W7j-Z^EaJW@)wF17_xqtKh^h?`owQ%-b^?eIeCT+LtC-gjj)#o zw)sX({KZlCaiQ1Ap0B4hy7gxzTv*|g;NxAicrJ}e+-X}`B)!hgo##|#wTorw zmW3N~r%Iil9+P?^VkN-QkK0>thqhjw_Xk9b*K(HHkVcP zee2iIjPJo`Zv7KE-e;Tq_vDdR-TCo4zPyp)GuEzA`!_d(=kk;LzBe1q7;8e-3);-& zpRjG?^@qoMmDJ}N%w2a_q2YW3!zw+N4?2nr)h>)%RyjBrGBL#z2uyJO>Av6q8^hNR zmLBhChq9M0^pU<|S^F$u;=T*=T8~VeTvw>9IPpvT<9$XcHl`m(M2^RYEsot0emjNd z_`2#HW~D8f7k{>yUDICrajxgR`O>b%Z@&DTdNW^rMXc#g&$l;Tv@U=DZhg>PW4oH% zsC}2C;`sNoYYoX2p|=V|5>t$MVMd|lC<+i{Nf)}LU_ITghT96}o1XZm{I`d`oHJLN5uZGY@9f4lg+L-vGc+2M}!ris0l zvv<26cGha&lro2%Q#t3Y-`Kri&9|A0TYIcFEOpGDwYY4Fa{Z%KJg4Fx)p?)16Seuu z{$HQ|xPSU`<3+z-^SAHqoJ+e+H~tPT(=la~TNM-hX-&2B+wC^aJwC#+kyEVJxLWVK zAR{vWpK6;|g#YK(jd@b8IYo!IO#0n?PN4g;i@w;>pskhG;;TQJCG<|uNUCLA@xsv9 z|Kw!JPLJ4DiGywej~3RP(V1aB@#~!Je9!kQvzlxUkotGV=E%o`tcyP=Cft(KKhOPh zo5783C!aS&bkAC?!k=iXk=M6(!kf!SuSp4PKdrrHwNzRrpU#~XR);uW-&xmd%Utko z2D6~gltDe2f4_EoK zTYdY?^YP;mujcuDS7v&rJ{QWUekZs4?49dht&?sJ^!TdD$HNEb45CPwqkcsym+EK}CDFwBx; z`LO+8yu-fZKNotX1s@B4?f+lcJ~jMn_0PwPJWakn$3K%8L$!eVEMFvqGHeuK+Tg{Nz$(mC@<1RV zk%N)ZPe8%q^Lqw+>yjJW=2_?VNPoR?E$+a+$2%_VcE4+Va@(uxT?<+^EMRF~@bmPd z9rt}M#|PiyK6kh9%C~nHzxg?Z*Y@w9^xEcp?p3=D$ts)IHQqK-h-;az=>B!NX5#EA z+{TGhwEw;2>s%$ssb!yL`>|MhdCK0@n(fMkFL>00r@KU)f!h70NSQ$+x{sXMJHz-kxtv(^%gWm@6trR_>gaV{_@) zqJYb+@`4T~XBU0r4%+j;J z$scv5Y_^}>wdJ8n#-|U0D_DZpbU%&neV+7tZG8o6YWJaoqPwSEH-B^N_PPb@v{tO; zdNrd->hOzyFD!SR4Z8K?p^k|BwT4GY$r|Ur2OQy^5pai*@#x=ePU1TYL|$G0$-wxb zh~vfAgN+Our!g_DJZ|jZ@OH5t%ar`ahFC6z?Xiq^M9;taC3h{Z(Cu;Vi(A*i4o#hM z<;GFOY^wY=Q-u9t$H_S5&|K47&gw4TvW+;>Xx z-+}_S)53rK-*w&lRN>uHy(RPR$+~Ad!ade|d|UbCUUA$-Wpf`bKZ%K-W3JA;ykAlD zq$6kT)dCIaZ^FKdN{@791ZU1jt?mBV)#;!4*#EH86ruLTs?MCZI`7U-b>*!(=l^!? zbeVUdl~(^$Qna7&M~ih`<_0ux+~WkWO4A>_S%)d>VN(Hr*ipQ z(BHWle=k?;`hMe~Vs>R{`seIfUjlml;}VXqz95tRaPJ(?l35&zx+{f~l%GBFQP-Ne z!fpTbFZVYbEcwh>ts57(Gdf3Y=l+~4tE~kRKR^B0I&&|>^N(%+x2f`Fe6!i|d39Fl ztiX}9@u_oq)-wY|_!tM5n2vWB2t=9TWMLf^AKZ+IsCW@6K_pTGT@`DT8~_l`4d36c_7%75ZF%R5*2@>Z zxEH7qb#vW|eZRju{FRHb?fKM}XJE9y;$Zc;bQhVq@x|qCyLa7|w_%k1 zyX|F5a%+B4ukZd9uNwkH+fnQy0Gad*k~Av%;^l*n5?hJ@T2q`y0Q1 z_0kxTPIvE+YahGpc6{{Na8A@RLW+U0MvaAmgCikPk-@W#!A6>C#jO8=2B9uY4tqTo z28-;f;acVJ#aYy1;-aS2D-1$Ha)MpXUvs>ey77W(sG8z~Kxv7m=5kk6Hs+s6&zpbo zrP0(k6$M@q^**5iC2MvcUFE&*y+p~WOYa{v-6`~bclCPe9Fz|XDURu6sDDW3oClb;vgY5Fid=W27^N#386gI|7J zHu?7N(tXum%Jyyd$3sjli)3>88xFH>kD9iRamUrH8#m1|+N51wkIg=3 zQr?lOI=_l}POGJ0W=?JG|LL#KDmXvVlAZTeD5fRn*{)NI?6yZ(bE%XT3kd9!7BAJg z_eIQAYu49WQ!gHzJax*;uy3AsGNPH9E}vnydmX}7w(DqA;F6t1>)-LT-0iLM6_1%> zeb>ZW@u9KRf!>V&b{al;sjtL;Z0Z&)x!`jXVB#>?+YZ~i*3a{bt{Fn<<}aGNcG z)_b2Hy1+Tve17K7FRxkVziyFKD6=X3H2K~E=SOS zH(#&heegwk8pM{x@|YUchOr)dq{QQ9u*G+h64Si*Up##tt>&Eb^2&lIk+Krm%r>{Y z_nrH9a5A4uCQn<6=TiVPGzO#~V&nt<_(WvZoQ`*3{-m_+r14mrzPlH}YgBkvh zR%o7S50_-<{@LQEn`W0{&|tFcc(|Uh;5)|Tmr7i1hBk9lW;gURKUyupF#V!+ z<`+H0*BqX&vafqV-StzYe7|~AXYapNd-B6~gBjL;q7v*r+@5pz^)xQduDZ!Tm#Gyz zU(R2bHYxv-&>H9STIUOnzl!?WTXy+enC{J$la3zNz17Mg!Qj07#M_c}XIUcZ%BrQ0 z?f6$GYU%!e&m`GZb+=akvQ65dv;4y*`Qrz-ZGGr{dynX~)29Dymb-^ z8YAWfhV@OGr&@WpY|J~5_~69VloXR9b2i?-nyo3)QZKt5y$v~ZJ!jeeQ#G7-l;NA9 zFZ-l-RJ5axfae)_-|z}?=EFd?dX>)uFe15m7dg{aZT|nUTL+dJL8F0%wcaK zGlvcBepge@zd97bp!8YV+$A`y+3nMY7o0q2O%%>Bu-!?JIv%Ke(=d`}K~Lm-pN8oP zPrWAl9eu-RdogHZq{9qFo*jz~vP9he@9!y{w0iZ+>3!~3<$h@QpWPU>^YiDs0#&*B zE>TMCstjY+HdvFxotT+v)Q%7 z`{oygWOXn7wEy)EX^XX6)9t!_{64(Rb2HxQvCHi4g{z^jw0+GkuIpJBa_kqEf-)DQ zq>xd{l)tBMtTWj5?=*u*Tic-z0TO&%$(>CG7egdW4%SxxQB!|enEC0$qEk@;k!mwK zm&RFK{1XLW;)r9!Dpc2_drSusZ(z0$YPZ|+RpD__$lS*Q5!+vg*3>f*N`2z;%`tuP1z!w`_b`IJZ+= z^7E}IdBaX7q5FOlIPG$KBDp29h4SoqweE^}OgN9AnB6BD(MmYlCO3X~tz zPk7cPc|bDFPbPG-Wj0r`uJerMV9x2T2O_jMjvMmuDo4uxxSHPkeOi3xu8uD2^ETO0 zYHruh+&z`gC%ti|^=h+nrT7TL4K;m_)D7Mn7yZ9$v*Fbn<_gx2b#Gp^9MrwKQ~t=l zyMB%LB!5oMwvPY7^Z0OAMCtQ7)#*`OYFz!-Qs+(AefH$=q2-T)yV{rnXZL$uKf2wd zt%FzP;I|*D2V&%yJEy#S1WNZs`L2z)uzdNx|$je zzCZN2Z?~mORnnoK<@SNj@>^VcpFQm4T>0bvf#kN|b1q$Ik6KmmC?&}$?cSZFx9)6v zVvaxAd;faPpLOSAYL7R~-Lj?VQeP8);B?6+Y1^Co+=8=ZYGQ?+Jv!9fb@43s^%+M` zEGg`%n0`d&&W_mUUJ?gH(;lmsyC~PNG#g9{x$$^EL$M@}!u*>SXJxNVa9up{yV_Zj|@Ec@zfGRHnSUod^dbAHnIbn`tA+w>Fe=e&)0Cim;( zhD>uxydKERJ?ko@59bE(^< zrNv7bOM+f6uQ}IUwDVpXd+h_!t2{9Q7NONO8myAuZ~c#--g!7XBCPq;4S|~Sb&QkG zTYqCcyxGO?4OFbN&eP z3gu;fvFO#YKvqe|DuZjuarRP@-PV`-1*229HW+tVD;>PBW^>=|#}m(mUYBr?|9X&T zdS}%}uHHQz)=Y8d)x7V^<-fl3i=9)tb31dnlwW{B&(}WnsR>86MK))osJ?q#Eo9I2 z|47M~s}CM@7yjI^`_9P~&*Dm)C$QIU-#zDq)af6K-DjLHD}4R`X27&>)saQJV>ZP4 zE$VGQ7$>rx=Wq7JW&P)^^LKO0o=iRZX;b%eoBQ`po28ZXw7JaKGw1FK%Zu9i`(<8! zVVXN9wclh>_N=LotyCYe@4n7u{q2f<`Yr9<^=}wrlXac|H z6Tj9yqLWx9kH+Wl?hn}GHDyNWo*BC74S88}*9n{Pyx)FlLVD|(fJa?lBC4ka{WfOV zF8)|ubJF+j1NM<(yE?x17m6nrWv(p@Uiww)_CAHzp~rlUE?!{EK6|`o^KprXlh++H zyy}u-5x4Ek#MHT>#oHJhb_&d^OtAd%XMd02IgkGJH+^JIS6p*FAe?a~(61mi;p}y% zzv&^1x0D^(>L-|Ur0#O$P4$Q8w{m}C(s~qkXNvaI=dxz+3k}bA>%EWYzHPgQ=-uf$N zJvHafzCEpB!f}uH!K+R#V)|@>UcsgLnreR=k}jERsr{9Il=Dd~iuLx= zTbGh^C#F_T49scyJKb*SotXv=-!-3Z)7oY8fBilE|7+(jXa5)XfAO|OpEYFiU#s0* z)U+o}<#Oz?s=1<@Kj{d(30l6Tw_g5z`MH2My=!h9ll|BG_22}lO3#G2PDauG)|>#% z*aF6>+gMj5sBmh{C~D!V6KP;=spQ|F@PGP8`=f6Q18nBAAH5&2uwsMKzDU7%=A+9m zEv%4v`u~ULi zwbJCu|NG`2ye3VU&@w@F$`T#r*DBuAmTXG%-SqzUjqPiMC%viO)^aSbCGm|5&rM$A zZ@E2pBhq#+d;fmU>zL;;&-a~wUw;4po^!u{{;|2Y{`?=)`4d=+59Dm#rY)JCpLAqK z{L!^(D?cne{J-kS#k1Odd%|UAu1mO+uX%Kz(dD1Fru^Ef8GbZ6TTx_NyptI>+7v`@Si?R(%$HaIbdAg99q}PZ&%MXXIIXCZ$r_gpeM)ZeWe^lM?X>Xev->P3N5 zuBkc-ya z4tzV&#klJ9+*Zy>ixNy1&o=*g>(bdP%!+c_j*~y|2yyTAy|;*So}BIGGNX&Pt~F?@ z-7~(v$M#cOxL0~v(rQNUx}X5< z5B5Lw74x_(WrLSp+1cwfMapot%gvr)P1q8_Gp@hZ1fA;PNwGSUTjt(Z z<2?UYu;!_O!ph45&M)UUx161wZ~W9Sd{%AWMMi(SfcIK2i&BImuEefco_ufjZgXc| zJI`-N>jF5hv@}lnA@cr@`Tu`E^sk@(cl!O_AEzT*|9$HBtgqb8)+(;U74Sp*`+s5k zx1s;Mnf?nIx^iizK3+IsV}u*m%{R*yPFU=!b;|AkUV~G|rcG97?RNeD;rjo{;eA|z zQnD#ROEnK>EvVV|ryijm)a12n zSJP&``DKB6AsT@n-dB}Q+wb3fpmf^D+Y378?6p4^U6{Fgft>ES%re7t_2>xMANifV z-o+cYwpOyTw1}Pdm0ZI7N|FD$+XC5A$#WB>^%tzU|6~H&%iHgLbkfrn%Eq;w&|X~Z zr*ytnN`Zg(V!zi)ni|uan3idtxOrrg=0VF#aotT#b6n@m4)D^LvWd@er=*i6qlo9{ zsiLd29*N{W_&xOpliBB%^~+A&`Du1}_VuX^=MBGm^IBdF4LW{GlI6!1v*Y@P>h~;* z7BGFU{kWb#xN`l)PyY|i{`*hp?&bYUOg`ve4F2Q&?rC6$Qx{+BlW*LCX0O($1e#5I zll|xZ&WG{yG(JuLzVYAF)AIj|_^?9v>`A^-gShXh7JlgI};7YR#MV?pGXMZx?@n-^mh{zkcnDe*$ESGPa zWAJUh-}Q%H2PWuON1u7CpS~vj@$|gVxE(WB{QFgZ);@Jnbxo)1iqLRTJKokj@l!g- zOOLLqF+9+8^8l0k(|M^p+`sy|S51<7Djd79ES^UpVVfXhhhcX_V_V_g1v|Zh<~Ez@ zxLv5TxjfzTxBtv5a`9;w_v;mu&PqOi`bPiqUuye)J-2Oj{iW8uYW~&J3u8BK7v`OF zFvqKAf@Io8eyw9)%g^XHy_VUMFny-E$eK8|&%(J!blKhRmn0p_Ix%mZv8&7J!nE)^ z>)vdyQHpZ=dEm6_@f$))9Y=lA=JayidhFeJ<=eKFDaV|A?k?acVzW?>&2NzX!)v?! zF<1TM;3)m*JDa9%SNpufPDT3eFQtyN|4M&ycWk_=x_M(2b4TH{mA^O63_Jh+z^+c0 zN6mUhA8M>NmTNtc)5~vbNw~pO6W zIDAiBXENi=E3wIvygBtt#Em82v^M?ZDSTgcS+MlaH|`^6{J(Li9$daVmwED{PkFT* z8=bpnz17kBq`57yF~#oDJcp}~i#FL$T6);?RoDZi-7;Gb=Ciz=-ZA4a2S@lzdB%Nn z1KCn8<%LgKy^*PeQTI#39fM3IF^_p$^%doH9Vu+x=$5_minNnAFW22fyiip81&Gr`I!uXk0Ur+2_ZlG6lShxPmmDTC@ zDuPVU$;xVXm%Zk5n4A*xMlEt9+p~bAc01pro#z>4yvi&mGCzvpSgd;QU*p;qpKp#g zAK(05ofBa;o%!p|%hKs}$&NE-CR^{lHa+{~PobHY)?Kn*d})ELoWr!F%J0mxLyZp| zdeO0bE$anUU$tv#tJ(HHop-52`MrnLxlNA`rLH-_;4+tUWwEkv-76KnsEltHcORW$ z`{_pH&pT(Ny3A&5`TMhB;%ZJSBjNuHTO&L(`j!Nn*vo#*v2&}`m>6i2q_#=hWr|Vz zqVF2>6s5vm-`5jK5vaHpw>baDNuRpSrn;-2CR|ySl5Uk^Flp7n%vDbHCu5eF^6zp> z6;gS6hp$;8Lz&;>=K=Av?*k1LbY=ffcD9#Wz*4iRe1VZcN_p&xpt4kD#qYg3FJ1;Q zGwo|vF=1bS@atxGX7MjSqhEfidvs|zbKl=1+oh^273VlCdK`WX&CQ*Vn<*8xMuAmbgAok=vg4h}1G}jt6&?eTq(MUP)GteO$}GG4lM8rm_h+ zMK>)T^?mcZ5d9`ndvUO+472^|M~}W0d;B<>Y!|qikzZ0P-|@E$`_`(z^9mDB&$RF< zXq@fEGXH9Y&Kd391&wKk*?spIUEIz4_{hP@E+%Jkmd9I4L~!0ZdBEg&afR@tExn;O zg$G-oDBbdWySXT!FM{FcnN;<|k5-g6c69Nn_(k*HX^8OI)vsyUwjis(BH@9{-xSXO zW;b8AOk6fE<7)Aa3CR)jov-N2EcYsU%C~X;ubVy(CfcdCb-VDkHT(&`rM;ikGDy~^ ztzvEJtGKD9m)zbLgnO}0n&zgP@T5r3q+L+*X0+!=&Bsd)KIYtV=bWA7!XJOs7pm#^ z$6l;gEAHSu@NfBt2W^sVB8MM_gl_rx(=n*xZ(xYihS>9=J9{E}Ui7zVO)siHk+wQA z^~B*l?4l=}MD~4{`DOM-A%2-_#g}f!9n0Lk=)3F8G!ZRW4V# z1e}!X7wot8V_SUZE8m{aJXfE!<=(QpZ=8H{_u}MhP6|OM4}8y>&lS#Gv%1J*-aP;Rov)iOJ?>Nr z=bQEH?c}SAmhGH$Ah3U9&n;=iYW41w$HNVU`&xUSh%Xc7l~4%G^*c0;V`Y2t`45Kc z7d@A%>ynq*@Z-nfTk*1AX0H(<(FP{_^Vmx6^suQmfa0J5c&;nsQd^#p?dzKW-aNaoVONY?kS)>Dv%~ z|737p#^uV+g)3K0?(QutK37x|m+5!z<+3ML_h(i1_1u2KYoX8gvpc(EcEg%yMGyD! z%~k%sdhRDR&PB^^SLZPL3HdF#e{oUVkKL`?ZRTv+^)jdF^+_k*uix^dMIVP>E7mPl znAv-6atLqqrk}G51kcS+$@9OYlO@Fdbf>wWTi(&b!aWYlciK7g-Aj)@zvyI!o7{qT z-_9GW?3})4hq;JQl;L!D9u3d6tY_m^t6!OHFD)f6X=@{6U?FuXJ2v8Gn)MyUH%m*l zDb?o%FgGuF$6I1nP`=c(zmp?MafObVUNbve^u*2Q9?N)5xAZ!`sWFdf?;6AK=^rNC z57)J+`^p{5dhWgbBxc(qj4gxa`vFD!I6JpME>l-t7Fb(}&Nd zE~s!;WDUuh+{JQk-BG*zlq+FDI)5T~e|)ld8_6-vz^Zk!@plD{nP)$QJWf0298mo2 z#J5jd4J92PxQWFcI_GS!#C9&>)~~V;yQhRnZA(p0*E}e9bJ5d_yDM9c7W?b(pY&mN zCRf93rt1bijy(mdXZ~ZbXl${R&6xUo7t`C^)`M&ojrJn5x3GUpp3|PPW}8C@o7mR7 z_Q6le+M2!CqGyXKJrSx)nyy+EaIYgvku5Jj=i$cbdw$=WW7og)evyuodI75{8^bPr zy-AUN7cOew`jOlDbCDCn1extujn6VKJbo~yLbP6h!>~zcs!{aYQx_i>yjU`2!j|OF z1%<}ePxYJK3vcChDG2xGed1LPn>gdcw3N`8^r-Kqj|yH-8(KzR?PnIcxd;p-4?0$-WlKheQL|ywfk3J z*`@kXp?Kq&sXmVzTrZvJ4sV{A)8M!~=baPZ)NhL~zW$zXpY6Wy{wHslJGF5u>(+!_ zj;gF%yidzjYeAupeX$1?&lG)-yYo3Vbgj3P4foV(Vg70zBZ)w_AOiT>hs%MEH`GI zn;PF6xKk%$LX0)v)ZBt1zs9$nVe51@uANg8`+fQ8*%NkL4M^R!UUulu$6=e=Ou!p7H7FN)``Z#PZaZTk9RYH8ZrUbK1Yq64t95 zujEa8e?$N4`kRwf*2SEiv-{^g&esCP^R4_FU$z_meg3PY{M0Am_SktGn{5;H^4fPA zre$fGU))$1T5Dt@@oUoWX@@NTZSQ7z_2F;v?!|lNPcu4yd&^Q!t`E28POds@^YOK!XKZ_!{O|phyqns0-)+9MaPq_V zdry_w$L!0gKP%KfV~3*j&tHFTRL*}lF?;*|3tx-`?H}oR6>?z%<@-%GnIsRRJUzhyJcZfn1v{G$Jm^mER3rl>^aIzM0dW zC!1cKqyJ@7^qaIJPl})Nx83ty`t8rTaMrX8j?*D6`<7l?KCkLVOjN-9`9%_!A2Tyb z|2y+C?Y`!`t2&dL*%h}WZ;upx{&S<-x>&yLm$LLDc)6U{=!PVG=uA5-RCnDv=4M92 z_Z@3I9nzK`Uhh6}*Ke15ZBa)L9S;e+wI{;d^ZDlKT2m{{d7hgsVwAeSuS1eYxAa); zmEhKB@wpLfDRFX4Z_?s6%6a@zTo>F?qLJ}??we;z4&Ssp``2OnjCs``Vkdv9I`q$@ zN3-|s#Yvxj%`~0*{7gz)`^zUYe$JjKTKwh0v$uB=6N~Tq-_9xNH&RyRSd+GTedOOu z&A;k*u32NercEwsap>}}`-jdx`+j%Z=39QfzeG%ztS=P!bvK3Ys^jwenM{+4|FC?j zW!?8vFMi_ZUjgea6xQFg-*)x%S_2cej+OjtQzqXln{>;p_tC6O<8`9%MW%ZQB)t8j zbg)`LP*(r>%ohL7>5nfR{I~D;YPDngcX%HV+MwgZGfCr@t`5s}<&0Iy&s#-q+FEV2 zeaO8ef2r#kJBR0OZ8vsXRSN`to#(6hFy+@aH}RiuYwmB5)XlRF_u5xsySduN=>M(r zQ{zr7lNI}}!E?au+m)SGEnUyw=!8YA(&&ErAY?;Htx`#Ey&F2A(- z+@@Gs@o-(0rI_cTdbfn5{@HRpy&h5@wC>2uNg6+x8uj^NjnrD*Dkl}s`Q^GoC%C&6 z=h;LrSoz*Kb7w?;dQesONAo31=hmL`@V=b)!0z*jqcU!fxfX4ecUZRe^jn?C_n&WD z-ujXr%2)hRC+Z7-b&&p4?lA90fvLC7IWe!=@b0?WeT9Rc*du;-39o#;Vp%AM+m_`| zXU>e*OTT2Td((N{^dm;QTozr)ney&((no9lr4P24GrvvRUlS8iuebl%I^&|bzt0}| zuDt)R)xzIP^;?Z*F?8-uQzeE;(?nZ-EjOJmyVjtxDZf4p4r z`1PUgwr{SxTW4wsiI-V&dPujn>Iqq0^EOs#<$o$={o@P!%g^ffI*xUH%G{B)=j2-R z&hzV(b*66@vg_^pCpkqZOXa@tle32tlaAi;w-lX{ucdO*(r8T>YmewV-c$v%9i998 zp3N-l&n#cQ?(UTR9D7R*@6^Wq_h>6NT48d9{b_R98IL7X-Tl_iNeS8RHpTGq`+g&F z_GKo^D-Z5Ys_lAMo_PD{$rr4#pYmqj&nwLReRQs^w*Q9L7mly=UBCCvWrrDWva}aQ z$Gn`$%g{LeTb>Yqo%YA2vtnzs*2%i|yIp)7P&kwG&DUwni_)L|kqX*Ya)V=g?Y{Uq ztND3X6ugvQQGBb?PUFcJCF5nR!q;|bd4qz@OG z9$xlws`_5dxuW|IWjaK<_z%ncu0M44_~SJ*KVZA)OihE8DQP)6Qdj;IWmhcRH}69IZ|~WM{(jYxRP#3X zOzd|~U7j|5@w%EcgS6dy)wyi0Cnvnzv8nggw>3ZO{&IhPdHDhxt4Bb($a=YR(w7-7 z?@zpycC?0Bu3^gbFG3ZME_QzSz0G!2-u`>x`C4g#D)-_K9ydB=dOc}lL;WL-4_|(5 znRly{d%4Bax7UO_9)2nju5f*6D-IRILbW!b; zXyv)l9^z{rYOR08!7V)d*z7vbiP;l1{q@(M%eK0g{dw94w-5gc^rx#WIuzD)RL(y9 zzysMlCtIfL-xu;~6+G@`neuRvi`uVJ=6`=@eN4V0{jno>SL<)S`RdJYJ_l`W?7hac z{&Y{0%DV;n7mZjWm(RRs{2^Z>Zbz`9@m#Ovg+a9|$~1yD?Jj>45Egvv`0*2QvumWg zQ`YA0RDGwG_2M7*=DmRiA_qS*cRbyxU$TAayR*AuPFy;EjqS?Zk5jl09=N&9+hO)u z+W>ZzFNc@@aR|!z?fUoeTiwU=Lpfjn_TM{CYFlS!^(x(;E0#qbm~Z#|2VX+$H=XrP zExSCX&Qa0L)M}L4y>3}+uC?>0FYdN!{}{}kOy8Yq((yf3Or-i;N?L}2Pny>;v12E@ zzPmn1JZ=(moBh51-lsAW{afSM?$`X0K4d&Q@vf7&(vq*tbN@0=SiE>~iO1sOk;k7% z9V~fdqCZLCfbtoYD@MP5otk;={3`F(SeM&pn3f)k{riF=_NvPH-fIq9f9#xfB5~J= z>Tspp_1|w!@F@&h@?38JoC9y?)#<*ExFMt!+H{S3rb?>k#MR3+y^F7_hgv({-R97~ z(cp1J>Vuez%da2qnKapX#oeZj?!42bvrNBKcW!+H zPhw$Mj(Ni|mD&wuzgT`Juuokt^j2lTEC2p0SK5odEbdS>d~i~tAR*i(`lIUm8yZ?t zQ`=h~I7jj39pHA{Ja0DFG0Q~5*?kcQtL|_vPc{|3Z2nTRJonDbv-5vl|Fq!EbM3-7 z;nGcueK;4HtutnS8I`gx-cxq}y7HClS69M$3T`de;@Xuime&89MK0|*{Oy)4!!|?i^2b+|3$Aj!Jp3j4vw#Ehw!doX$KAgb zhFWgVFe-PPUZ5npI{lRU;m@BL&N3fqx>vbwY4e@=a$Jh_Ay!cbp2r?Pdn9TJ`wIyU z6ZhppiW604GhWqhPiJnIvMDupvCoWSIr=U`|NEM9>lyRj}@w{4Q>;xmbw zg26w^Th^9+ZF+mRv!(p5U}Z@6*CVu^`?_8d{IbJ%BH!iZLH)W?Yd$S{+G`SBw`+y)SJq9FUxr$4 zs@CK7y`C^-e!z6EHs-tWeZ7Y*uh*X3oMeCeFw2z~etA~@HIIa>zGS$D@;{lkq;Ao) z=zej1X5ZRXg>0NJcD6GdnjD-KP?}ryYx~YQ44Y<}b9Dy3`7kY2W>GA^(z2}Ee@sf> zFo*Yq#z+cU9q|^b4S)K_)oOxP-aPremLXBcJ}-;i^iYjq$J^bzYL~1F+LHUEW4C;r z`ftbYvAdL`6=q5On|zHWEa`pJrNU;Mmt|9PCq4>`U}p=@Jt z;Eb)?g3;BzMaD>$WxprZCWYJ-e(V3}ncMQz_tvq6J8!RE zQ1V;leyR7q@H3p{w=b=d?~Jsyy3n(I&*!7ho=n^x?Cp@gVbLX9S^J7~tzXe^mQAg4 zUV6kpYRZ~dB2$dTL{>k(yLxHF618|0op_eZJKoOAsJ!+vQoX-iX~+C@9m86?x9oPk zjMlRvFG&9qyYNawH8B3r&b-pZ?#Xr5d=gHMLf6F8EY|LRJWcj~QKFVm-PQ;OdtOd9S+`DR1DPE;S4>VjS!T_8 zDRaVCxYx0k!Dgx0^3}GT(QXGb1UJ2y%piTO)qMrmtth@b242-)>pp*OSf@GX)pXI~ z7h1NDSo9}lo}O`oSF_vd$*#;KfUnK@3G%b%T4+&Ag`|K(ySqMMIb&Xh`O5NF)V(Y~B(z3Sx~ zYTG3@u}l47wmFb^$3$>K;kA|Rv#p{l|2xJQ-6`H6?LVjL#zG~z*X7MmZ*%WEzwG}( zdG`8t?_k3?Z}T_B*q|7C$9tD|`U=YhMQ zGpZak^xrWoWsI8PoN1tQ!-=_J_oa+Er>~#TUEp=O|I&Ak#lcdG4r~!|$mbV5IFn1S zIf!k0>ed!1O^@z*OCGTAJ`&>p=-!!26T2Qw-0^6l;+qSsWgc_xnCIO&IsZ<*#m@OR zb~xwU5&RJQ{(J0vMWG_jmOJqrOfF3fjUS#VPkXWZ+^gODSmW<2`E#~}I)z;;KVws| zMm*O9Ez(0b@$FT^#_rqi0;8RNAIg0mEh@h*X+O*UTl4;9 z#x7s`cvVGOVj!=R?wrjxlJAK#JL_-P^kK~}Q(G(AU(%vjnJeIN^Y6;Nk!XG_a~Ea@9POSNpY-g_!%vmcYymv4=1ln&`0vSH(Z{c3_ia7)XoG2|>E6FZkNA^X z8>V`Fcz90m?;mFWTCG*u_xz@q$5?(^q^SSx{{6(On*H1NSF3(X`g_-VqWER`_Xnal zTJ(3$@809An0Mt|h(o=_o9hhLex_QvyJ{<#QX`oaN^AEXlEb zSajU>!rklVZXP{;{pd-?hmUfe*B@+t)n$FAb?DD=j0^$Zj7%a7A`A=+91MZn6+Tbmj-33(#=!8Eje$Xs0UeYSr54Ah7AF^F z7L@2Er6%U0YxiAhb?PuP1H(RM1_mt@-J0wS46Zqu6`4hP{spOdX*v1jdY*a3B}Jvl zC7JnodhYop`5}o(IjLC9Wfwd?reR!^RLq{`RN%)rpa%)lUy(9XcXAf633!4v99$Gl3c zhFI&@{kg);z;K?Mfk7U{klT51Lwxen(^HG|T#JhGi#!tZQgU#_+pLq%Ws_ML7(ihK zO8yA*-c-R2b56`j%}Ytd9`k*(izKc185m6Y85qP6+8G!a8tdUE1eX@17L{cd=VLcx z>y#t@C)61jcB-R??E5~r8Sbfhu4Sot*yHA)1N#$OP6h@GP6h@g6j!|51~pVMJZTabZy@k-gI6Dh9q7F1|bv|@!n=&NClTE@tL^=`9;`6 zDBGuV%Pl4bhO0~r4ALn2m%M;G0G9T`Qj3Z~#Syl`?c{+&9~ea#82$(|FbJR+wEZ>Q zppeAkY^>gxEx+-32qy!BKPLl&CW`K7|KYl^1Z;j@Nl|`IPHK@6R%84BTblf5Vqo~i zgkCZ>ax%h7T$t~IQqxk4QuC6r$CO_~#2j^I1_mXJl4v&%+^FD^qRhN>J?D(XqGCO~ zCDBw~j+87`28L8t1_n744=W15je~nQ093@5_?Kdj!^lZTw)3+wFmSOkFi4`9bzBr~ zR!C85v7T>gadBdLYBAQpnmtugZ$2{v!z^ZWC-KU_jd2Erc5(?gxA-O&1gBzm(x-^^ zXJuI#7$jI37*tW5^ginxV|Xz#zwhUh287gPR2_4}wzDQ!B6rtxx@)nMc(a7`CaQ zmwNLz!HoeIp24ZfrA4XE8HstI)-Sd?aMRT6Rcx#b42-M{3^FJYzu+d^EJ!r@Waeg; z;La>gwHhC4I2jnqInmSLgM07@%Yih&v3jd{-jq9Egcum!3!zuXy)WQS@yRU4p5xt0 zx=-hEF)(Cup~v?6-*D?eSs)pb>#?`rBAR2@i?A>-@Ux&-0>Nxdu-X?(YzL+0CKh2& zKzmpizqK$jFw`@mNBSx*Oe0;BGxD*9j_l2(3;Kc#3|fNdp_9r3Hzpvl1bZjRdQU(l ztRIJ3;vABN>xTOan!f|`3qa)*Qd!Hq_0!Fc-P z>(TVhzw*_Dja>roRobAu&z34t#@fY+$6Y{p}E4Z z!X-61qy&4-ULYs``ke#=!%GSDzDdzyxQXB{1*{{PioL3s{VG9NT#$i52!v5$W9A;X zLC_q5EjDj_^f`TxnStRZGkS6H;4EA}DC)2#&3%7Pxo=`&U|7q-z@UR-`Io)lZ;;=;nf07?$1!x=BG;W0U&v^WE2S>!(Xeq{p-14A_nddI!s zHr!04vItvvMcpi~KFP_za0H_SQh9(dBOg2#;g(;Nn^;nUBT;={vhunz4+Dc74|)e) z{T1A#(7cq?w9LHJ6t~hm@Q@AedUao+^YtJB1_oaN^d!~w4Q`%ueoCsIXG&^bNoHCm z4u^@0c2DSMVqoaP=#7Z7GsEgOSnC64(%7=X>(v8628P>$=+)URPG(p`KQpf+wWuIJ zClP09BXQSc-FG;ZRE*?6$L9|>2_AdUf)m;;!c}x0 zY`tc%fR%w^HY<8MX9>nM)2TE$8%IaJDLo?W3=;#xag6byyAg1MVCfvxS;jRYaA(G< z_i>U843QXJkVnyQgMw1iGmA@#u=k4XQ|p?Ow3LV8aeR28PXi=)<>cGvOvW78K-U zCgaJ%MS-WLvN14BU_&2D_`Do$2;5oFMBrBet0a8#6R~IG#lKSTeP&@`c!$v!y|50` zTtq$u_r0)II&Ws5H>zQ0U?^utA6#JF0yhz<{Q(*V(+f(>OUGVNq|LHQDHdd4$irxg zAJ`2y4(A9=uH!=0@0<(_pE%Jw#))U)rBrHBQGOBj)X>S>{AM)^1H&>F1_o`E(xBry z+;+@h4=>6rN%c)FNlZyBNyHjTORv;fCowZH#4JlEr#lbToriD2w* zPF+7czL1%LAqO-XiQ;C>2be}fOwtR;F9=9V!s^t9+9LO*oD2*LFv9TGV|WTQ zV0TZ?t~vKrSs54MId}F46tW3=F$42Kqkygd2pEOkr&YtcJ}!VRG&P8w0~_HuR$B&0n}-uu>4*IKtK} zYB;p6kB@_afs+HhU%|x60&6okrDi0SW#*S+?^5?UZ-|=Dz`!tvk%55=CGMShSr}6D z%ZgKy^b!jS(5D{P+xlE{;ACL1=0tBHaPh&dMhXpg*sKbk$&l`e%?Cb=S!x%(2CJObiT}ObiTsD4N&lu|O;G;!^B=y`{X8rl4ru%8Wh><6#K5 z7(LU18oju3z^Ci|k?uka3{Dt>CC;XB1Hr9AY|-jm_`u)-0|Ub>Mg|5>6tC=XXJN=H z2F?9oiIPUs&Hz#6DYnI*-Lkw0v~vp94a|6E}Ph8e=> zO*WQfxVu0t2b_ic-ttFI>MRTlN-XF;yIcl00UlD&0uS8E1@&@5un$6f@{m0Fl8J%g z2@`tL+rAoZC@f3kuD+hH3FLju%D`}sm4Sf=CDh~VSQyHRaZj}iSZrMinn$!@K`-fY zTHp>KCLgMPI3pm$%)r2lF{t{i18xw^Cj@HauZusk9b#c%*o!g2Y0?8X3m(*Nsfi_} zpm`2S?=_inStRQGXsMOiurq| z;WOX8D8IA-$^637R2ZO5)^ehc zgKMvYn+x_PbP~}cHK!mIdo5)@MKt0CBLl-@Ms%|zcfid;s>EGV(-KQ_un$ESJ8@2R zU}0de#>ghNyWnOa)m%7h-!w2#o2SZ*cHxXJlY#Vnm;(UU&l2Y1Sn!UEm4Shk z72T-hQ^`p6d0F#{nStRcGXsMGijP;E$21C&)R29QJ72N>Vqdsbk%3{E zBKiQsgUfK!-E#7AOiJ>kU3&@Zn4+$CIr0^*9a_KXfx2&*MX9;C)`_flD0v4;7q8Sn zOTCc2diDnk1889vwno)~lIk67EDQ{cEa*cC+yBCy0B$ybx@X{^#+tHbZ95{F&CkG) z#?QcDh-^8-S4LJ?yAVstLM8()o4>>{p1se`z;KHleWYT&Ag1x49;I(S_ReIW zP{^`$76yhSjI!s1B&JD-1Oe$x;;tl2t$rsntYlzVz7ajmmdnA-g$_ddfyN7SaE?NM z4bJH9V`X6IWJQmYO~>IzIVGk9gV)yKj+43m#{Bwn3=CRw=#ATov#hX&PfBVU)(Cz0 z`*)Ph#uuBxZgxJUlHxe?ZgRLZN7cL6j&&t5C3u6pyzC2vJQ(}r!etu4BVjk|a zuy~0#$1@fN2GE2K>Hz+9Rk%U$=tLxYa5)GrdvLWava_mXn)w(Q>iE!`1jn`DCPSB7 z;%*YKlqU)vV`E@A$c8>--(U$h1)4S-3$XX8KHII~zQn@7a27P9j#36yIl#?;`w=pL zjH{;9=3O`AFB1d94<__@>u^Pw1zwMeE#Brwh_XCa5W7B<&bI!D{ zybKHvH+j_ToowUb zrh!{ikj1$0;Sp?J(%anl3pV|Pn*P*M;D*8D29f^2^#k7Y7x`Y6vw)j{A)6b0nSoyh z8?2N|%*jkF#<6DX{Ev$G4N$()%6Q7#OVC z&`0EMRAZWm@GPj_!t2+$81V`iA8zXSF%mtDX^oBg@K`w1%1GbwHMRGp!}RvJni-q=^PQTW-V$5>%>G% z!w}&GE}Y;kN!)d_^hWS7RCt zF-Z@!3_RU8zYO~@cGcmFy0Fn4)Fm8;H)0xz>{@I&K5mbd#$GlCh8=9^BU~L@;ikdY zl*8vgaaUnw`@eqH2F)*F%vQDSfg6Ziy5a0b?U1nWd8Ej|a9NQ7ZJqDy{cw}O^JiE+ zrk1Fm$H>5t$%tOy{C)^6o#PAet(D(5<9xwsHUAHS00ldU#m~k2+|&$FZPC zsrQC-C@TX)04sV0y6P3&P+!p6INTw;jr+}B&}t9RqCX9k3e@-m+zhzGpzbLu(F4s5 zT#ViJS)04w?_^?N*n+W0{Gb&(tW6Ba765Lwg&PG=1fZzL)z!Krkt%(TiGkrHMjL9CGu)(*(gGYSfR7uP zm>20YFeK}v4|XhfgKNh#w(nD@mFmRGzyMlOg}Ru>vkb1^2U2H-gEB^H5z=@LwxBtB zL~+&@76yj(EDUHni_TWVO@s#xw9>=f-SAz?>l~KAz+jPpUJM;*fE(wUSBiZ-?5-A3 zjtV9QhGL8;cX|G66HBhi(tfx!-4%ZD2T6iH5oLT}|h>k6~xMb^OqSY7}eALhr z(COcBGoc}bP5&`5{v-)@1_ohv^gckC5(hL(7pE4L;TS>eRSdk2p;H&DiGWh#nZ&S&@p-A4ORw*%dF^GtZ+5l+~CT*lEez!jyoFDDxk~Dz@Wj4 z-tws1h)7XIIq|rbpU8R4INKJ)z>ph+o+R(=gS!ISSI3qk63=~TX;olgC{sXhWK0s| zge6I+erF%-v!|uJ#_5t;AZ04Fy!=`t5ydzzlV`wbKK$jG2IlL zS`tzUT90pLf;GdQO46;H%*DXa$Az9X?t62>%8^o>lPv+KZqGcrsR`fNc zPvYPP!GjBt)gT2vXgD0#kkrZ-T$^KA85klk<`0ZhFii$$U0iJ#^P@JQnyd^ADy-;h znoeh88U;^hkQKqsc_p4H*t>5c$}wMlGcz!J!$`k2`IzP+Jde-Y%Y?%MuQ4z%C^Ml? z;#U`QLh37QQvmwDTbK9qGca`VqbK`{61Y4%)p?J zQn(*F0XGUBdWg&c>Wn~VDRH;Ds{_wYgEbXUj|cd625vaG)$Lze;-BV}Uz(Rvj61pf zH8H<&iiLsUD8}N%ic4_g;BJM+bZ}~kTV`qw_8BS_Gb`l-Yzz#$+0greE!UyhIKCtq zdn4R4(u(a469dC_CiF&l!97mcXcefgz?y2$aGw16mY0Fy1uy!bUe!amU%(^2t`*>o ze)#s^ugsQfw7(j^%btR+x54b__NJ1Vh0Ihkggv1*5(l0~W@%ddg28J6L6Bl8B;6{VP4EN-R zWb5ry2B6iO82(IW=7Ke=V0q9puOt<800OoG@c)bq*}u#T3_mc|V_jf}8w7R?Yykx1 z0EJ*2tJJ2bhBLt?;!$VMd3oUG!NUzTBSHp*@YPp4yYp7hU}s>M%#OY$Dpe5EbVwxV z`BgaQ=jDNRd|}^R7ws;Trsu%GAnt&k?3^UIV6CMRyt{yIcI@A5#LB>+i&3F1j)c1l zTtc|!7L?#wUw^~3Fuz=ofuTqcz2(Li&jndk7oVC3YItGIj>|RfzPZQGz;KfveFSw& z0o)zX0SqV5?)toxVm;^lykgL3JGSKEHd*P15H|w@FUI*AmmA;)!WNMd%2T(NaX7$M zFrtpYDfPe&gQpF5@OBN*Y#Y85vB*JinJ%cx1YwkdUtt2KneY@rU?aiOZKv%FH5nL` zG|`9VCQpGU3D8c^f;0xRwhRn& zZP6QDJcr;W`r_Um?|5Qrs5LVKgBiwxxflQ8`ax+FdMXHLrVrP)2G=|xv2-Q|h9r!A zK=I7nuu20qqK$L-^75CAQ^%PZ7!F~a0P}~H8&-&c2i5V6*~LzM!2z3rM?Gxk2PZeA z2^*i74BEAbW9@ai{iGR!EDQ`hpyCuI1uf-;y8@mn!DGST-Pr`&Uak)wJXpcXz_1wO z0D=F4=q|%O#%rD0@a(rB1H(5#^wR0BINW*esd;#Mz84pJZ0=xWU}(lzn8vLIHv;ZM zM83mTAiR(ed#DW>aRc>QQ9|pyHl|s~`3=%z#cJs7!`;U{Sr`~VyZTVuzV-%q40X%Q z1C0*m<2WFs=DzjY1@CCz#VF{S#AsQyQ=ZGgsFvuTaW?%sAw?N%R zGcg5jB5W-lbm=*$DuXV`!`(qxbE-EiOn{MrN0Ojt zwRfH%69WSe6M81tHJ1n07Q{6jcmGny{>8iu4D)!=JH|ie!<_)l)j_E#rOBx{_GdX> zyt&1WnSsHQ8Qm{e*1`>f6%(K$5UXE)9_FwOWMyFR!Dy6vY=oN`JXsNJSvIJKpk zk%6I%5xt9eX&c-ia329%(pmb*!7YKGfgze7y%?_E0oM)7CIN{>#W;pC-dx$qVa3b9 zV9JX=RwaCbhoQ7I6MNrS*@mCrOOb)WS`mF6XvtN$^`JBhK7J6CncP#cH^AcmF06%R zC)CY5Kfdw68a+5t=hWM4dhbdwFx-$pALv)RdO9{EKVh43a zcD5)lMy^H_WRB_SMXBkDC8>Dk9DWw_eW+w(U?{=ZCR{BEHyhj{^U2K1#$AH#in??S zbW(#UX#WaID?vvAZVo)qq1Az3g&(ec)f#?|D|?t27}}Z8Pk&gZ0yht(zQfn&?bc{n zyMl#*VKEE(s9}LR+%%`mlJd-A?1k5qXmeK36!j4%^d#-612+JcKk*Fzw{aZZah{oh z0W`UWdUB4JKHMB|#sUp0;M+*cwRiLDw=4_{pbWY95m{Mwa6%bx?^W0 z0|Ub&@Zq1x5m#=(3&~?x){5P=v;RJYnSlXxnj@+=#4O>K!-4~}iU4PkmBwwn`v(gH z18DCqYD4ge4csKSH{j`pz!sF#@2Wi;B^elMB++}tN=|T7p>t3HiA9MyIjK3&<5IBi z^muNlD`dpNzyRuaq7Dc?iG~{pcP98e9#F-PYw}@ZL6(}jFav|WFna0^O~5n`(on@c z7g_Ao!FN-^}9T zOnf8dYd7@G1MLH4;Xuzg+Z*6U!2`}CH75uAay@Z8Xgv&=L(ad7YV<13GaAdpBX?f0vE! z%nS@p7{f3n$9bV6C^?zMC8>F-ML5TDGV<339b{r)*u#WA&;Q{R+(n?WAJ3r(T3ZrS z@3Sy4+`>5H>FHUx8Sq2~nwSqt&CM@M#oq7>sx0*U&C0;=4P$vD(`C46;I<6zc8vO6 zo!S&;28MWy?Hz_U;by>H1kICv70xB#;|#Dy-I5v2-*T837(l~ks5$Az9k`L;bytv% zJD7agaW?0VN&85pV+&{wXP|A1-6HaY!~Nqjym z6`_v2mi~i#0v;}i5(Ye}k2T7|zCPZZ%gVrz!HPaLJB^tSPm3FrLLp@qw)mU2yyxQ+ zUIvEy7)x_JIpL<_8-&df5>;8o&cLt`wBQW6t;P^14mSYSvBKtpuARyARJj-!6hIio z08@Fm0q~IWtAGt{VPD^W-C_5~!>kMp`!EhEnxTnl8aDSl7u@hqPN4={?58K!VU zkt$GZo?=UU`jMN3fdRB-5_KYRqcz+NcpxKBBoZ88zF+!G3%2qMbu^LP0d6#s^KdPz zxvVF&z?hAJL5~f6$LlQ@xH)hqB619Jj}mvC8M8k@>%IU3!z}^yd5dUoxamIm;A7FS z1)$ahTOrsK6zU#>tQfd{=)8vy_`DW;XYg@*s?po zZUzej!(@z=S`)M2uD}vTpfUhYlf~$&-Pe6A3=BIl*3lN^!;M8=8ig%{qT{FR2W=EL zV?m!HNG^dJ0Zb@JZi$iuX+qFO&Tv*ZjdWRmu zb^C*lu!N2^fhpWg07<{DstYU(45vZ;J(NOH_c@Y*(CaJUO#s;C3E(!MGxkmX?>F(g z=d&>|WU-;go5vft(U29at~eTk=5`0?zF=oyc+8Hz^yt+mepo9qzbv(=C^H3odO5Z* zJ2A^Qsz#WBp&a9^687J4cYs11XTN>w*9~H@;Rn>A@U46TusDM!AMAmIHC?vk)m9j= zGB9YfqOYHSEsSX}!4h-L*>4_y`4|{}@S)G^Mo9|5f(!5Iz9J{y7j-i+FtjnDAM((t z0e29%%?4?{;TwlqrFJv81iD-swG|(r4L1wg+yW<1e2Ztej`4KyvNJGnu%nN2G8(`Q zgH?dA8GEc@?(DeX=`%hC2GIUD)EQL|b9f*XrIv$E1H)d&9!O@EoHn;TjG%0cF4t)+WG~L5gZF z=Y3&eV0h1hew<5w4BRw$@(V{@k31&wPR_ z*Y0*${awJzz%U!*o{Vd$0EHD<97`DJog2%ZZ=yXw7`xw;K#&LcJgNqET8B~6D=;^ae3=AhQHUhnx1UCjH zci=4fnZL*LC$cjz#9-`r`7{-76l85awgkp=WttFZuorYtBkE4SrnzwK@KA;)Fvy|U zrN!7!D(^J8{x_6~fgu26N#m|Xa6@t4m64MfS7y$`z+l9KUPst2h36UA+7_(A8?q$e z?N<&4h7TO*b;PobaQoqV-vbhhN-}XCavQtP`{4&m28M^0=yQtKcfrkq))66<1*xDr zp|CoNforEZY;X*9ke=Zy+#saR0`9}V7BTYw1I5=%jOOOYU-0@NDL)@{JQ|L^edLW# zj-aOI4vcXXAqGK=_5#xF6}Y<@X?bb-u=BuBFG>+&fg1_S8lc7u)=&zKxSIf5SBZKf zPZT@c6nJ_>6iuK~2~RVhug%mNG!5&HaX!mMZn&`_MU{kRVqfnzcy^76f#D)X@fIow zHwWfP(AWXa)8%sfgmTnb85oo>7AK{Oz)eCa0qOqnVTBxXfZ7$VR)?Ddk2l=wY8`^VUirYn z!0?&{z5bBbg`0yCEVzz(^x1c(XATns!*nL}Nk&g2L0HwBn_7~Qk7K___9LxxuvsMaX*`lri-5!Tz3TAf-d`J-Xk*C*N8~(0RVPKf8f<7wm zlma)?r7|xuH#0dH|K)4{pLc!&4L@JS7~og$gc}7VG_lWmzmZEVo#N~n{K2AkjxK_0(=4nyF)Yi@~%-D}t>I;ef3i0xPn z1eJ2QmZ>&qX@9C>W?(4A*!?nT7u+y-Y(iTXE~&}j12l15&)~3o)q-MX28KM0g-ZeZ zaGDvAUx0I6Z+N}WpAG>AhGqfuCLzlqLCAeRg1ZN$ z#KPI~VGJ$I0Bsb?$C!bfdlhaJmf*s@h|g`swomh!7#LNm=z6m!G#c{B17^u4h zT_}e&R`AKz$z4zI;HTgiJN+$?yigDyzHb)@ov;IDDPtPBi(7{_^R zSPD1HDUpbKM|!^Ac^}Baz~Fiir6Ue24=q##od<-m;c534xCto1j(_Lj)aRb#AN?hop z1)=&PutGKjbR8{$c5u}`ArIIA->B`nt(FLbK!;$X+z$vEn8UslpzJ`zy4y?)44~dC z>S~3X_NWG;Z4||A^?g0Sgg)HRRao!~~o5)tmQQ{i~wD?eri22YFv;k7H=7|bl= zT7lzc?DC|kH$j7j#u$ZOr6;C&h#>^r8L<0J_Ss2n3=F*(hXa20#WWF~sX54Eq2fVJ?07t<^Y9z!mrjsk zVCa@Y?<_gyAq>IOgZ7NE7HeM3zz}%^y_8| #{inspect(key)} + end\n + """, + [:append, :binary] + ) + end + + File.write( + "lib/openflow/enums.ex", + """ + def to_int(_int, :#{enum_name}) do + throw(:bad_enum) + end\n + """, + [:append, :binary] + ) + end + + for {enum_name, enum_def} <- @enums do + to_atom_fn_name = :"#{enum_name}_to_atom" + + for {_key, value} <- enum_def do + File.write( + "lib/openflow/enums.ex", + """ + def to_atom(#{inspect(value, base: :hex)}, :#{enum_name}) do + #{to_atom_fn_name}(#{inspect(value, base: :hex)}) + catch + _class, _reason -> #{value} + end\n + """, + [:append, :binary] + ) + end + + File.write( + "lib/openflow/enums.ex", + """ + def to_atom(_, :#{enum_name}) do + throw(:bad_enum) + end\n + """, + [:append, :binary] + ) + end + + for {enum_name, enum_def} <- @enums do + to_int_fn_name = :"#{enum_name}_to_int" + to_atom_fn_name = :"#{enum_name}_to_atom" + + for {key, value} <- enum_def do + File.write( + "lib/openflow/enums.ex", + " def #{to_int_fn_name}(#{inspect(key)}), do: #{inspect(value, base: :hex)}\n", + [ + :append, + :binary + ] + ) + end + + File.write("lib/openflow/enums.ex", " def #{to_int_fn_name}(_), do: throw(:bad_enum)\n", [ + :append, + :binary + ]) + + for {key, value} <- enum_def do + File.write( + "lib/openflow/enums.ex", + " def #{to_atom_fn_name}(#{inspect(value, base: :hex)}), do: #{inspect(key)}\n", + [ + :append, + :binary + ] + ) + end + + File.write("lib/openflow/enums.ex", " def #{to_atom_fn_name}(_), do: throw(:bad_enum)\n", [ + :append, + :binary + ]) + end + + for {enum_name, _enum_def} <- @enums do + File.write( + "lib/openflow/enums.ex", + """ + def int_to_flags(int, :#{enum_name}) do + Openflow.Utils.int_to_flags([], int, enum_of(:#{enum_name})) + end\n + """, + [:append, :binary] + ) + end + + for {enum_name, _enum_def} <- @enums do + File.write( + "lib/openflow/enums.ex", + """ + def flags_to_int(flags, :#{enum_name}) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:#{enum_name})) + end\n + """, + [:append, :binary] + ) + end + + for {enum_name, enum_def} <- @enums do + File.write( + "lib/openflow/enums.ex", + " defp enum_of(:#{enum_name}), do: #{inspect(enum_def, pretty: true, limit: 100_000)}\n", + [:append, :binary] + ) + end + + File.write("lib/openflow/enums.ex", "end", [:append, :binary]) + end +end diff --git a/lib/openflow/enums.ex b/lib/openflow/enums.ex index 5ae9212..430fffa 100644 --- a/lib/openflow/enums.ex +++ b/lib/openflow/enums.ex @@ -1,8 +1,12535 @@ defmodule Openflow.Enums do - import Bitwise + @moduledoc "auto generated code" - @enums [ - openflow_codec: [ + def to_int(Openflow.Hello, :openflow_codec) do + openflow_codec_to_int(Openflow.Hello) + catch + _class, _reason -> Openflow.Hello + end + + def to_int(Openflow.ErrorMsg, :openflow_codec) do + openflow_codec_to_int(Openflow.ErrorMsg) + catch + _class, _reason -> Openflow.ErrorMsg + end + + def to_int(Openflow.Echo.Request, :openflow_codec) do + openflow_codec_to_int(Openflow.Echo.Request) + catch + _class, _reason -> Openflow.Echo.Request + end + + def to_int(Openflow.Echo.Reply, :openflow_codec) do + openflow_codec_to_int(Openflow.Echo.Reply) + catch + _class, _reason -> Openflow.Echo.Reply + end + + def to_int(Openflow.Experimenter, :openflow_codec) do + openflow_codec_to_int(Openflow.Experimenter) + catch + _class, _reason -> Openflow.Experimenter + end + + def to_int(Openflow.Features.Request, :openflow_codec) do + openflow_codec_to_int(Openflow.Features.Request) + catch + _class, _reason -> Openflow.Features.Request + end + + def to_int(Openflow.Features.Reply, :openflow_codec) do + openflow_codec_to_int(Openflow.Features.Reply) + catch + _class, _reason -> Openflow.Features.Reply + end + + def to_int(Openflow.GetConfig.Request, :openflow_codec) do + openflow_codec_to_int(Openflow.GetConfig.Request) + catch + _class, _reason -> Openflow.GetConfig.Request + end + + def to_int(Openflow.GetConfig.Reply, :openflow_codec) do + openflow_codec_to_int(Openflow.GetConfig.Reply) + catch + _class, _reason -> Openflow.GetConfig.Reply + end + + def to_int(Openflow.SetConfig, :openflow_codec) do + openflow_codec_to_int(Openflow.SetConfig) + catch + _class, _reason -> Openflow.SetConfig + end + + def to_int(Openflow.PacketIn, :openflow_codec) do + openflow_codec_to_int(Openflow.PacketIn) + catch + _class, _reason -> Openflow.PacketIn + end + + def to_int(Openflow.FlowRemoved, :openflow_codec) do + openflow_codec_to_int(Openflow.FlowRemoved) + catch + _class, _reason -> Openflow.FlowRemoved + end + + def to_int(Openflow.PortStatus, :openflow_codec) do + openflow_codec_to_int(Openflow.PortStatus) + catch + _class, _reason -> Openflow.PortStatus + end + + def to_int(Openflow.PacketOut, :openflow_codec) do + openflow_codec_to_int(Openflow.PacketOut) + catch + _class, _reason -> Openflow.PacketOut + end + + def to_int(Openflow.FlowMod, :openflow_codec) do + openflow_codec_to_int(Openflow.FlowMod) + catch + _class, _reason -> Openflow.FlowMod + end + + def to_int(Openflow.GroupMod, :openflow_codec) do + openflow_codec_to_int(Openflow.GroupMod) + catch + _class, _reason -> Openflow.GroupMod + end + + def to_int(Openflow.PortMod, :openflow_codec) do + openflow_codec_to_int(Openflow.PortMod) + catch + _class, _reason -> Openflow.PortMod + end + + def to_int(Openflow.TableMod, :openflow_codec) do + openflow_codec_to_int(Openflow.TableMod) + catch + _class, _reason -> Openflow.TableMod + end + + def to_int(Openflow.Multipart.Request, :openflow_codec) do + openflow_codec_to_int(Openflow.Multipart.Request) + catch + _class, _reason -> Openflow.Multipart.Request + end + + def to_int(Openflow.Multipart.Reply, :openflow_codec) do + openflow_codec_to_int(Openflow.Multipart.Reply) + catch + _class, _reason -> Openflow.Multipart.Reply + end + + def to_int(Openflow.Barrier.Request, :openflow_codec) do + openflow_codec_to_int(Openflow.Barrier.Request) + catch + _class, _reason -> Openflow.Barrier.Request + end + + def to_int(Openflow.Barrier.Reply, :openflow_codec) do + openflow_codec_to_int(Openflow.Barrier.Reply) + catch + _class, _reason -> Openflow.Barrier.Reply + end + + def to_int(Openflow.Role.Request, :openflow_codec) do + openflow_codec_to_int(Openflow.Role.Request) + catch + _class, _reason -> Openflow.Role.Request + end + + def to_int(Openflow.Role.Reply, :openflow_codec) do + openflow_codec_to_int(Openflow.Role.Reply) + catch + _class, _reason -> Openflow.Role.Reply + end + + def to_int(Openflow.GetAsync.Request, :openflow_codec) do + openflow_codec_to_int(Openflow.GetAsync.Request) + catch + _class, _reason -> Openflow.GetAsync.Request + end + + def to_int(Openflow.GetAsync.Reply, :openflow_codec) do + openflow_codec_to_int(Openflow.GetAsync.Reply) + catch + _class, _reason -> Openflow.GetAsync.Reply + end + + def to_int(Openflow.SetAsync, :openflow_codec) do + openflow_codec_to_int(Openflow.SetAsync) + catch + _class, _reason -> Openflow.SetAsync + end + + def to_int(Openflow.MeterMod, :openflow_codec) do + openflow_codec_to_int(Openflow.MeterMod) + catch + _class, _reason -> Openflow.MeterMod + end + + def to_int(_int, :openflow_codec) do + throw(:bad_enum) + end + + def to_int(:nicira_ext_message, :experimenter_id) do + experimenter_id_to_int(:nicira_ext_message) + catch + _class, _reason -> :nicira_ext_message + end + + def to_int(:onf_ext_message, :experimenter_id) do + experimenter_id_to_int(:onf_ext_message) + catch + _class, _reason -> :onf_ext_message + end + + def to_int(_int, :experimenter_id) do + throw(:bad_enum) + end + + def to_int(Openflow.NxSetPacketInFormat, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxSetPacketInFormat) + catch + _class, _reason -> Openflow.NxSetPacketInFormat + end + + def to_int(Openflow.NxSetControllerId, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxSetControllerId) + catch + _class, _reason -> Openflow.NxSetControllerId + end + + def to_int(Openflow.NxFlowMonitor.Cancel, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxFlowMonitor.Cancel) + catch + _class, _reason -> Openflow.NxFlowMonitor.Cancel + end + + def to_int(Openflow.NxFlowMonitor.Paused, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxFlowMonitor.Paused) + catch + _class, _reason -> Openflow.NxFlowMonitor.Paused + end + + def to_int(Openflow.NxFlowMonitor.Resumed, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxFlowMonitor.Resumed) + catch + _class, _reason -> Openflow.NxFlowMonitor.Resumed + end + + def to_int(Openflow.NxTLVTableMod, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxTLVTableMod) + catch + _class, _reason -> Openflow.NxTLVTableMod + end + + def to_int(Openflow.NxTLVTable.Request, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxTLVTable.Request) + catch + _class, _reason -> Openflow.NxTLVTable.Request + end + + def to_int(Openflow.NxTLVTable.Reply, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxTLVTable.Reply) + catch + _class, _reason -> Openflow.NxTLVTable.Reply + end + + def to_int(Openflow.NxSetAsyncConfig2, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxSetAsyncConfig2) + catch + _class, _reason -> Openflow.NxSetAsyncConfig2 + end + + def to_int(Openflow.NxResume, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxResume) + catch + _class, _reason -> Openflow.NxResume + end + + def to_int(Openflow.NxCtFlushZone, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxCtFlushZone) + catch + _class, _reason -> Openflow.NxCtFlushZone + end + + def to_int(Openflow.NxPacketIn2, :nicira_ext_message) do + nicira_ext_message_to_int(Openflow.NxPacketIn2) + catch + _class, _reason -> Openflow.NxPacketIn2 + end + + def to_int(_int, :nicira_ext_message) do + throw(:bad_enum) + end + + def to_int(Openflow.OnfBundleControl, :onf_ext_message) do + onf_ext_message_to_int(Openflow.OnfBundleControl) + catch + _class, _reason -> Openflow.OnfBundleControl + end + + def to_int(Openflow.OnfBundleAddMessage, :onf_ext_message) do + onf_ext_message_to_int(Openflow.OnfBundleAddMessage) + catch + _class, _reason -> Openflow.OnfBundleAddMessage + end + + def to_int(_int, :onf_ext_message) do + throw(:bad_enum) + end + + def to_int(:more, :multipart_request_flags) do + multipart_request_flags_to_int(:more) + catch + _class, _reason -> :more + end + + def to_int(_int, :multipart_request_flags) do + throw(:bad_enum) + end + + def to_int(:more, :multipart_reply_flags) do + multipart_reply_flags_to_int(:more) + catch + _class, _reason -> :more + end + + def to_int(_int, :multipart_reply_flags) do + throw(:bad_enum) + end + + def to_int(Openflow.Multipart.Desc.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Desc.Request) + catch + _class, _reason -> Openflow.Multipart.Desc.Request + end + + def to_int(Openflow.Multipart.Flow.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Flow.Request) + catch + _class, _reason -> Openflow.Multipart.Flow.Request + end + + def to_int(Openflow.Multipart.Aggregate.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Aggregate.Request) + catch + _class, _reason -> Openflow.Multipart.Aggregate.Request + end + + def to_int(Openflow.Multipart.Table.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Table.Request) + catch + _class, _reason -> Openflow.Multipart.Table.Request + end + + def to_int(Openflow.Multipart.PortStats.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.PortStats.Request) + catch + _class, _reason -> Openflow.Multipart.PortStats.Request + end + + def to_int(Openflow.Multipart.Queue.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Queue.Request) + catch + _class, _reason -> Openflow.Multipart.Queue.Request + end + + def to_int(Openflow.Multipart.Group.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Group.Request) + catch + _class, _reason -> Openflow.Multipart.Group.Request + end + + def to_int(Openflow.Multipart.GroupDesc.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.GroupDesc.Request) + catch + _class, _reason -> Openflow.Multipart.GroupDesc.Request + end + + def to_int(Openflow.Multipart.GroupFeatures.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.GroupFeatures.Request) + catch + _class, _reason -> Openflow.Multipart.GroupFeatures.Request + end + + def to_int(Openflow.Multipart.Meter.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Meter.Request) + catch + _class, _reason -> Openflow.Multipart.Meter.Request + end + + def to_int(Openflow.Multipart.MeterConfig.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.MeterConfig.Request) + catch + _class, _reason -> Openflow.Multipart.MeterConfig.Request + end + + def to_int(Openflow.Multipart.MeterFeatures.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.MeterFeatures.Request) + catch + _class, _reason -> Openflow.Multipart.MeterFeatures.Request + end + + def to_int(Openflow.Multipart.TableFeatures.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.TableFeatures.Request) + catch + _class, _reason -> Openflow.Multipart.TableFeatures.Request + end + + def to_int(Openflow.Multipart.PortDesc.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.PortDesc.Request) + catch + _class, _reason -> Openflow.Multipart.PortDesc.Request + end + + def to_int(Openflow.Multipart.Experimenter.Request, :multipart_request_codec) do + multipart_request_codec_to_int(Openflow.Multipart.Experimenter.Request) + catch + _class, _reason -> Openflow.Multipart.Experimenter.Request + end + + def to_int(_int, :multipart_request_codec) do + throw(:bad_enum) + end + + def to_int(Openflow.Multipart.Desc.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Desc.Reply) + catch + _class, _reason -> Openflow.Multipart.Desc.Reply + end + + def to_int(Openflow.Multipart.Flow.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Flow.Reply) + catch + _class, _reason -> Openflow.Multipart.Flow.Reply + end + + def to_int(Openflow.Multipart.Aggregate.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Aggregate.Reply) + catch + _class, _reason -> Openflow.Multipart.Aggregate.Reply + end + + def to_int(Openflow.Multipart.Table.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Table.Reply) + catch + _class, _reason -> Openflow.Multipart.Table.Reply + end + + def to_int(Openflow.Multipart.PortStats.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.PortStats.Reply) + catch + _class, _reason -> Openflow.Multipart.PortStats.Reply + end + + def to_int(Openflow.Multipart.Queue.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Queue.Reply) + catch + _class, _reason -> Openflow.Multipart.Queue.Reply + end + + def to_int(Openflow.Multipart.Group.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Group.Reply) + catch + _class, _reason -> Openflow.Multipart.Group.Reply + end + + def to_int(Openflow.Multipart.GroupDesc.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.GroupDesc.Reply) + catch + _class, _reason -> Openflow.Multipart.GroupDesc.Reply + end + + def to_int(Openflow.Multipart.GroupFeatures.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.GroupFeatures.Reply) + catch + _class, _reason -> Openflow.Multipart.GroupFeatures.Reply + end + + def to_int(Openflow.Multipart.Meter.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Meter.Reply) + catch + _class, _reason -> Openflow.Multipart.Meter.Reply + end + + def to_int(Openflow.Multipart.MeterConfig.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.MeterConfig.Reply) + catch + _class, _reason -> Openflow.Multipart.MeterConfig.Reply + end + + def to_int(Openflow.Multipart.MeterFeatures.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.MeterFeatures.Reply) + catch + _class, _reason -> Openflow.Multipart.MeterFeatures.Reply + end + + def to_int(Openflow.Multipart.TableFeatures.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.TableFeatures.Reply) + catch + _class, _reason -> Openflow.Multipart.TableFeatures.Reply + end + + def to_int(Openflow.Multipart.PortDesc.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.PortDesc.Reply) + catch + _class, _reason -> Openflow.Multipart.PortDesc.Reply + end + + def to_int(Openflow.Multipart.Experimenter.Reply, :multipart_reply_codec) do + multipart_reply_codec_to_int(Openflow.Multipart.Experimenter.Reply) + catch + _class, _reason -> Openflow.Multipart.Experimenter.Reply + end + + def to_int(_int, :multipart_reply_codec) do + throw(:bad_enum) + end + + def to_int(Openflow.Multipart.NxFlow, :nicira_ext_stats) do + nicira_ext_stats_to_int(Openflow.Multipart.NxFlow) + catch + _class, _reason -> Openflow.Multipart.NxFlow + end + + def to_int(Openflow.Multipart.NxAggregate, :nicira_ext_stats) do + nicira_ext_stats_to_int(Openflow.Multipart.NxAggregate) + catch + _class, _reason -> Openflow.Multipart.NxAggregate + end + + def to_int(Openflow.Multipart.NxFlowMonitor, :nicira_ext_stats) do + nicira_ext_stats_to_int(Openflow.Multipart.NxFlowMonitor) + catch + _class, _reason -> Openflow.Multipart.NxFlowMonitor + end + + def to_int(Openflow.Multipart.NxIPFIXBridge, :nicira_ext_stats) do + nicira_ext_stats_to_int(Openflow.Multipart.NxIPFIXBridge) + catch + _class, _reason -> Openflow.Multipart.NxIPFIXBridge + end + + def to_int(Openflow.Multipart.NxIPFIXFlow, :nicira_ext_stats) do + nicira_ext_stats_to_int(Openflow.Multipart.NxIPFIXFlow) + catch + _class, _reason -> Openflow.Multipart.NxIPFIXFlow + end + + def to_int(_int, :nicira_ext_stats) do + throw(:bad_enum) + end + + def to_int(:versionbitmap, :hello_elem) do + hello_elem_to_int(:versionbitmap) + catch + _class, _reason -> :versionbitmap + end + + def to_int(_int, :hello_elem) do + throw(:bad_enum) + end + + def to_int(:hello_failed, :error_type) do + error_type_to_int(:hello_failed) + catch + _class, _reason -> :hello_failed + end + + def to_int(:bad_request, :error_type) do + error_type_to_int(:bad_request) + catch + _class, _reason -> :bad_request + end + + def to_int(:bad_action, :error_type) do + error_type_to_int(:bad_action) + catch + _class, _reason -> :bad_action + end + + def to_int(:bad_instruction, :error_type) do + error_type_to_int(:bad_instruction) + catch + _class, _reason -> :bad_instruction + end + + def to_int(:bad_match, :error_type) do + error_type_to_int(:bad_match) + catch + _class, _reason -> :bad_match + end + + def to_int(:flow_mod_failed, :error_type) do + error_type_to_int(:flow_mod_failed) + catch + _class, _reason -> :flow_mod_failed + end + + def to_int(:group_mod_failed, :error_type) do + error_type_to_int(:group_mod_failed) + catch + _class, _reason -> :group_mod_failed + end + + def to_int(:port_mod_failed, :error_type) do + error_type_to_int(:port_mod_failed) + catch + _class, _reason -> :port_mod_failed + end + + def to_int(:table_mod_failed, :error_type) do + error_type_to_int(:table_mod_failed) + catch + _class, _reason -> :table_mod_failed + end + + def to_int(:queue_op_failed, :error_type) do + error_type_to_int(:queue_op_failed) + catch + _class, _reason -> :queue_op_failed + end + + def to_int(:switch_config_failed, :error_type) do + error_type_to_int(:switch_config_failed) + catch + _class, _reason -> :switch_config_failed + end + + def to_int(:role_request_failed, :error_type) do + error_type_to_int(:role_request_failed) + catch + _class, _reason -> :role_request_failed + end + + def to_int(:meter_mod_failed, :error_type) do + error_type_to_int(:meter_mod_failed) + catch + _class, _reason -> :meter_mod_failed + end + + def to_int(:table_features_failed, :error_type) do + error_type_to_int(:table_features_failed) + catch + _class, _reason -> :table_features_failed + end + + def to_int(:experimenter, :error_type) do + error_type_to_int(:experimenter) + catch + _class, _reason -> :experimenter + end + + def to_int(_int, :error_type) do + throw(:bad_enum) + end + + def to_int(:inconpatible, :hello_failed) do + hello_failed_to_int(:inconpatible) + catch + _class, _reason -> :inconpatible + end + + def to_int(:eperm, :hello_failed) do + hello_failed_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :hello_failed) do + throw(:bad_enum) + end + + def to_int(:bad_version, :bad_request) do + bad_request_to_int(:bad_version) + catch + _class, _reason -> :bad_version + end + + def to_int(:bad_type, :bad_request) do + bad_request_to_int(:bad_type) + catch + _class, _reason -> :bad_type + end + + def to_int(:bad_multipart, :bad_request) do + bad_request_to_int(:bad_multipart) + catch + _class, _reason -> :bad_multipart + end + + def to_int(:bad_experimeter, :bad_request) do + bad_request_to_int(:bad_experimeter) + catch + _class, _reason -> :bad_experimeter + end + + def to_int(:bad_exp_type, :bad_request) do + bad_request_to_int(:bad_exp_type) + catch + _class, _reason -> :bad_exp_type + end + + def to_int(:eperm, :bad_request) do + bad_request_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(:bad_len, :bad_request) do + bad_request_to_int(:bad_len) + catch + _class, _reason -> :bad_len + end + + def to_int(:buffer_empty, :bad_request) do + bad_request_to_int(:buffer_empty) + catch + _class, _reason -> :buffer_empty + end + + def to_int(:buffer_unknown, :bad_request) do + bad_request_to_int(:buffer_unknown) + catch + _class, _reason -> :buffer_unknown + end + + def to_int(:bad_table_id, :bad_request) do + bad_request_to_int(:bad_table_id) + catch + _class, _reason -> :bad_table_id + end + + def to_int(:is_slave, :bad_request) do + bad_request_to_int(:is_slave) + catch + _class, _reason -> :is_slave + end + + def to_int(:bad_port, :bad_request) do + bad_request_to_int(:bad_port) + catch + _class, _reason -> :bad_port + end + + def to_int(:bad_packet, :bad_request) do + bad_request_to_int(:bad_packet) + catch + _class, _reason -> :bad_packet + end + + def to_int(:multipart_buffer_overflow, :bad_request) do + bad_request_to_int(:multipart_buffer_overflow) + catch + _class, _reason -> :multipart_buffer_overflow + end + + def to_int(_int, :bad_request) do + throw(:bad_enum) + end + + def to_int(:bad_type, :bad_action) do + bad_action_to_int(:bad_type) + catch + _class, _reason -> :bad_type + end + + def to_int(:bad_len, :bad_action) do + bad_action_to_int(:bad_len) + catch + _class, _reason -> :bad_len + end + + def to_int(:bad_experimeter, :bad_action) do + bad_action_to_int(:bad_experimeter) + catch + _class, _reason -> :bad_experimeter + end + + def to_int(:bad_exp_type, :bad_action) do + bad_action_to_int(:bad_exp_type) + catch + _class, _reason -> :bad_exp_type + end + + def to_int(:bad_out_port, :bad_action) do + bad_action_to_int(:bad_out_port) + catch + _class, _reason -> :bad_out_port + end + + def to_int(:bad_argument, :bad_action) do + bad_action_to_int(:bad_argument) + catch + _class, _reason -> :bad_argument + end + + def to_int(:eperm, :bad_action) do + bad_action_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(:too_many, :bad_action) do + bad_action_to_int(:too_many) + catch + _class, _reason -> :too_many + end + + def to_int(:bad_queue, :bad_action) do + bad_action_to_int(:bad_queue) + catch + _class, _reason -> :bad_queue + end + + def to_int(:bad_out_group, :bad_action) do + bad_action_to_int(:bad_out_group) + catch + _class, _reason -> :bad_out_group + end + + def to_int(:match_inconsistent, :bad_action) do + bad_action_to_int(:match_inconsistent) + catch + _class, _reason -> :match_inconsistent + end + + def to_int(:unsupported_order, :bad_action) do + bad_action_to_int(:unsupported_order) + catch + _class, _reason -> :unsupported_order + end + + def to_int(:bad_tag, :bad_action) do + bad_action_to_int(:bad_tag) + catch + _class, _reason -> :bad_tag + end + + def to_int(:bad_set_type, :bad_action) do + bad_action_to_int(:bad_set_type) + catch + _class, _reason -> :bad_set_type + end + + def to_int(:bad_set_len, :bad_action) do + bad_action_to_int(:bad_set_len) + catch + _class, _reason -> :bad_set_len + end + + def to_int(:bad_set_argument, :bad_action) do + bad_action_to_int(:bad_set_argument) + catch + _class, _reason -> :bad_set_argument + end + + def to_int(_int, :bad_action) do + throw(:bad_enum) + end + + def to_int(:unknown_instruction, :bad_instruction) do + bad_instruction_to_int(:unknown_instruction) + catch + _class, _reason -> :unknown_instruction + end + + def to_int(:unsupported_instruction, :bad_instruction) do + bad_instruction_to_int(:unsupported_instruction) + catch + _class, _reason -> :unsupported_instruction + end + + def to_int(:bad_table_id, :bad_instruction) do + bad_instruction_to_int(:bad_table_id) + catch + _class, _reason -> :bad_table_id + end + + def to_int(:unsupported_metadata, :bad_instruction) do + bad_instruction_to_int(:unsupported_metadata) + catch + _class, _reason -> :unsupported_metadata + end + + def to_int(:unsupported_metadata_mask, :bad_instruction) do + bad_instruction_to_int(:unsupported_metadata_mask) + catch + _class, _reason -> :unsupported_metadata_mask + end + + def to_int(:bad_experimeter, :bad_instruction) do + bad_instruction_to_int(:bad_experimeter) + catch + _class, _reason -> :bad_experimeter + end + + def to_int(:bad_exp_type, :bad_instruction) do + bad_instruction_to_int(:bad_exp_type) + catch + _class, _reason -> :bad_exp_type + end + + def to_int(:bad_len, :bad_instruction) do + bad_instruction_to_int(:bad_len) + catch + _class, _reason -> :bad_len + end + + def to_int(:eperm, :bad_instruction) do + bad_instruction_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :bad_instruction) do + throw(:bad_enum) + end + + def to_int(:bad_type, :bad_match) do + bad_match_to_int(:bad_type) + catch + _class, _reason -> :bad_type + end + + def to_int(:bad_len, :bad_match) do + bad_match_to_int(:bad_len) + catch + _class, _reason -> :bad_len + end + + def to_int(:bad_tag, :bad_match) do + bad_match_to_int(:bad_tag) + catch + _class, _reason -> :bad_tag + end + + def to_int(:bad_dl_addr_mask, :bad_match) do + bad_match_to_int(:bad_dl_addr_mask) + catch + _class, _reason -> :bad_dl_addr_mask + end + + def to_int(:bad_nw_addr_mask, :bad_match) do + bad_match_to_int(:bad_nw_addr_mask) + catch + _class, _reason -> :bad_nw_addr_mask + end + + def to_int(:bad_wildcards, :bad_match) do + bad_match_to_int(:bad_wildcards) + catch + _class, _reason -> :bad_wildcards + end + + def to_int(:bad_field, :bad_match) do + bad_match_to_int(:bad_field) + catch + _class, _reason -> :bad_field + end + + def to_int(:bad_value, :bad_match) do + bad_match_to_int(:bad_value) + catch + _class, _reason -> :bad_value + end + + def to_int(:bad_mask, :bad_match) do + bad_match_to_int(:bad_mask) + catch + _class, _reason -> :bad_mask + end + + def to_int(:bad_prereq, :bad_match) do + bad_match_to_int(:bad_prereq) + catch + _class, _reason -> :bad_prereq + end + + def to_int(:dup_field, :bad_match) do + bad_match_to_int(:dup_field) + catch + _class, _reason -> :dup_field + end + + def to_int(:eperm, :bad_match) do + bad_match_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :bad_match) do + throw(:bad_enum) + end + + def to_int(:unknown, :flow_mod_failed) do + flow_mod_failed_to_int(:unknown) + catch + _class, _reason -> :unknown + end + + def to_int(:table_full, :flow_mod_failed) do + flow_mod_failed_to_int(:table_full) + catch + _class, _reason -> :table_full + end + + def to_int(:bad_table_id, :flow_mod_failed) do + flow_mod_failed_to_int(:bad_table_id) + catch + _class, _reason -> :bad_table_id + end + + def to_int(:overlap, :flow_mod_failed) do + flow_mod_failed_to_int(:overlap) + catch + _class, _reason -> :overlap + end + + def to_int(:eperm, :flow_mod_failed) do + flow_mod_failed_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(:bad_timeout, :flow_mod_failed) do + flow_mod_failed_to_int(:bad_timeout) + catch + _class, _reason -> :bad_timeout + end + + def to_int(:bad_command, :flow_mod_failed) do + flow_mod_failed_to_int(:bad_command) + catch + _class, _reason -> :bad_command + end + + def to_int(:bad_flags, :flow_mod_failed) do + flow_mod_failed_to_int(:bad_flags) + catch + _class, _reason -> :bad_flags + end + + def to_int(_int, :flow_mod_failed) do + throw(:bad_enum) + end + + def to_int(:group_exists, :group_mod_failed) do + group_mod_failed_to_int(:group_exists) + catch + _class, _reason -> :group_exists + end + + def to_int(:invalid_group, :group_mod_failed) do + group_mod_failed_to_int(:invalid_group) + catch + _class, _reason -> :invalid_group + end + + def to_int(:weight_unsupported, :group_mod_failed) do + group_mod_failed_to_int(:weight_unsupported) + catch + _class, _reason -> :weight_unsupported + end + + def to_int(:out_of_groups, :group_mod_failed) do + group_mod_failed_to_int(:out_of_groups) + catch + _class, _reason -> :out_of_groups + end + + def to_int(:ouf_of_buckets, :group_mod_failed) do + group_mod_failed_to_int(:ouf_of_buckets) + catch + _class, _reason -> :ouf_of_buckets + end + + def to_int(:chaining_unsupported, :group_mod_failed) do + group_mod_failed_to_int(:chaining_unsupported) + catch + _class, _reason -> :chaining_unsupported + end + + def to_int(:watch_unsupported, :group_mod_failed) do + group_mod_failed_to_int(:watch_unsupported) + catch + _class, _reason -> :watch_unsupported + end + + def to_int(:loop, :group_mod_failed) do + group_mod_failed_to_int(:loop) + catch + _class, _reason -> :loop + end + + def to_int(:unknown_group, :group_mod_failed) do + group_mod_failed_to_int(:unknown_group) + catch + _class, _reason -> :unknown_group + end + + def to_int(:chained_group, :group_mod_failed) do + group_mod_failed_to_int(:chained_group) + catch + _class, _reason -> :chained_group + end + + def to_int(:bad_type, :group_mod_failed) do + group_mod_failed_to_int(:bad_type) + catch + _class, _reason -> :bad_type + end + + def to_int(:bad_command, :group_mod_failed) do + group_mod_failed_to_int(:bad_command) + catch + _class, _reason -> :bad_command + end + + def to_int(:bad_bucket, :group_mod_failed) do + group_mod_failed_to_int(:bad_bucket) + catch + _class, _reason -> :bad_bucket + end + + def to_int(:bad_watch, :group_mod_failed) do + group_mod_failed_to_int(:bad_watch) + catch + _class, _reason -> :bad_watch + end + + def to_int(:eperm, :group_mod_failed) do + group_mod_failed_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :group_mod_failed) do + throw(:bad_enum) + end + + def to_int(:bad_port, :port_mod_failed) do + port_mod_failed_to_int(:bad_port) + catch + _class, _reason -> :bad_port + end + + def to_int(:bad_hw_addr, :port_mod_failed) do + port_mod_failed_to_int(:bad_hw_addr) + catch + _class, _reason -> :bad_hw_addr + end + + def to_int(:bad_config, :port_mod_failed) do + port_mod_failed_to_int(:bad_config) + catch + _class, _reason -> :bad_config + end + + def to_int(:bad_advertise, :port_mod_failed) do + port_mod_failed_to_int(:bad_advertise) + catch + _class, _reason -> :bad_advertise + end + + def to_int(:eperm, :port_mod_failed) do + port_mod_failed_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :port_mod_failed) do + throw(:bad_enum) + end + + def to_int(:bad_table, :table_mod_failed) do + table_mod_failed_to_int(:bad_table) + catch + _class, _reason -> :bad_table + end + + def to_int(:bad_config, :table_mod_failed) do + table_mod_failed_to_int(:bad_config) + catch + _class, _reason -> :bad_config + end + + def to_int(:eperm, :table_mod_failed) do + table_mod_failed_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :table_mod_failed) do + throw(:bad_enum) + end + + def to_int(:bad_port, :queue_op_failed) do + queue_op_failed_to_int(:bad_port) + catch + _class, _reason -> :bad_port + end + + def to_int(:bad_queue, :queue_op_failed) do + queue_op_failed_to_int(:bad_queue) + catch + _class, _reason -> :bad_queue + end + + def to_int(:eperm, :queue_op_failed) do + queue_op_failed_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :queue_op_failed) do + throw(:bad_enum) + end + + def to_int(:bad_flags, :switch_config_failed) do + switch_config_failed_to_int(:bad_flags) + catch + _class, _reason -> :bad_flags + end + + def to_int(:bad_len, :switch_config_failed) do + switch_config_failed_to_int(:bad_len) + catch + _class, _reason -> :bad_len + end + + def to_int(:eperm, :switch_config_failed) do + switch_config_failed_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :switch_config_failed) do + throw(:bad_enum) + end + + def to_int(:stale, :role_request_failed) do + role_request_failed_to_int(:stale) + catch + _class, _reason -> :stale + end + + def to_int(:unsup, :role_request_failed) do + role_request_failed_to_int(:unsup) + catch + _class, _reason -> :unsup + end + + def to_int(:bad_role, :role_request_failed) do + role_request_failed_to_int(:bad_role) + catch + _class, _reason -> :bad_role + end + + def to_int(_int, :role_request_failed) do + throw(:bad_enum) + end + + def to_int(:unknown, :meter_mod_failed) do + meter_mod_failed_to_int(:unknown) + catch + _class, _reason -> :unknown + end + + def to_int(:meter_exists, :meter_mod_failed) do + meter_mod_failed_to_int(:meter_exists) + catch + _class, _reason -> :meter_exists + end + + def to_int(:invalid_meter, :meter_mod_failed) do + meter_mod_failed_to_int(:invalid_meter) + catch + _class, _reason -> :invalid_meter + end + + def to_int(:unknown_meter, :meter_mod_failed) do + meter_mod_failed_to_int(:unknown_meter) + catch + _class, _reason -> :unknown_meter + end + + def to_int(:bad_command, :meter_mod_failed) do + meter_mod_failed_to_int(:bad_command) + catch + _class, _reason -> :bad_command + end + + def to_int(:bad_flags, :meter_mod_failed) do + meter_mod_failed_to_int(:bad_flags) + catch + _class, _reason -> :bad_flags + end + + def to_int(:bad_rate, :meter_mod_failed) do + meter_mod_failed_to_int(:bad_rate) + catch + _class, _reason -> :bad_rate + end + + def to_int(:bad_burst, :meter_mod_failed) do + meter_mod_failed_to_int(:bad_burst) + catch + _class, _reason -> :bad_burst + end + + def to_int(:bad_band, :meter_mod_failed) do + meter_mod_failed_to_int(:bad_band) + catch + _class, _reason -> :bad_band + end + + def to_int(:bad_band_value, :meter_mod_failed) do + meter_mod_failed_to_int(:bad_band_value) + catch + _class, _reason -> :bad_band_value + end + + def to_int(:out_of_meters, :meter_mod_failed) do + meter_mod_failed_to_int(:out_of_meters) + catch + _class, _reason -> :out_of_meters + end + + def to_int(:out_of_bands, :meter_mod_failed) do + meter_mod_failed_to_int(:out_of_bands) + catch + _class, _reason -> :out_of_bands + end + + def to_int(_int, :meter_mod_failed) do + throw(:bad_enum) + end + + def to_int(:bad_table, :table_features_failed) do + table_features_failed_to_int(:bad_table) + catch + _class, _reason -> :bad_table + end + + def to_int(:bad_metadata, :table_features_failed) do + table_features_failed_to_int(:bad_metadata) + catch + _class, _reason -> :bad_metadata + end + + def to_int(:bad_type, :table_features_failed) do + table_features_failed_to_int(:bad_type) + catch + _class, _reason -> :bad_type + end + + def to_int(:bad_len, :table_features_failed) do + table_features_failed_to_int(:bad_len) + catch + _class, _reason -> :bad_len + end + + def to_int(:bad_argument, :table_features_failed) do + table_features_failed_to_int(:bad_argument) + catch + _class, _reason -> :bad_argument + end + + def to_int(:eperm, :table_features_failed) do + table_features_failed_to_int(:eperm) + catch + _class, _reason -> :eperm + end + + def to_int(_int, :table_features_failed) do + throw(:bad_enum) + end + + def to_int(:flow_stats, :switch_capabilities) do + switch_capabilities_to_int(:flow_stats) + catch + _class, _reason -> :flow_stats + end + + def to_int(:table_stats, :switch_capabilities) do + switch_capabilities_to_int(:table_stats) + catch + _class, _reason -> :table_stats + end + + def to_int(:port_stats, :switch_capabilities) do + switch_capabilities_to_int(:port_stats) + catch + _class, _reason -> :port_stats + end + + def to_int(:group_stats, :switch_capabilities) do + switch_capabilities_to_int(:group_stats) + catch + _class, _reason -> :group_stats + end + + def to_int(:ip_reasm, :switch_capabilities) do + switch_capabilities_to_int(:ip_reasm) + catch + _class, _reason -> :ip_reasm + end + + def to_int(:queue_stats, :switch_capabilities) do + switch_capabilities_to_int(:queue_stats) + catch + _class, _reason -> :queue_stats + end + + def to_int(:arp_match_ip, :switch_capabilities) do + switch_capabilities_to_int(:arp_match_ip) + catch + _class, _reason -> :arp_match_ip + end + + def to_int(:port_blocked, :switch_capabilities) do + switch_capabilities_to_int(:port_blocked) + catch + _class, _reason -> :port_blocked + end + + def to_int(_int, :switch_capabilities) do + throw(:bad_enum) + end + + def to_int(:drop, :config_flags) do + config_flags_to_int(:drop) + catch + _class, _reason -> :drop + end + + def to_int(:reasm, :config_flags) do + config_flags_to_int(:reasm) + catch + _class, _reason -> :reasm + end + + def to_int(_int, :config_flags) do + throw(:bad_enum) + end + + def to_int(:max, :controller_max_len) do + controller_max_len_to_int(:max) + catch + _class, _reason -> :max + end + + def to_int(:no_buffer, :controller_max_len) do + controller_max_len_to_int(:no_buffer) + catch + _class, _reason -> :no_buffer + end + + def to_int(_int, :controller_max_len) do + throw(:bad_enum) + end + + def to_int(:nicira_ext_match, :experimenter_oxm_vendors) do + experimenter_oxm_vendors_to_int(:nicira_ext_match) + catch + _class, _reason -> :nicira_ext_match + end + + def to_int(:hp_ext_match, :experimenter_oxm_vendors) do + experimenter_oxm_vendors_to_int(:hp_ext_match) + catch + _class, _reason -> :hp_ext_match + end + + def to_int(:onf_ext_match, :experimenter_oxm_vendors) do + experimenter_oxm_vendors_to_int(:onf_ext_match) + catch + _class, _reason -> :onf_ext_match + end + + def to_int(_int, :experimenter_oxm_vendors) do + throw(:bad_enum) + end + + def to_int(:standard, :match_type) do + match_type_to_int(:standard) + catch + _class, _reason -> :standard + end + + def to_int(:oxm, :match_type) do + match_type_to_int(:oxm) + catch + _class, _reason -> :oxm + end + + def to_int(_int, :match_type) do + throw(:bad_enum) + end + + def to_int(:nxm_0, :oxm_class) do + oxm_class_to_int(:nxm_0) + catch + _class, _reason -> :nxm_0 + end + + def to_int(:nxm_1, :oxm_class) do + oxm_class_to_int(:nxm_1) + catch + _class, _reason -> :nxm_1 + end + + def to_int(:openflow_basic, :oxm_class) do + oxm_class_to_int(:openflow_basic) + catch + _class, _reason -> :openflow_basic + end + + def to_int(:packet_register, :oxm_class) do + oxm_class_to_int(:packet_register) + catch + _class, _reason -> :packet_register + end + + def to_int(:experimenter, :oxm_class) do + oxm_class_to_int(:experimenter) + catch + _class, _reason -> :experimenter + end + + def to_int(_int, :oxm_class) do + throw(:bad_enum) + end + + def to_int(:nx_in_port, :nxm_0) do + nxm_0_to_int(:nx_in_port) + catch + _class, _reason -> :nx_in_port + end + + def to_int(:nx_eth_dst, :nxm_0) do + nxm_0_to_int(:nx_eth_dst) + catch + _class, _reason -> :nx_eth_dst + end + + def to_int(:nx_eth_src, :nxm_0) do + nxm_0_to_int(:nx_eth_src) + catch + _class, _reason -> :nx_eth_src + end + + def to_int(:nx_eth_type, :nxm_0) do + nxm_0_to_int(:nx_eth_type) + catch + _class, _reason -> :nx_eth_type + end + + def to_int(:nx_vlan_tci, :nxm_0) do + nxm_0_to_int(:nx_vlan_tci) + catch + _class, _reason -> :nx_vlan_tci + end + + def to_int(:nx_ip_tos, :nxm_0) do + nxm_0_to_int(:nx_ip_tos) + catch + _class, _reason -> :nx_ip_tos + end + + def to_int(:nx_ip_proto, :nxm_0) do + nxm_0_to_int(:nx_ip_proto) + catch + _class, _reason -> :nx_ip_proto + end + + def to_int(:nx_ipv4_src, :nxm_0) do + nxm_0_to_int(:nx_ipv4_src) + catch + _class, _reason -> :nx_ipv4_src + end + + def to_int(:nx_ipv4_dst, :nxm_0) do + nxm_0_to_int(:nx_ipv4_dst) + catch + _class, _reason -> :nx_ipv4_dst + end + + def to_int(:nx_tcp_src, :nxm_0) do + nxm_0_to_int(:nx_tcp_src) + catch + _class, _reason -> :nx_tcp_src + end + + def to_int(:nx_tcp_dst, :nxm_0) do + nxm_0_to_int(:nx_tcp_dst) + catch + _class, _reason -> :nx_tcp_dst + end + + def to_int(:nx_udp_src, :nxm_0) do + nxm_0_to_int(:nx_udp_src) + catch + _class, _reason -> :nx_udp_src + end + + def to_int(:nx_udp_dst, :nxm_0) do + nxm_0_to_int(:nx_udp_dst) + catch + _class, _reason -> :nx_udp_dst + end + + def to_int(:nx_icmpv4_type, :nxm_0) do + nxm_0_to_int(:nx_icmpv4_type) + catch + _class, _reason -> :nx_icmpv4_type + end + + def to_int(:nx_icmpv4_code, :nxm_0) do + nxm_0_to_int(:nx_icmpv4_code) + catch + _class, _reason -> :nx_icmpv4_code + end + + def to_int(:nx_arp_op, :nxm_0) do + nxm_0_to_int(:nx_arp_op) + catch + _class, _reason -> :nx_arp_op + end + + def to_int(:nx_arp_spa, :nxm_0) do + nxm_0_to_int(:nx_arp_spa) + catch + _class, _reason -> :nx_arp_spa + end + + def to_int(:nx_arp_tpa, :nxm_0) do + nxm_0_to_int(:nx_arp_tpa) + catch + _class, _reason -> :nx_arp_tpa + end + + def to_int(:nx_tcp_flags, :nxm_0) do + nxm_0_to_int(:nx_tcp_flags) + catch + _class, _reason -> :nx_tcp_flags + end + + def to_int(_int, :nxm_0) do + throw(:bad_enum) + end + + def to_int(:reg0, :nxm_1) do + nxm_1_to_int(:reg0) + catch + _class, _reason -> :reg0 + end + + def to_int(:reg1, :nxm_1) do + nxm_1_to_int(:reg1) + catch + _class, _reason -> :reg1 + end + + def to_int(:reg2, :nxm_1) do + nxm_1_to_int(:reg2) + catch + _class, _reason -> :reg2 + end + + def to_int(:reg3, :nxm_1) do + nxm_1_to_int(:reg3) + catch + _class, _reason -> :reg3 + end + + def to_int(:reg4, :nxm_1) do + nxm_1_to_int(:reg4) + catch + _class, _reason -> :reg4 + end + + def to_int(:reg5, :nxm_1) do + nxm_1_to_int(:reg5) + catch + _class, _reason -> :reg5 + end + + def to_int(:reg6, :nxm_1) do + nxm_1_to_int(:reg6) + catch + _class, _reason -> :reg6 + end + + def to_int(:reg7, :nxm_1) do + nxm_1_to_int(:reg7) + catch + _class, _reason -> :reg7 + end + + def to_int(:reg8, :nxm_1) do + nxm_1_to_int(:reg8) + catch + _class, _reason -> :reg8 + end + + def to_int(:reg9, :nxm_1) do + nxm_1_to_int(:reg9) + catch + _class, _reason -> :reg9 + end + + def to_int(:reg10, :nxm_1) do + nxm_1_to_int(:reg10) + catch + _class, _reason -> :reg10 + end + + def to_int(:reg11, :nxm_1) do + nxm_1_to_int(:reg11) + catch + _class, _reason -> :reg11 + end + + def to_int(:reg12, :nxm_1) do + nxm_1_to_int(:reg12) + catch + _class, _reason -> :reg12 + end + + def to_int(:reg13, :nxm_1) do + nxm_1_to_int(:reg13) + catch + _class, _reason -> :reg13 + end + + def to_int(:reg14, :nxm_1) do + nxm_1_to_int(:reg14) + catch + _class, _reason -> :reg14 + end + + def to_int(:reg15, :nxm_1) do + nxm_1_to_int(:reg15) + catch + _class, _reason -> :reg15 + end + + def to_int(:tun_id, :nxm_1) do + nxm_1_to_int(:tun_id) + catch + _class, _reason -> :tun_id + end + + def to_int(:nx_arp_sha, :nxm_1) do + nxm_1_to_int(:nx_arp_sha) + catch + _class, _reason -> :nx_arp_sha + end + + def to_int(:nx_arp_tha, :nxm_1) do + nxm_1_to_int(:nx_arp_tha) + catch + _class, _reason -> :nx_arp_tha + end + + def to_int(:nx_ipv6_src, :nxm_1) do + nxm_1_to_int(:nx_ipv6_src) + catch + _class, _reason -> :nx_ipv6_src + end + + def to_int(:nx_ipv6_dst, :nxm_1) do + nxm_1_to_int(:nx_ipv6_dst) + catch + _class, _reason -> :nx_ipv6_dst + end + + def to_int(:nx_icmpv6_type, :nxm_1) do + nxm_1_to_int(:nx_icmpv6_type) + catch + _class, _reason -> :nx_icmpv6_type + end + + def to_int(:nx_icmpv6_code, :nxm_1) do + nxm_1_to_int(:nx_icmpv6_code) + catch + _class, _reason -> :nx_icmpv6_code + end + + def to_int(:nx_ipv6_nd_target, :nxm_1) do + nxm_1_to_int(:nx_ipv6_nd_target) + catch + _class, _reason -> :nx_ipv6_nd_target + end + + def to_int(:nx_ipv6_nd_sll, :nxm_1) do + nxm_1_to_int(:nx_ipv6_nd_sll) + catch + _class, _reason -> :nx_ipv6_nd_sll + end + + def to_int(:nx_ipv6_nd_tll, :nxm_1) do + nxm_1_to_int(:nx_ipv6_nd_tll) + catch + _class, _reason -> :nx_ipv6_nd_tll + end + + def to_int(:nx_ip_frag, :nxm_1) do + nxm_1_to_int(:nx_ip_frag) + catch + _class, _reason -> :nx_ip_frag + end + + def to_int(:nx_ipv6_label, :nxm_1) do + nxm_1_to_int(:nx_ipv6_label) + catch + _class, _reason -> :nx_ipv6_label + end + + def to_int(:nx_ip_ecn, :nxm_1) do + nxm_1_to_int(:nx_ip_ecn) + catch + _class, _reason -> :nx_ip_ecn + end + + def to_int(:nx_ip_ttl, :nxm_1) do + nxm_1_to_int(:nx_ip_ttl) + catch + _class, _reason -> :nx_ip_ttl + end + + def to_int(:nx_mpls_ttl, :nxm_1) do + nxm_1_to_int(:nx_mpls_ttl) + catch + _class, _reason -> :nx_mpls_ttl + end + + def to_int(:tun_src, :nxm_1) do + nxm_1_to_int(:tun_src) + catch + _class, _reason -> :tun_src + end + + def to_int(:tun_dst, :nxm_1) do + nxm_1_to_int(:tun_dst) + catch + _class, _reason -> :tun_dst + end + + def to_int(:pkt_mark, :nxm_1) do + nxm_1_to_int(:pkt_mark) + catch + _class, _reason -> :pkt_mark + end + + def to_int(:dp_hash, :nxm_1) do + nxm_1_to_int(:dp_hash) + catch + _class, _reason -> :dp_hash + end + + def to_int(:recirc_id, :nxm_1) do + nxm_1_to_int(:recirc_id) + catch + _class, _reason -> :recirc_id + end + + def to_int(:conj_id, :nxm_1) do + nxm_1_to_int(:conj_id) + catch + _class, _reason -> :conj_id + end + + def to_int(:tun_gbp_id, :nxm_1) do + nxm_1_to_int(:tun_gbp_id) + catch + _class, _reason -> :tun_gbp_id + end + + def to_int(:tun_gbp_flags, :nxm_1) do + nxm_1_to_int(:tun_gbp_flags) + catch + _class, _reason -> :tun_gbp_flags + end + + def to_int(:tun_metadata0, :nxm_1) do + nxm_1_to_int(:tun_metadata0) + catch + _class, _reason -> :tun_metadata0 + end + + def to_int(:tun_metadata1, :nxm_1) do + nxm_1_to_int(:tun_metadata1) + catch + _class, _reason -> :tun_metadata1 + end + + def to_int(:tun_metadata2, :nxm_1) do + nxm_1_to_int(:tun_metadata2) + catch + _class, _reason -> :tun_metadata2 + end + + def to_int(:tun_metadata3, :nxm_1) do + nxm_1_to_int(:tun_metadata3) + catch + _class, _reason -> :tun_metadata3 + end + + def to_int(:tun_metadata4, :nxm_1) do + nxm_1_to_int(:tun_metadata4) + catch + _class, _reason -> :tun_metadata4 + end + + def to_int(:tun_metadata5, :nxm_1) do + nxm_1_to_int(:tun_metadata5) + catch + _class, _reason -> :tun_metadata5 + end + + def to_int(:tun_metadata6, :nxm_1) do + nxm_1_to_int(:tun_metadata6) + catch + _class, _reason -> :tun_metadata6 + end + + def to_int(:tun_metadata7, :nxm_1) do + nxm_1_to_int(:tun_metadata7) + catch + _class, _reason -> :tun_metadata7 + end + + def to_int(:tun_metadata8, :nxm_1) do + nxm_1_to_int(:tun_metadata8) + catch + _class, _reason -> :tun_metadata8 + end + + def to_int(:tun_metadata9, :nxm_1) do + nxm_1_to_int(:tun_metadata9) + catch + _class, _reason -> :tun_metadata9 + end + + def to_int(:tun_metadata10, :nxm_1) do + nxm_1_to_int(:tun_metadata10) + catch + _class, _reason -> :tun_metadata10 + end + + def to_int(:tun_metadata11, :nxm_1) do + nxm_1_to_int(:tun_metadata11) + catch + _class, _reason -> :tun_metadata11 + end + + def to_int(:tun_metadata12, :nxm_1) do + nxm_1_to_int(:tun_metadata12) + catch + _class, _reason -> :tun_metadata12 + end + + def to_int(:tun_metadata13, :nxm_1) do + nxm_1_to_int(:tun_metadata13) + catch + _class, _reason -> :tun_metadata13 + end + + def to_int(:tun_metadata14, :nxm_1) do + nxm_1_to_int(:tun_metadata14) + catch + _class, _reason -> :tun_metadata14 + end + + def to_int(:tun_metadata15, :nxm_1) do + nxm_1_to_int(:tun_metadata15) + catch + _class, _reason -> :tun_metadata15 + end + + def to_int(:tun_metadata16, :nxm_1) do + nxm_1_to_int(:tun_metadata16) + catch + _class, _reason -> :tun_metadata16 + end + + def to_int(:tun_metadata17, :nxm_1) do + nxm_1_to_int(:tun_metadata17) + catch + _class, _reason -> :tun_metadata17 + end + + def to_int(:tun_metadata18, :nxm_1) do + nxm_1_to_int(:tun_metadata18) + catch + _class, _reason -> :tun_metadata18 + end + + def to_int(:tun_metadata19, :nxm_1) do + nxm_1_to_int(:tun_metadata19) + catch + _class, _reason -> :tun_metadata19 + end + + def to_int(:tun_metadata20, :nxm_1) do + nxm_1_to_int(:tun_metadata20) + catch + _class, _reason -> :tun_metadata20 + end + + def to_int(:tun_metadata21, :nxm_1) do + nxm_1_to_int(:tun_metadata21) + catch + _class, _reason -> :tun_metadata21 + end + + def to_int(:tun_metadata22, :nxm_1) do + nxm_1_to_int(:tun_metadata22) + catch + _class, _reason -> :tun_metadata22 + end + + def to_int(:tun_metadata23, :nxm_1) do + nxm_1_to_int(:tun_metadata23) + catch + _class, _reason -> :tun_metadata23 + end + + def to_int(:tun_metadata24, :nxm_1) do + nxm_1_to_int(:tun_metadata24) + catch + _class, _reason -> :tun_metadata24 + end + + def to_int(:tun_metadata25, :nxm_1) do + nxm_1_to_int(:tun_metadata25) + catch + _class, _reason -> :tun_metadata25 + end + + def to_int(:tun_metadata26, :nxm_1) do + nxm_1_to_int(:tun_metadata26) + catch + _class, _reason -> :tun_metadata26 + end + + def to_int(:tun_metadata27, :nxm_1) do + nxm_1_to_int(:tun_metadata27) + catch + _class, _reason -> :tun_metadata27 + end + + def to_int(:tun_metadata28, :nxm_1) do + nxm_1_to_int(:tun_metadata28) + catch + _class, _reason -> :tun_metadata28 + end + + def to_int(:tun_metadata29, :nxm_1) do + nxm_1_to_int(:tun_metadata29) + catch + _class, _reason -> :tun_metadata29 + end + + def to_int(:tun_metadata30, :nxm_1) do + nxm_1_to_int(:tun_metadata30) + catch + _class, _reason -> :tun_metadata30 + end + + def to_int(:tun_metadata31, :nxm_1) do + nxm_1_to_int(:tun_metadata31) + catch + _class, _reason -> :tun_metadata31 + end + + def to_int(:tun_metadata32, :nxm_1) do + nxm_1_to_int(:tun_metadata32) + catch + _class, _reason -> :tun_metadata32 + end + + def to_int(:tun_metadata33, :nxm_1) do + nxm_1_to_int(:tun_metadata33) + catch + _class, _reason -> :tun_metadata33 + end + + def to_int(:tun_metadata34, :nxm_1) do + nxm_1_to_int(:tun_metadata34) + catch + _class, _reason -> :tun_metadata34 + end + + def to_int(:tun_metadata35, :nxm_1) do + nxm_1_to_int(:tun_metadata35) + catch + _class, _reason -> :tun_metadata35 + end + + def to_int(:tun_metadata36, :nxm_1) do + nxm_1_to_int(:tun_metadata36) + catch + _class, _reason -> :tun_metadata36 + end + + def to_int(:tun_metadata37, :nxm_1) do + nxm_1_to_int(:tun_metadata37) + catch + _class, _reason -> :tun_metadata37 + end + + def to_int(:tun_metadata38, :nxm_1) do + nxm_1_to_int(:tun_metadata38) + catch + _class, _reason -> :tun_metadata38 + end + + def to_int(:tun_metadata39, :nxm_1) do + nxm_1_to_int(:tun_metadata39) + catch + _class, _reason -> :tun_metadata39 + end + + def to_int(:tun_metadata40, :nxm_1) do + nxm_1_to_int(:tun_metadata40) + catch + _class, _reason -> :tun_metadata40 + end + + def to_int(:tun_metadata41, :nxm_1) do + nxm_1_to_int(:tun_metadata41) + catch + _class, _reason -> :tun_metadata41 + end + + def to_int(:tun_metadata42, :nxm_1) do + nxm_1_to_int(:tun_metadata42) + catch + _class, _reason -> :tun_metadata42 + end + + def to_int(:tun_metadata43, :nxm_1) do + nxm_1_to_int(:tun_metadata43) + catch + _class, _reason -> :tun_metadata43 + end + + def to_int(:tun_metadata44, :nxm_1) do + nxm_1_to_int(:tun_metadata44) + catch + _class, _reason -> :tun_metadata44 + end + + def to_int(:tun_metadata45, :nxm_1) do + nxm_1_to_int(:tun_metadata45) + catch + _class, _reason -> :tun_metadata45 + end + + def to_int(:tun_metadata46, :nxm_1) do + nxm_1_to_int(:tun_metadata46) + catch + _class, _reason -> :tun_metadata46 + end + + def to_int(:tun_metadata47, :nxm_1) do + nxm_1_to_int(:tun_metadata47) + catch + _class, _reason -> :tun_metadata47 + end + + def to_int(:tun_metadata48, :nxm_1) do + nxm_1_to_int(:tun_metadata48) + catch + _class, _reason -> :tun_metadata48 + end + + def to_int(:tun_metadata49, :nxm_1) do + nxm_1_to_int(:tun_metadata49) + catch + _class, _reason -> :tun_metadata49 + end + + def to_int(:tun_metadata50, :nxm_1) do + nxm_1_to_int(:tun_metadata50) + catch + _class, _reason -> :tun_metadata50 + end + + def to_int(:tun_metadata51, :nxm_1) do + nxm_1_to_int(:tun_metadata51) + catch + _class, _reason -> :tun_metadata51 + end + + def to_int(:tun_metadata52, :nxm_1) do + nxm_1_to_int(:tun_metadata52) + catch + _class, _reason -> :tun_metadata52 + end + + def to_int(:tun_metadata53, :nxm_1) do + nxm_1_to_int(:tun_metadata53) + catch + _class, _reason -> :tun_metadata53 + end + + def to_int(:tun_metadata54, :nxm_1) do + nxm_1_to_int(:tun_metadata54) + catch + _class, _reason -> :tun_metadata54 + end + + def to_int(:tun_metadata55, :nxm_1) do + nxm_1_to_int(:tun_metadata55) + catch + _class, _reason -> :tun_metadata55 + end + + def to_int(:tun_metadata56, :nxm_1) do + nxm_1_to_int(:tun_metadata56) + catch + _class, _reason -> :tun_metadata56 + end + + def to_int(:tun_metadata57, :nxm_1) do + nxm_1_to_int(:tun_metadata57) + catch + _class, _reason -> :tun_metadata57 + end + + def to_int(:tun_metadata58, :nxm_1) do + nxm_1_to_int(:tun_metadata58) + catch + _class, _reason -> :tun_metadata58 + end + + def to_int(:tun_metadata59, :nxm_1) do + nxm_1_to_int(:tun_metadata59) + catch + _class, _reason -> :tun_metadata59 + end + + def to_int(:tun_metadata60, :nxm_1) do + nxm_1_to_int(:tun_metadata60) + catch + _class, _reason -> :tun_metadata60 + end + + def to_int(:tun_metadata61, :nxm_1) do + nxm_1_to_int(:tun_metadata61) + catch + _class, _reason -> :tun_metadata61 + end + + def to_int(:tun_metadata62, :nxm_1) do + nxm_1_to_int(:tun_metadata62) + catch + _class, _reason -> :tun_metadata62 + end + + def to_int(:tun_metadata63, :nxm_1) do + nxm_1_to_int(:tun_metadata63) + catch + _class, _reason -> :tun_metadata63 + end + + def to_int(:tun_flags, :nxm_1) do + nxm_1_to_int(:tun_flags) + catch + _class, _reason -> :tun_flags + end + + def to_int(:ct_state, :nxm_1) do + nxm_1_to_int(:ct_state) + catch + _class, _reason -> :ct_state + end + + def to_int(:ct_zone, :nxm_1) do + nxm_1_to_int(:ct_zone) + catch + _class, _reason -> :ct_zone + end + + def to_int(:ct_mark, :nxm_1) do + nxm_1_to_int(:ct_mark) + catch + _class, _reason -> :ct_mark + end + + def to_int(:ct_label, :nxm_1) do + nxm_1_to_int(:ct_label) + catch + _class, _reason -> :ct_label + end + + def to_int(:tun_ipv6_src, :nxm_1) do + nxm_1_to_int(:tun_ipv6_src) + catch + _class, _reason -> :tun_ipv6_src + end + + def to_int(:tun_ipv6_dst, :nxm_1) do + nxm_1_to_int(:tun_ipv6_dst) + catch + _class, _reason -> :tun_ipv6_dst + end + + def to_int(:xxreg0, :nxm_1) do + nxm_1_to_int(:xxreg0) + catch + _class, _reason -> :xxreg0 + end + + def to_int(:xxreg1, :nxm_1) do + nxm_1_to_int(:xxreg1) + catch + _class, _reason -> :xxreg1 + end + + def to_int(:xxreg2, :nxm_1) do + nxm_1_to_int(:xxreg2) + catch + _class, _reason -> :xxreg2 + end + + def to_int(:xxreg3, :nxm_1) do + nxm_1_to_int(:xxreg3) + catch + _class, _reason -> :xxreg3 + end + + def to_int(:xxreg4, :nxm_1) do + nxm_1_to_int(:xxreg4) + catch + _class, _reason -> :xxreg4 + end + + def to_int(:xxreg5, :nxm_1) do + nxm_1_to_int(:xxreg5) + catch + _class, _reason -> :xxreg5 + end + + def to_int(:xxreg6, :nxm_1) do + nxm_1_to_int(:xxreg6) + catch + _class, _reason -> :xxreg6 + end + + def to_int(:xxreg7, :nxm_1) do + nxm_1_to_int(:xxreg7) + catch + _class, _reason -> :xxreg7 + end + + def to_int(:ct_nw_proto, :nxm_1) do + nxm_1_to_int(:ct_nw_proto) + catch + _class, _reason -> :ct_nw_proto + end + + def to_int(:ct_nw_src, :nxm_1) do + nxm_1_to_int(:ct_nw_src) + catch + _class, _reason -> :ct_nw_src + end + + def to_int(:ct_nw_dst, :nxm_1) do + nxm_1_to_int(:ct_nw_dst) + catch + _class, _reason -> :ct_nw_dst + end + + def to_int(:ct_ipv6_src, :nxm_1) do + nxm_1_to_int(:ct_ipv6_src) + catch + _class, _reason -> :ct_ipv6_src + end + + def to_int(:ct_ipv6_dst, :nxm_1) do + nxm_1_to_int(:ct_ipv6_dst) + catch + _class, _reason -> :ct_ipv6_dst + end + + def to_int(:ct_tp_src, :nxm_1) do + nxm_1_to_int(:ct_tp_src) + catch + _class, _reason -> :ct_tp_src + end + + def to_int(:ct_tp_dst, :nxm_1) do + nxm_1_to_int(:ct_tp_dst) + catch + _class, _reason -> :ct_tp_dst + end + + def to_int(_int, :nxm_1) do + throw(:bad_enum) + end + + def to_int(:in_port, :openflow_basic) do + openflow_basic_to_int(:in_port) + catch + _class, _reason -> :in_port + end + + def to_int(:in_phy_port, :openflow_basic) do + openflow_basic_to_int(:in_phy_port) + catch + _class, _reason -> :in_phy_port + end + + def to_int(:metadata, :openflow_basic) do + openflow_basic_to_int(:metadata) + catch + _class, _reason -> :metadata + end + + def to_int(:eth_dst, :openflow_basic) do + openflow_basic_to_int(:eth_dst) + catch + _class, _reason -> :eth_dst + end + + def to_int(:eth_src, :openflow_basic) do + openflow_basic_to_int(:eth_src) + catch + _class, _reason -> :eth_src + end + + def to_int(:eth_type, :openflow_basic) do + openflow_basic_to_int(:eth_type) + catch + _class, _reason -> :eth_type + end + + def to_int(:vlan_vid, :openflow_basic) do + openflow_basic_to_int(:vlan_vid) + catch + _class, _reason -> :vlan_vid + end + + def to_int(:vlan_pcp, :openflow_basic) do + openflow_basic_to_int(:vlan_pcp) + catch + _class, _reason -> :vlan_pcp + end + + def to_int(:ip_dscp, :openflow_basic) do + openflow_basic_to_int(:ip_dscp) + catch + _class, _reason -> :ip_dscp + end + + def to_int(:ip_ecn, :openflow_basic) do + openflow_basic_to_int(:ip_ecn) + catch + _class, _reason -> :ip_ecn + end + + def to_int(:ip_proto, :openflow_basic) do + openflow_basic_to_int(:ip_proto) + catch + _class, _reason -> :ip_proto + end + + def to_int(:ipv4_src, :openflow_basic) do + openflow_basic_to_int(:ipv4_src) + catch + _class, _reason -> :ipv4_src + end + + def to_int(:ipv4_dst, :openflow_basic) do + openflow_basic_to_int(:ipv4_dst) + catch + _class, _reason -> :ipv4_dst + end + + def to_int(:tcp_src, :openflow_basic) do + openflow_basic_to_int(:tcp_src) + catch + _class, _reason -> :tcp_src + end + + def to_int(:tcp_dst, :openflow_basic) do + openflow_basic_to_int(:tcp_dst) + catch + _class, _reason -> :tcp_dst + end + + def to_int(:udp_src, :openflow_basic) do + openflow_basic_to_int(:udp_src) + catch + _class, _reason -> :udp_src + end + + def to_int(:udp_dst, :openflow_basic) do + openflow_basic_to_int(:udp_dst) + catch + _class, _reason -> :udp_dst + end + + def to_int(:sctp_src, :openflow_basic) do + openflow_basic_to_int(:sctp_src) + catch + _class, _reason -> :sctp_src + end + + def to_int(:sctp_dst, :openflow_basic) do + openflow_basic_to_int(:sctp_dst) + catch + _class, _reason -> :sctp_dst + end + + def to_int(:icmpv4_type, :openflow_basic) do + openflow_basic_to_int(:icmpv4_type) + catch + _class, _reason -> :icmpv4_type + end + + def to_int(:icmpv4_code, :openflow_basic) do + openflow_basic_to_int(:icmpv4_code) + catch + _class, _reason -> :icmpv4_code + end + + def to_int(:arp_op, :openflow_basic) do + openflow_basic_to_int(:arp_op) + catch + _class, _reason -> :arp_op + end + + def to_int(:arp_spa, :openflow_basic) do + openflow_basic_to_int(:arp_spa) + catch + _class, _reason -> :arp_spa + end + + def to_int(:arp_tpa, :openflow_basic) do + openflow_basic_to_int(:arp_tpa) + catch + _class, _reason -> :arp_tpa + end + + def to_int(:arp_sha, :openflow_basic) do + openflow_basic_to_int(:arp_sha) + catch + _class, _reason -> :arp_sha + end + + def to_int(:arp_tha, :openflow_basic) do + openflow_basic_to_int(:arp_tha) + catch + _class, _reason -> :arp_tha + end + + def to_int(:ipv6_src, :openflow_basic) do + openflow_basic_to_int(:ipv6_src) + catch + _class, _reason -> :ipv6_src + end + + def to_int(:ipv6_dst, :openflow_basic) do + openflow_basic_to_int(:ipv6_dst) + catch + _class, _reason -> :ipv6_dst + end + + def to_int(:ipv6_flabel, :openflow_basic) do + openflow_basic_to_int(:ipv6_flabel) + catch + _class, _reason -> :ipv6_flabel + end + + def to_int(:icmpv6_type, :openflow_basic) do + openflow_basic_to_int(:icmpv6_type) + catch + _class, _reason -> :icmpv6_type + end + + def to_int(:icmpv6_code, :openflow_basic) do + openflow_basic_to_int(:icmpv6_code) + catch + _class, _reason -> :icmpv6_code + end + + def to_int(:ipv6_nd_target, :openflow_basic) do + openflow_basic_to_int(:ipv6_nd_target) + catch + _class, _reason -> :ipv6_nd_target + end + + def to_int(:ipv6_nd_sll, :openflow_basic) do + openflow_basic_to_int(:ipv6_nd_sll) + catch + _class, _reason -> :ipv6_nd_sll + end + + def to_int(:ipv6_nd_tll, :openflow_basic) do + openflow_basic_to_int(:ipv6_nd_tll) + catch + _class, _reason -> :ipv6_nd_tll + end + + def to_int(:mpls_label, :openflow_basic) do + openflow_basic_to_int(:mpls_label) + catch + _class, _reason -> :mpls_label + end + + def to_int(:mpls_tc, :openflow_basic) do + openflow_basic_to_int(:mpls_tc) + catch + _class, _reason -> :mpls_tc + end + + def to_int(:mpls_bos, :openflow_basic) do + openflow_basic_to_int(:mpls_bos) + catch + _class, _reason -> :mpls_bos + end + + def to_int(:pbb_isid, :openflow_basic) do + openflow_basic_to_int(:pbb_isid) + catch + _class, _reason -> :pbb_isid + end + + def to_int(:tunnel_id, :openflow_basic) do + openflow_basic_to_int(:tunnel_id) + catch + _class, _reason -> :tunnel_id + end + + def to_int(:ipv6_exthdr, :openflow_basic) do + openflow_basic_to_int(:ipv6_exthdr) + catch + _class, _reason -> :ipv6_exthdr + end + + def to_int(:pbb_uca, :openflow_basic) do + openflow_basic_to_int(:pbb_uca) + catch + _class, _reason -> :pbb_uca + end + + def to_int(:packet_type, :openflow_basic) do + openflow_basic_to_int(:packet_type) + catch + _class, _reason -> :packet_type + end + + def to_int(:gre_flags, :openflow_basic) do + openflow_basic_to_int(:gre_flags) + catch + _class, _reason -> :gre_flags + end + + def to_int(:gre_ver, :openflow_basic) do + openflow_basic_to_int(:gre_ver) + catch + _class, _reason -> :gre_ver + end + + def to_int(:gre_protocol, :openflow_basic) do + openflow_basic_to_int(:gre_protocol) + catch + _class, _reason -> :gre_protocol + end + + def to_int(:gre_key, :openflow_basic) do + openflow_basic_to_int(:gre_key) + catch + _class, _reason -> :gre_key + end + + def to_int(:gre_seqnum, :openflow_basic) do + openflow_basic_to_int(:gre_seqnum) + catch + _class, _reason -> :gre_seqnum + end + + def to_int(:lisp_flags, :openflow_basic) do + openflow_basic_to_int(:lisp_flags) + catch + _class, _reason -> :lisp_flags + end + + def to_int(:lisp_nonce, :openflow_basic) do + openflow_basic_to_int(:lisp_nonce) + catch + _class, _reason -> :lisp_nonce + end + + def to_int(:lisp_id, :openflow_basic) do + openflow_basic_to_int(:lisp_id) + catch + _class, _reason -> :lisp_id + end + + def to_int(:vxlan_flags, :openflow_basic) do + openflow_basic_to_int(:vxlan_flags) + catch + _class, _reason -> :vxlan_flags + end + + def to_int(:vxlan_vni, :openflow_basic) do + openflow_basic_to_int(:vxlan_vni) + catch + _class, _reason -> :vxlan_vni + end + + def to_int(:mpls_data_first_nibble, :openflow_basic) do + openflow_basic_to_int(:mpls_data_first_nibble) + catch + _class, _reason -> :mpls_data_first_nibble + end + + def to_int(:mpls_ach_version, :openflow_basic) do + openflow_basic_to_int(:mpls_ach_version) + catch + _class, _reason -> :mpls_ach_version + end + + def to_int(:mpls_ach_channel, :openflow_basic) do + openflow_basic_to_int(:mpls_ach_channel) + catch + _class, _reason -> :mpls_ach_channel + end + + def to_int(:mpls_pw_metadata, :openflow_basic) do + openflow_basic_to_int(:mpls_pw_metadata) + catch + _class, _reason -> :mpls_pw_metadata + end + + def to_int(:mpls_cw_flags, :openflow_basic) do + openflow_basic_to_int(:mpls_cw_flags) + catch + _class, _reason -> :mpls_cw_flags + end + + def to_int(:mpls_cw_fragment, :openflow_basic) do + openflow_basic_to_int(:mpls_cw_fragment) + catch + _class, _reason -> :mpls_cw_fragment + end + + def to_int(:mpls_cw_len, :openflow_basic) do + openflow_basic_to_int(:mpls_cw_len) + catch + _class, _reason -> :mpls_cw_len + end + + def to_int(:mpls_cw_seq_num, :openflow_basic) do + openflow_basic_to_int(:mpls_cw_seq_num) + catch + _class, _reason -> :mpls_cw_seq_num + end + + def to_int(:gtpu_flags, :openflow_basic) do + openflow_basic_to_int(:gtpu_flags) + catch + _class, _reason -> :gtpu_flags + end + + def to_int(:gtpu_ver, :openflow_basic) do + openflow_basic_to_int(:gtpu_ver) + catch + _class, _reason -> :gtpu_ver + end + + def to_int(:gtpu_msg_type, :openflow_basic) do + openflow_basic_to_int(:gtpu_msg_type) + catch + _class, _reason -> :gtpu_msg_type + end + + def to_int(:gtpu_teid, :openflow_basic) do + openflow_basic_to_int(:gtpu_teid) + catch + _class, _reason -> :gtpu_teid + end + + def to_int(:gtpu_extn_hdr, :openflow_basic) do + openflow_basic_to_int(:gtpu_extn_hdr) + catch + _class, _reason -> :gtpu_extn_hdr + end + + def to_int(:gtpu_extn_udp_port, :openflow_basic) do + openflow_basic_to_int(:gtpu_extn_udp_port) + catch + _class, _reason -> :gtpu_extn_udp_port + end + + def to_int(:gtpu_extn_sci, :openflow_basic) do + openflow_basic_to_int(:gtpu_extn_sci) + catch + _class, _reason -> :gtpu_extn_sci + end + + def to_int(_int, :openflow_basic) do + throw(:bad_enum) + end + + def to_int(:present, :vlan_id) do + vlan_id_to_int(:present) + catch + _class, _reason -> :present + end + + def to_int(:none, :vlan_id) do + vlan_id_to_int(:none) + catch + _class, _reason -> :none + end + + def to_int(_int, :vlan_id) do + throw(:bad_enum) + end + + def to_int(:nonext, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:nonext) + catch + _class, _reason -> :nonext + end + + def to_int(:esp, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:esp) + catch + _class, _reason -> :esp + end + + def to_int(:auth, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:auth) + catch + _class, _reason -> :auth + end + + def to_int(:dest, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:dest) + catch + _class, _reason -> :dest + end + + def to_int(:frag, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:frag) + catch + _class, _reason -> :frag + end + + def to_int(:router, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:router) + catch + _class, _reason -> :router + end + + def to_int(:hop, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:hop) + catch + _class, _reason -> :hop + end + + def to_int(:unrep, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:unrep) + catch + _class, _reason -> :unrep + end + + def to_int(:unseq, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_int(:unseq) + catch + _class, _reason -> :unseq + end + + def to_int(_int, :ipv6exthdr_flags) do + throw(:bad_enum) + end + + def to_int(:fin, :tcp_flags) do + tcp_flags_to_int(:fin) + catch + _class, _reason -> :fin + end + + def to_int(:syn, :tcp_flags) do + tcp_flags_to_int(:syn) + catch + _class, _reason -> :syn + end + + def to_int(:rst, :tcp_flags) do + tcp_flags_to_int(:rst) + catch + _class, _reason -> :rst + end + + def to_int(:psh, :tcp_flags) do + tcp_flags_to_int(:psh) + catch + _class, _reason -> :psh + end + + def to_int(:ack, :tcp_flags) do + tcp_flags_to_int(:ack) + catch + _class, _reason -> :ack + end + + def to_int(:urg, :tcp_flags) do + tcp_flags_to_int(:urg) + catch + _class, _reason -> :urg + end + + def to_int(:ece, :tcp_flags) do + tcp_flags_to_int(:ece) + catch + _class, _reason -> :ece + end + + def to_int(:cwr, :tcp_flags) do + tcp_flags_to_int(:cwr) + catch + _class, _reason -> :cwr + end + + def to_int(:ns, :tcp_flags) do + tcp_flags_to_int(:ns) + catch + _class, _reason -> :ns + end + + def to_int(_int, :tcp_flags) do + throw(:bad_enum) + end + + def to_int(:new, :ct_state_flags) do + ct_state_flags_to_int(:new) + catch + _class, _reason -> :new + end + + def to_int(:est, :ct_state_flags) do + ct_state_flags_to_int(:est) + catch + _class, _reason -> :est + end + + def to_int(:rel, :ct_state_flags) do + ct_state_flags_to_int(:rel) + catch + _class, _reason -> :rel + end + + def to_int(:rep, :ct_state_flags) do + ct_state_flags_to_int(:rep) + catch + _class, _reason -> :rep + end + + def to_int(:inv, :ct_state_flags) do + ct_state_flags_to_int(:inv) + catch + _class, _reason -> :inv + end + + def to_int(:trk, :ct_state_flags) do + ct_state_flags_to_int(:trk) + catch + _class, _reason -> :trk + end + + def to_int(:snat, :ct_state_flags) do + ct_state_flags_to_int(:snat) + catch + _class, _reason -> :snat + end + + def to_int(:dnat, :ct_state_flags) do + ct_state_flags_to_int(:dnat) + catch + _class, _reason -> :dnat + end + + def to_int(_int, :ct_state_flags) do + throw(:bad_enum) + end + + def to_int(:xreg0, :packet_register) do + packet_register_to_int(:xreg0) + catch + _class, _reason -> :xreg0 + end + + def to_int(:xreg1, :packet_register) do + packet_register_to_int(:xreg1) + catch + _class, _reason -> :xreg1 + end + + def to_int(:xreg2, :packet_register) do + packet_register_to_int(:xreg2) + catch + _class, _reason -> :xreg2 + end + + def to_int(:xreg3, :packet_register) do + packet_register_to_int(:xreg3) + catch + _class, _reason -> :xreg3 + end + + def to_int(:xreg4, :packet_register) do + packet_register_to_int(:xreg4) + catch + _class, _reason -> :xreg4 + end + + def to_int(:xreg5, :packet_register) do + packet_register_to_int(:xreg5) + catch + _class, _reason -> :xreg5 + end + + def to_int(:xreg6, :packet_register) do + packet_register_to_int(:xreg6) + catch + _class, _reason -> :xreg6 + end + + def to_int(:xreg7, :packet_register) do + packet_register_to_int(:xreg7) + catch + _class, _reason -> :xreg7 + end + + def to_int(_int, :packet_register) do + throw(:bad_enum) + end + + def to_int(:nsh_flags, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_flags) + catch + _class, _reason -> :nsh_flags + end + + def to_int(:nsh_mdtype, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_mdtype) + catch + _class, _reason -> :nsh_mdtype + end + + def to_int(:nsh_np, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_np) + catch + _class, _reason -> :nsh_np + end + + def to_int(:nsh_spi, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_spi) + catch + _class, _reason -> :nsh_spi + end + + def to_int(:nsh_si, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_si) + catch + _class, _reason -> :nsh_si + end + + def to_int(:nsh_c1, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_c1) + catch + _class, _reason -> :nsh_c1 + end + + def to_int(:nsh_c2, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_c2) + catch + _class, _reason -> :nsh_c2 + end + + def to_int(:nsh_c3, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_c3) + catch + _class, _reason -> :nsh_c3 + end + + def to_int(:nsh_c4, :nicira_ext_match) do + nicira_ext_match_to_int(:nsh_c4) + catch + _class, _reason -> :nsh_c4 + end + + def to_int(_int, :nicira_ext_match) do + throw(:bad_enum) + end + + def to_int(:hp_udp_src_port_range, :hp_ext_match) do + hp_ext_match_to_int(:hp_udp_src_port_range) + catch + _class, _reason -> :hp_udp_src_port_range + end + + def to_int(:hp_udp_dst_port_range, :hp_ext_match) do + hp_ext_match_to_int(:hp_udp_dst_port_range) + catch + _class, _reason -> :hp_udp_dst_port_range + end + + def to_int(:hp_tcp_src_port_range, :hp_ext_match) do + hp_ext_match_to_int(:hp_tcp_src_port_range) + catch + _class, _reason -> :hp_tcp_src_port_range + end + + def to_int(:hp_tcp_dst_port_range, :hp_ext_match) do + hp_ext_match_to_int(:hp_tcp_dst_port_range) + catch + _class, _reason -> :hp_tcp_dst_port_range + end + + def to_int(:hp_tcp_flags, :hp_ext_match) do + hp_ext_match_to_int(:hp_tcp_flags) + catch + _class, _reason -> :hp_tcp_flags + end + + def to_int(:hp_custom_1, :hp_ext_match) do + hp_ext_match_to_int(:hp_custom_1) + catch + _class, _reason -> :hp_custom_1 + end + + def to_int(:hp_custom_2, :hp_ext_match) do + hp_ext_match_to_int(:hp_custom_2) + catch + _class, _reason -> :hp_custom_2 + end + + def to_int(:hp_custom_3, :hp_ext_match) do + hp_ext_match_to_int(:hp_custom_3) + catch + _class, _reason -> :hp_custom_3 + end + + def to_int(:hp_custom_4, :hp_ext_match) do + hp_ext_match_to_int(:hp_custom_4) + catch + _class, _reason -> :hp_custom_4 + end + + def to_int(_int, :hp_ext_match) do + throw(:bad_enum) + end + + def to_int(:l2_start, :hp_custom_match_type) do + hp_custom_match_type_to_int(:l2_start) + catch + _class, _reason -> :l2_start + end + + def to_int(:l3_start, :hp_custom_match_type) do + hp_custom_match_type_to_int(:l3_start) + catch + _class, _reason -> :l3_start + end + + def to_int(:l4_start, :hp_custom_match_type) do + hp_custom_match_type_to_int(:l4_start) + catch + _class, _reason -> :l4_start + end + + def to_int(_int, :hp_custom_match_type) do + throw(:bad_enum) + end + + def to_int(:onf_tcp_flags, :onf_ext_match) do + onf_ext_match_to_int(:onf_tcp_flags) + catch + _class, _reason -> :onf_tcp_flags + end + + def to_int(:onf_actset_output, :onf_ext_match) do + onf_ext_match_to_int(:onf_actset_output) + catch + _class, _reason -> :onf_actset_output + end + + def to_int(:onf_pbb_uca, :onf_ext_match) do + onf_ext_match_to_int(:onf_pbb_uca) + catch + _class, _reason -> :onf_pbb_uca + end + + def to_int(_int, :onf_ext_match) do + throw(:bad_enum) + end + + def to_int(:no_buffer, :buffer_id) do + buffer_id_to_int(:no_buffer) + catch + _class, _reason -> :no_buffer + end + + def to_int(_int, :buffer_id) do + throw(:bad_enum) + end + + def to_int(:port_down, :port_config) do + port_config_to_int(:port_down) + catch + _class, _reason -> :port_down + end + + def to_int(:no_receive, :port_config) do + port_config_to_int(:no_receive) + catch + _class, _reason -> :no_receive + end + + def to_int(:no_forward, :port_config) do + port_config_to_int(:no_forward) + catch + _class, _reason -> :no_forward + end + + def to_int(:no_packet_in, :port_config) do + port_config_to_int(:no_packet_in) + catch + _class, _reason -> :no_packet_in + end + + def to_int(_int, :port_config) do + throw(:bad_enum) + end + + def to_int(:link_down, :port_state) do + port_state_to_int(:link_down) + catch + _class, _reason -> :link_down + end + + def to_int(:blocked, :port_state) do + port_state_to_int(:blocked) + catch + _class, _reason -> :blocked + end + + def to_int(:live, :port_state) do + port_state_to_int(:live) + catch + _class, _reason -> :live + end + + def to_int(_int, :port_state) do + throw(:bad_enum) + end + + def to_int(:"10mb_hd", :port_features) do + port_features_to_int(:"10mb_hd") + catch + _class, _reason -> :"10mb_hd" + end + + def to_int(:"10mb_fd", :port_features) do + port_features_to_int(:"10mb_fd") + catch + _class, _reason -> :"10mb_fd" + end + + def to_int(:"100mb_hd", :port_features) do + port_features_to_int(:"100mb_hd") + catch + _class, _reason -> :"100mb_hd" + end + + def to_int(:"100mb_fd", :port_features) do + port_features_to_int(:"100mb_fd") + catch + _class, _reason -> :"100mb_fd" + end + + def to_int(:"1gb_hd", :port_features) do + port_features_to_int(:"1gb_hd") + catch + _class, _reason -> :"1gb_hd" + end + + def to_int(:"1gb_fd", :port_features) do + port_features_to_int(:"1gb_fd") + catch + _class, _reason -> :"1gb_fd" + end + + def to_int(:"10gb_fd", :port_features) do + port_features_to_int(:"10gb_fd") + catch + _class, _reason -> :"10gb_fd" + end + + def to_int(:"40gb_fd", :port_features) do + port_features_to_int(:"40gb_fd") + catch + _class, _reason -> :"40gb_fd" + end + + def to_int(:"100gb_fd", :port_features) do + port_features_to_int(:"100gb_fd") + catch + _class, _reason -> :"100gb_fd" + end + + def to_int(:"1tb_fd", :port_features) do + port_features_to_int(:"1tb_fd") + catch + _class, _reason -> :"1tb_fd" + end + + def to_int(:other, :port_features) do + port_features_to_int(:other) + catch + _class, _reason -> :other + end + + def to_int(:copper, :port_features) do + port_features_to_int(:copper) + catch + _class, _reason -> :copper + end + + def to_int(:fiber, :port_features) do + port_features_to_int(:fiber) + catch + _class, _reason -> :fiber + end + + def to_int(:autoneg, :port_features) do + port_features_to_int(:autoneg) + catch + _class, _reason -> :autoneg + end + + def to_int(:pause, :port_features) do + port_features_to_int(:pause) + catch + _class, _reason -> :pause + end + + def to_int(:pause_asym, :port_features) do + port_features_to_int(:pause_asym) + catch + _class, _reason -> :pause_asym + end + + def to_int(_int, :port_features) do + throw(:bad_enum) + end + + def to_int(:max, :openflow10_port_no) do + openflow10_port_no_to_int(:max) + catch + _class, _reason -> :max + end + + def to_int(:in_port, :openflow10_port_no) do + openflow10_port_no_to_int(:in_port) + catch + _class, _reason -> :in_port + end + + def to_int(:table, :openflow10_port_no) do + openflow10_port_no_to_int(:table) + catch + _class, _reason -> :table + end + + def to_int(:normal, :openflow10_port_no) do + openflow10_port_no_to_int(:normal) + catch + _class, _reason -> :normal + end + + def to_int(:flood, :openflow10_port_no) do + openflow10_port_no_to_int(:flood) + catch + _class, _reason -> :flood + end + + def to_int(:all, :openflow10_port_no) do + openflow10_port_no_to_int(:all) + catch + _class, _reason -> :all + end + + def to_int(:controller, :openflow10_port_no) do + openflow10_port_no_to_int(:controller) + catch + _class, _reason -> :controller + end + + def to_int(:local, :openflow10_port_no) do + openflow10_port_no_to_int(:local) + catch + _class, _reason -> :local + end + + def to_int(:none, :openflow10_port_no) do + openflow10_port_no_to_int(:none) + catch + _class, _reason -> :none + end + + def to_int(_int, :openflow10_port_no) do + throw(:bad_enum) + end + + def to_int(:max, :openflow13_port_no) do + openflow13_port_no_to_int(:max) + catch + _class, _reason -> :max + end + + def to_int(:in_port, :openflow13_port_no) do + openflow13_port_no_to_int(:in_port) + catch + _class, _reason -> :in_port + end + + def to_int(:table, :openflow13_port_no) do + openflow13_port_no_to_int(:table) + catch + _class, _reason -> :table + end + + def to_int(:normal, :openflow13_port_no) do + openflow13_port_no_to_int(:normal) + catch + _class, _reason -> :normal + end + + def to_int(:flood, :openflow13_port_no) do + openflow13_port_no_to_int(:flood) + catch + _class, _reason -> :flood + end + + def to_int(:all, :openflow13_port_no) do + openflow13_port_no_to_int(:all) + catch + _class, _reason -> :all + end + + def to_int(:controller, :openflow13_port_no) do + openflow13_port_no_to_int(:controller) + catch + _class, _reason -> :controller + end + + def to_int(:local, :openflow13_port_no) do + openflow13_port_no_to_int(:local) + catch + _class, _reason -> :local + end + + def to_int(:any, :openflow13_port_no) do + openflow13_port_no_to_int(:any) + catch + _class, _reason -> :any + end + + def to_int(_int, :openflow13_port_no) do + throw(:bad_enum) + end + + def to_int(:no_match, :packet_in_reason) do + packet_in_reason_to_int(:no_match) + catch + _class, _reason -> :no_match + end + + def to_int(:action, :packet_in_reason) do + packet_in_reason_to_int(:action) + catch + _class, _reason -> :action + end + + def to_int(:invalid_ttl, :packet_in_reason) do + packet_in_reason_to_int(:invalid_ttl) + catch + _class, _reason -> :invalid_ttl + end + + def to_int(:action_set, :packet_in_reason) do + packet_in_reason_to_int(:action_set) + catch + _class, _reason -> :action_set + end + + def to_int(:group, :packet_in_reason) do + packet_in_reason_to_int(:group) + catch + _class, _reason -> :group + end + + def to_int(:packet_out, :packet_in_reason) do + packet_in_reason_to_int(:packet_out) + catch + _class, _reason -> :packet_out + end + + def to_int(_int, :packet_in_reason) do + throw(:bad_enum) + end + + def to_int(:add, :flow_mod_command) do + flow_mod_command_to_int(:add) + catch + _class, _reason -> :add + end + + def to_int(:modify, :flow_mod_command) do + flow_mod_command_to_int(:modify) + catch + _class, _reason -> :modify + end + + def to_int(:modify_strict, :flow_mod_command) do + flow_mod_command_to_int(:modify_strict) + catch + _class, _reason -> :modify_strict + end + + def to_int(:delete, :flow_mod_command) do + flow_mod_command_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(:delete_strict, :flow_mod_command) do + flow_mod_command_to_int(:delete_strict) + catch + _class, _reason -> :delete_strict + end + + def to_int(_int, :flow_mod_command) do + throw(:bad_enum) + end + + def to_int(:send_flow_rem, :flow_mod_flags) do + flow_mod_flags_to_int(:send_flow_rem) + catch + _class, _reason -> :send_flow_rem + end + + def to_int(:check_overlap, :flow_mod_flags) do + flow_mod_flags_to_int(:check_overlap) + catch + _class, _reason -> :check_overlap + end + + def to_int(:reset_counts, :flow_mod_flags) do + flow_mod_flags_to_int(:reset_counts) + catch + _class, _reason -> :reset_counts + end + + def to_int(:no_packet_counts, :flow_mod_flags) do + flow_mod_flags_to_int(:no_packet_counts) + catch + _class, _reason -> :no_packet_counts + end + + def to_int(:no_byte_counts, :flow_mod_flags) do + flow_mod_flags_to_int(:no_byte_counts) + catch + _class, _reason -> :no_byte_counts + end + + def to_int(_int, :flow_mod_flags) do + throw(:bad_enum) + end + + def to_int(:idle_timeout, :flow_removed_reason) do + flow_removed_reason_to_int(:idle_timeout) + catch + _class, _reason -> :idle_timeout + end + + def to_int(:hard_timeout, :flow_removed_reason) do + flow_removed_reason_to_int(:hard_timeout) + catch + _class, _reason -> :hard_timeout + end + + def to_int(:delete, :flow_removed_reason) do + flow_removed_reason_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(:group_delete, :flow_removed_reason) do + flow_removed_reason_to_int(:group_delete) + catch + _class, _reason -> :group_delete + end + + def to_int(:meter_delete, :flow_removed_reason) do + flow_removed_reason_to_int(:meter_delete) + catch + _class, _reason -> :meter_delete + end + + def to_int(:eviction, :flow_removed_reason) do + flow_removed_reason_to_int(:eviction) + catch + _class, _reason -> :eviction + end + + def to_int(_int, :flow_removed_reason) do + throw(:bad_enum) + end + + def to_int(:add, :port_reason) do + port_reason_to_int(:add) + catch + _class, _reason -> :add + end + + def to_int(:delete, :port_reason) do + port_reason_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(:modify, :port_reason) do + port_reason_to_int(:modify) + catch + _class, _reason -> :modify + end + + def to_int(_int, :port_reason) do + throw(:bad_enum) + end + + def to_int(:add, :group_mod_command) do + group_mod_command_to_int(:add) + catch + _class, _reason -> :add + end + + def to_int(:modify, :group_mod_command) do + group_mod_command_to_int(:modify) + catch + _class, _reason -> :modify + end + + def to_int(:delete, :group_mod_command) do + group_mod_command_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(_int, :group_mod_command) do + throw(:bad_enum) + end + + def to_int(:all, :group_type) do + group_type_to_int(:all) + catch + _class, _reason -> :all + end + + def to_int(:select, :group_type) do + group_type_to_int(:select) + catch + _class, _reason -> :select + end + + def to_int(:indirect, :group_type) do + group_type_to_int(:indirect) + catch + _class, _reason -> :indirect + end + + def to_int(:fast_failover, :group_type) do + group_type_to_int(:fast_failover) + catch + _class, _reason -> :fast_failover + end + + def to_int(_int, :group_type) do + throw(:bad_enum) + end + + def to_int(:max, :group_id) do + group_id_to_int(:max) + catch + _class, _reason -> :max + end + + def to_int(:all, :group_id) do + group_id_to_int(:all) + catch + _class, _reason -> :all + end + + def to_int(:any, :group_id) do + group_id_to_int(:any) + catch + _class, _reason -> :any + end + + def to_int(_int, :group_id) do + throw(:bad_enum) + end + + def to_int(:select_weight, :group_capabilities) do + group_capabilities_to_int(:select_weight) + catch + _class, _reason -> :select_weight + end + + def to_int(:select_liveness, :group_capabilities) do + group_capabilities_to_int(:select_liveness) + catch + _class, _reason -> :select_liveness + end + + def to_int(:chaining, :group_capabilities) do + group_capabilities_to_int(:chaining) + catch + _class, _reason -> :chaining + end + + def to_int(:chaining_checks, :group_capabilities) do + group_capabilities_to_int(:chaining_checks) + catch + _class, _reason -> :chaining_checks + end + + def to_int(_int, :group_capabilities) do + throw(:bad_enum) + end + + def to_int(:max, :table_id) do + table_id_to_int(:max) + catch + _class, _reason -> :max + end + + def to_int(:all, :table_id) do + table_id_to_int(:all) + catch + _class, _reason -> :all + end + + def to_int(_int, :table_id) do + throw(:bad_enum) + end + + def to_int(:all, :queue_id) do + queue_id_to_int(:all) + catch + _class, _reason -> :all + end + + def to_int(_int, :queue_id) do + throw(:bad_enum) + end + + def to_int(:add, :meter_mod_command) do + meter_mod_command_to_int(:add) + catch + _class, _reason -> :add + end + + def to_int(:modify, :meter_mod_command) do + meter_mod_command_to_int(:modify) + catch + _class, _reason -> :modify + end + + def to_int(:delete, :meter_mod_command) do + meter_mod_command_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(_int, :meter_mod_command) do + throw(:bad_enum) + end + + def to_int(:max, :meter_id) do + meter_id_to_int(:max) + catch + _class, _reason -> :max + end + + def to_int(:slowpath, :meter_id) do + meter_id_to_int(:slowpath) + catch + _class, _reason -> :slowpath + end + + def to_int(:controller, :meter_id) do + meter_id_to_int(:controller) + catch + _class, _reason -> :controller + end + + def to_int(:all, :meter_id) do + meter_id_to_int(:all) + catch + _class, _reason -> :all + end + + def to_int(_int, :meter_id) do + throw(:bad_enum) + end + + def to_int(:kbps, :meter_flags) do + meter_flags_to_int(:kbps) + catch + _class, _reason -> :kbps + end + + def to_int(:pktps, :meter_flags) do + meter_flags_to_int(:pktps) + catch + _class, _reason -> :pktps + end + + def to_int(:burst, :meter_flags) do + meter_flags_to_int(:burst) + catch + _class, _reason -> :burst + end + + def to_int(:stats, :meter_flags) do + meter_flags_to_int(:stats) + catch + _class, _reason -> :stats + end + + def to_int(_int, :meter_flags) do + throw(:bad_enum) + end + + def to_int(Openflow.MeterBand.Drop, :meter_band_type) do + meter_band_type_to_int(Openflow.MeterBand.Drop) + catch + _class, _reason -> Openflow.MeterBand.Drop + end + + def to_int(Openflow.MeterBand.Remark, :meter_band_type) do + meter_band_type_to_int(Openflow.MeterBand.Remark) + catch + _class, _reason -> Openflow.MeterBand.Remark + end + + def to_int(Openflow.MeterBand.Experimenter, :meter_band_type) do + meter_band_type_to_int(Openflow.MeterBand.Experimenter) + catch + _class, _reason -> Openflow.MeterBand.Experimenter + end + + def to_int(_int, :meter_band_type) do + throw(:bad_enum) + end + + def to_int(:table_miss_controller, :table_config) do + table_config_to_int(:table_miss_controller) + catch + _class, _reason -> :table_miss_controller + end + + def to_int(:table_miss_continue, :table_config) do + table_config_to_int(:table_miss_continue) + catch + _class, _reason -> :table_miss_continue + end + + def to_int(:table_miss_drop, :table_config) do + table_config_to_int(:table_miss_drop) + catch + _class, _reason -> :table_miss_drop + end + + def to_int(:table_miss_mask, :table_config) do + table_config_to_int(:table_miss_mask) + catch + _class, _reason -> :table_miss_mask + end + + def to_int(:eviction, :table_config) do + table_config_to_int(:eviction) + catch + _class, _reason -> :eviction + end + + def to_int(:vacancy_events, :table_config) do + table_config_to_int(:vacancy_events) + catch + _class, _reason -> :vacancy_events + end + + def to_int(_int, :table_config) do + throw(:bad_enum) + end + + def to_int(Openflow.Action.Output, :action_type) do + action_type_to_int(Openflow.Action.Output) + catch + _class, _reason -> Openflow.Action.Output + end + + def to_int(Openflow.Action.CopyTtlOut, :action_type) do + action_type_to_int(Openflow.Action.CopyTtlOut) + catch + _class, _reason -> Openflow.Action.CopyTtlOut + end + + def to_int(Openflow.Action.CopyTtlIn, :action_type) do + action_type_to_int(Openflow.Action.CopyTtlIn) + catch + _class, _reason -> Openflow.Action.CopyTtlIn + end + + def to_int(Openflow.Action.SetMplsTtl, :action_type) do + action_type_to_int(Openflow.Action.SetMplsTtl) + catch + _class, _reason -> Openflow.Action.SetMplsTtl + end + + def to_int(Openflow.Action.DecMplsTtl, :action_type) do + action_type_to_int(Openflow.Action.DecMplsTtl) + catch + _class, _reason -> Openflow.Action.DecMplsTtl + end + + def to_int(Openflow.Action.PushVlan, :action_type) do + action_type_to_int(Openflow.Action.PushVlan) + catch + _class, _reason -> Openflow.Action.PushVlan + end + + def to_int(Openflow.Action.PopVlan, :action_type) do + action_type_to_int(Openflow.Action.PopVlan) + catch + _class, _reason -> Openflow.Action.PopVlan + end + + def to_int(Openflow.Action.PushMpls, :action_type) do + action_type_to_int(Openflow.Action.PushMpls) + catch + _class, _reason -> Openflow.Action.PushMpls + end + + def to_int(Openflow.Action.PopMpls, :action_type) do + action_type_to_int(Openflow.Action.PopMpls) + catch + _class, _reason -> Openflow.Action.PopMpls + end + + def to_int(Openflow.Action.SetQueue, :action_type) do + action_type_to_int(Openflow.Action.SetQueue) + catch + _class, _reason -> Openflow.Action.SetQueue + end + + def to_int(Openflow.Action.Group, :action_type) do + action_type_to_int(Openflow.Action.Group) + catch + _class, _reason -> Openflow.Action.Group + end + + def to_int(Openflow.Action.SetNwTtl, :action_type) do + action_type_to_int(Openflow.Action.SetNwTtl) + catch + _class, _reason -> Openflow.Action.SetNwTtl + end + + def to_int(Openflow.Action.DecNwTtl, :action_type) do + action_type_to_int(Openflow.Action.DecNwTtl) + catch + _class, _reason -> Openflow.Action.DecNwTtl + end + + def to_int(Openflow.Action.SetField, :action_type) do + action_type_to_int(Openflow.Action.SetField) + catch + _class, _reason -> Openflow.Action.SetField + end + + def to_int(Openflow.Action.PushPbb, :action_type) do + action_type_to_int(Openflow.Action.PushPbb) + catch + _class, _reason -> Openflow.Action.PushPbb + end + + def to_int(Openflow.Action.PopPbb, :action_type) do + action_type_to_int(Openflow.Action.PopPbb) + catch + _class, _reason -> Openflow.Action.PopPbb + end + + def to_int(Openflow.Action.Encap, :action_type) do + action_type_to_int(Openflow.Action.Encap) + catch + _class, _reason -> Openflow.Action.Encap + end + + def to_int(Openflow.Action.Decap, :action_type) do + action_type_to_int(Openflow.Action.Decap) + catch + _class, _reason -> Openflow.Action.Decap + end + + def to_int(Openflow.Action.SetSequence, :action_type) do + action_type_to_int(Openflow.Action.SetSequence) + catch + _class, _reason -> Openflow.Action.SetSequence + end + + def to_int(Openflow.Action.ValidateSequence, :action_type) do + action_type_to_int(Openflow.Action.ValidateSequence) + catch + _class, _reason -> Openflow.Action.ValidateSequence + end + + def to_int(Openflow.Action.Experimenter, :action_type) do + action_type_to_int(Openflow.Action.Experimenter) + catch + _class, _reason -> Openflow.Action.Experimenter + end + + def to_int(_int, :action_type) do + throw(:bad_enum) + end + + def to_int(:nicira_ext_action, :action_vendor) do + action_vendor_to_int(:nicira_ext_action) + catch + _class, _reason -> :nicira_ext_action + end + + def to_int(:onf_ext_action, :action_vendor) do + action_vendor_to_int(:onf_ext_action) + catch + _class, _reason -> :onf_ext_action + end + + def to_int(_int, :action_vendor) do + throw(:bad_enum) + end + + def to_int(Openflow.Action.OnfCopyField, :onf_ext_action) do + onf_ext_action_to_int(Openflow.Action.OnfCopyField) + catch + _class, _reason -> Openflow.Action.OnfCopyField + end + + def to_int(_int, :onf_ext_action) do + throw(:bad_enum) + end + + def to_int(Openflow.Action.NxResubmit, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxResubmit) + catch + _class, _reason -> Openflow.Action.NxResubmit + end + + def to_int(Openflow.Action.NxSetTunnel, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSetTunnel) + catch + _class, _reason -> Openflow.Action.NxSetTunnel + end + + def to_int(Openflow.Action.NxSetQueue, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSetQueue) + catch + _class, _reason -> Openflow.Action.NxSetQueue + end + + def to_int(Openflow.Action.NxPopQueue, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxPopQueue) + catch + _class, _reason -> Openflow.Action.NxPopQueue + end + + def to_int(Openflow.Action.NxRegMove, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxRegMove) + catch + _class, _reason -> Openflow.Action.NxRegMove + end + + def to_int(Openflow.Action.NxRegLoad, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxRegLoad) + catch + _class, _reason -> Openflow.Action.NxRegLoad + end + + def to_int(Openflow.Action.NxNote, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxNote) + catch + _class, _reason -> Openflow.Action.NxNote + end + + def to_int(Openflow.Action.NxSetTunnel64, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSetTunnel64) + catch + _class, _reason -> Openflow.Action.NxSetTunnel64 + end + + def to_int(Openflow.Action.NxMultipath, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxMultipath) + catch + _class, _reason -> Openflow.Action.NxMultipath + end + + def to_int(Openflow.Action.NxBundle, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxBundle) + catch + _class, _reason -> Openflow.Action.NxBundle + end + + def to_int(Openflow.Action.NxBundleLoad, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxBundleLoad) + catch + _class, _reason -> Openflow.Action.NxBundleLoad + end + + def to_int(Openflow.Action.NxResubmitTable, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxResubmitTable) + catch + _class, _reason -> Openflow.Action.NxResubmitTable + end + + def to_int(Openflow.Action.NxOutputReg, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxOutputReg) + catch + _class, _reason -> Openflow.Action.NxOutputReg + end + + def to_int(Openflow.Action.NxLearn, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxLearn) + catch + _class, _reason -> Openflow.Action.NxLearn + end + + def to_int(Openflow.Action.NxExit, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxExit) + catch + _class, _reason -> Openflow.Action.NxExit + end + + def to_int(Openflow.Action.NxDecTtl, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxDecTtl) + catch + _class, _reason -> Openflow.Action.NxDecTtl + end + + def to_int(Openflow.Action.NxFinTimeout, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxFinTimeout) + catch + _class, _reason -> Openflow.Action.NxFinTimeout + end + + def to_int(Openflow.Action.NxController, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxController) + catch + _class, _reason -> Openflow.Action.NxController + end + + def to_int(Openflow.Action.NxDecTtlCntIds, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxDecTtlCntIds) + catch + _class, _reason -> Openflow.Action.NxDecTtlCntIds + end + + def to_int(Openflow.Action.NxWriteMetadata, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxWriteMetadata) + catch + _class, _reason -> Openflow.Action.NxWriteMetadata + end + + def to_int(Openflow.Action.NxPushMpls, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxPushMpls) + catch + _class, _reason -> Openflow.Action.NxPushMpls + end + + def to_int(Openflow.Action.NxPopMpls, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxPopMpls) + catch + _class, _reason -> Openflow.Action.NxPopMpls + end + + def to_int(Openflow.Action.NxSetMplsTtl, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSetMplsTtl) + catch + _class, _reason -> Openflow.Action.NxSetMplsTtl + end + + def to_int(Openflow.Action.NxDecMplsTtl, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxDecMplsTtl) + catch + _class, _reason -> Openflow.Action.NxDecMplsTtl + end + + def to_int(Openflow.Action.NxStackPush, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxStackPush) + catch + _class, _reason -> Openflow.Action.NxStackPush + end + + def to_int(Openflow.Action.NxStackPop, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxStackPop) + catch + _class, _reason -> Openflow.Action.NxStackPop + end + + def to_int(Openflow.Action.NxSample, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSample) + catch + _class, _reason -> Openflow.Action.NxSample + end + + def to_int(Openflow.Action.NxSetMplsLabel, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSetMplsLabel) + catch + _class, _reason -> Openflow.Action.NxSetMplsLabel + end + + def to_int(Openflow.Action.NxSetMplsTc, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSetMplsTc) + catch + _class, _reason -> Openflow.Action.NxSetMplsTc + end + + def to_int(Openflow.Action.NxOutputReg2, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxOutputReg2) + catch + _class, _reason -> Openflow.Action.NxOutputReg2 + end + + def to_int(Openflow.Action.NxRegLoad2, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxRegLoad2) + catch + _class, _reason -> Openflow.Action.NxRegLoad2 + end + + def to_int(Openflow.Action.NxConjunction, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxConjunction) + catch + _class, _reason -> Openflow.Action.NxConjunction + end + + def to_int(Openflow.Action.NxConntrack, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxConntrack) + catch + _class, _reason -> Openflow.Action.NxConntrack + end + + def to_int(Openflow.Action.NxNat, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxNat) + catch + _class, _reason -> Openflow.Action.NxNat + end + + def to_int(Openflow.Action.NxController2, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxController2) + catch + _class, _reason -> Openflow.Action.NxController2 + end + + def to_int(Openflow.Action.NxSample2, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSample2) + catch + _class, _reason -> Openflow.Action.NxSample2 + end + + def to_int(Openflow.Action.NxOutputTrunc, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxOutputTrunc) + catch + _class, _reason -> Openflow.Action.NxOutputTrunc + end + + def to_int(Openflow.Action.NxGroup, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxGroup) + catch + _class, _reason -> Openflow.Action.NxGroup + end + + def to_int(Openflow.Action.NxSample3, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxSample3) + catch + _class, _reason -> Openflow.Action.NxSample3 + end + + def to_int(Openflow.Action.NxClone, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxClone) + catch + _class, _reason -> Openflow.Action.NxClone + end + + def to_int(Openflow.Action.NxCtClear, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxCtClear) + catch + _class, _reason -> Openflow.Action.NxCtClear + end + + def to_int(Openflow.Action.NxResubmitTableCt, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxResubmitTableCt) + catch + _class, _reason -> Openflow.Action.NxResubmitTableCt + end + + def to_int(Openflow.Action.NxLearn2, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxLearn2) + catch + _class, _reason -> Openflow.Action.NxLearn2 + end + + def to_int(Openflow.Action.NxEncap, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxEncap) + catch + _class, _reason -> Openflow.Action.NxEncap + end + + def to_int(Openflow.Action.NxDecap, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxDecap) + catch + _class, _reason -> Openflow.Action.NxDecap + end + + def to_int(Openflow.Action.NxDebugRecirc, :nicira_ext_action) do + nicira_ext_action_to_int(Openflow.Action.NxDebugRecirc) + catch + _class, _reason -> Openflow.Action.NxDebugRecirc + end + + def to_int(_int, :nicira_ext_action) do + throw(:bad_enum) + end + + def to_int(:modulo_n, :nx_mp_algorithm) do + nx_mp_algorithm_to_int(:modulo_n) + catch + _class, _reason -> :modulo_n + end + + def to_int(:hash_threshold, :nx_mp_algorithm) do + nx_mp_algorithm_to_int(:hash_threshold) + catch + _class, _reason -> :hash_threshold + end + + def to_int(:highest_random_weight, :nx_mp_algorithm) do + nx_mp_algorithm_to_int(:highest_random_weight) + catch + _class, _reason -> :highest_random_weight + end + + def to_int(:iterative_hash, :nx_mp_algorithm) do + nx_mp_algorithm_to_int(:iterative_hash) + catch + _class, _reason -> :iterative_hash + end + + def to_int(_int, :nx_mp_algorithm) do + throw(:bad_enum) + end + + def to_int(:eth_src, :nx_hash_fields) do + nx_hash_fields_to_int(:eth_src) + catch + _class, _reason -> :eth_src + end + + def to_int(:symmetric_l4, :nx_hash_fields) do + nx_hash_fields_to_int(:symmetric_l4) + catch + _class, _reason -> :symmetric_l4 + end + + def to_int(:symmetric_l3l4, :nx_hash_fields) do + nx_hash_fields_to_int(:symmetric_l3l4) + catch + _class, _reason -> :symmetric_l3l4 + end + + def to_int(:symmetric_l3l4_udp, :nx_hash_fields) do + nx_hash_fields_to_int(:symmetric_l3l4_udp) + catch + _class, _reason -> :symmetric_l3l4_udp + end + + def to_int(:nw_src, :nx_hash_fields) do + nx_hash_fields_to_int(:nw_src) + catch + _class, _reason -> :nw_src + end + + def to_int(:nw_dst, :nx_hash_fields) do + nx_hash_fields_to_int(:nw_dst) + catch + _class, _reason -> :nw_dst + end + + def to_int(_int, :nx_hash_fields) do + throw(:bad_enum) + end + + def to_int(:active_backup, :nx_bd_algorithm) do + nx_bd_algorithm_to_int(:active_backup) + catch + _class, _reason -> :active_backup + end + + def to_int(:highest_random_weight, :nx_bd_algorithm) do + nx_bd_algorithm_to_int(:highest_random_weight) + catch + _class, _reason -> :highest_random_weight + end + + def to_int(_int, :nx_bd_algorithm) do + throw(:bad_enum) + end + + def to_int(:send_flow_rem, :nx_learn_flag) do + nx_learn_flag_to_int(:send_flow_rem) + catch + _class, _reason -> :send_flow_rem + end + + def to_int(:delete_learned, :nx_learn_flag) do + nx_learn_flag_to_int(:delete_learned) + catch + _class, _reason -> :delete_learned + end + + def to_int(:write_result, :nx_learn_flag) do + nx_learn_flag_to_int(:write_result) + catch + _class, _reason -> :write_result + end + + def to_int(_int, :nx_learn_flag) do + throw(:bad_enum) + end + + def to_int(:commit, :nx_conntrack_flags) do + nx_conntrack_flags_to_int(:commit) + catch + _class, _reason -> :commit + end + + def to_int(:force, :nx_conntrack_flags) do + nx_conntrack_flags_to_int(:force) + catch + _class, _reason -> :force + end + + def to_int(_int, :nx_conntrack_flags) do + throw(:bad_enum) + end + + def to_int(:src, :nx_nat_flags) do + nx_nat_flags_to_int(:src) + catch + _class, _reason -> :src + end + + def to_int(:dst, :nx_nat_flags) do + nx_nat_flags_to_int(:dst) + catch + _class, _reason -> :dst + end + + def to_int(:persistent, :nx_nat_flags) do + nx_nat_flags_to_int(:persistent) + catch + _class, _reason -> :persistent + end + + def to_int(:protocol_hash, :nx_nat_flags) do + nx_nat_flags_to_int(:protocol_hash) + catch + _class, _reason -> :protocol_hash + end + + def to_int(:protocol_random, :nx_nat_flags) do + nx_nat_flags_to_int(:protocol_random) + catch + _class, _reason -> :protocol_random + end + + def to_int(_int, :nx_nat_flags) do + throw(:bad_enum) + end + + def to_int(:ipv4_min, :nx_nat_range) do + nx_nat_range_to_int(:ipv4_min) + catch + _class, _reason -> :ipv4_min + end + + def to_int(:ipv4_max, :nx_nat_range) do + nx_nat_range_to_int(:ipv4_max) + catch + _class, _reason -> :ipv4_max + end + + def to_int(:ipv6_min, :nx_nat_range) do + nx_nat_range_to_int(:ipv6_min) + catch + _class, _reason -> :ipv6_min + end + + def to_int(:ipv6_max, :nx_nat_range) do + nx_nat_range_to_int(:ipv6_max) + catch + _class, _reason -> :ipv6_max + end + + def to_int(:proto_min, :nx_nat_range) do + nx_nat_range_to_int(:proto_min) + catch + _class, _reason -> :proto_min + end + + def to_int(:proto_max, :nx_nat_range) do + nx_nat_range_to_int(:proto_max) + catch + _class, _reason -> :proto_max + end + + def to_int(_int, :nx_nat_range) do + throw(:bad_enum) + end + + def to_int(:max_len, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_int(:max_len) + catch + _class, _reason -> :max_len + end + + def to_int(:controller_id, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_int(:controller_id) + catch + _class, _reason -> :controller_id + end + + def to_int(:reason, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_int(:reason) + catch + _class, _reason -> :reason + end + + def to_int(:userdata, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_int(:userdata) + catch + _class, _reason -> :userdata + end + + def to_int(:pause, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_int(:pause) + catch + _class, _reason -> :pause + end + + def to_int(_int, :nx_action_controller2_prop_type) do + throw(:bad_enum) + end + + def to_int(:default, :nx_action_sample_direction) do + nx_action_sample_direction_to_int(:default) + catch + _class, _reason -> :default + end + + def to_int(:ingress, :nx_action_sample_direction) do + nx_action_sample_direction_to_int(:ingress) + catch + _class, _reason -> :ingress + end + + def to_int(:egress, :nx_action_sample_direction) do + nx_action_sample_direction_to_int(:egress) + catch + _class, _reason -> :egress + end + + def to_int(_int, :nx_action_sample_direction) do + throw(:bad_enum) + end + + def to_int(Openflow.Action.NxFlowSpecMatch, :nx_flow_spec_type) do + nx_flow_spec_type_to_int(Openflow.Action.NxFlowSpecMatch) + catch + _class, _reason -> Openflow.Action.NxFlowSpecMatch + end + + def to_int(Openflow.Action.NxFlowSpecLoad, :nx_flow_spec_type) do + nx_flow_spec_type_to_int(Openflow.Action.NxFlowSpecLoad) + catch + _class, _reason -> Openflow.Action.NxFlowSpecLoad + end + + def to_int(Openflow.Action.NxFlowSpecOutput, :nx_flow_spec_type) do + nx_flow_spec_type_to_int(Openflow.Action.NxFlowSpecOutput) + catch + _class, _reason -> Openflow.Action.NxFlowSpecOutput + end + + def to_int(_int, :nx_flow_spec_type) do + throw(:bad_enum) + end + + def to_int(Openflow.Instruction.GotoTable, :instruction_type) do + instruction_type_to_int(Openflow.Instruction.GotoTable) + catch + _class, _reason -> Openflow.Instruction.GotoTable + end + + def to_int(Openflow.Instruction.WriteMetadata, :instruction_type) do + instruction_type_to_int(Openflow.Instruction.WriteMetadata) + catch + _class, _reason -> Openflow.Instruction.WriteMetadata + end + + def to_int(Openflow.Instruction.WriteActions, :instruction_type) do + instruction_type_to_int(Openflow.Instruction.WriteActions) + catch + _class, _reason -> Openflow.Instruction.WriteActions + end + + def to_int(Openflow.Instruction.ApplyActions, :instruction_type) do + instruction_type_to_int(Openflow.Instruction.ApplyActions) + catch + _class, _reason -> Openflow.Instruction.ApplyActions + end + + def to_int(Openflow.Instruction.ClearActions, :instruction_type) do + instruction_type_to_int(Openflow.Instruction.ClearActions) + catch + _class, _reason -> Openflow.Instruction.ClearActions + end + + def to_int(Openflow.Instruction.Meter, :instruction_type) do + instruction_type_to_int(Openflow.Instruction.Meter) + catch + _class, _reason -> Openflow.Instruction.Meter + end + + def to_int(Openflow.Instruction.Experimenter, :instruction_type) do + instruction_type_to_int(Openflow.Instruction.Experimenter) + catch + _class, _reason -> Openflow.Instruction.Experimenter + end + + def to_int(_int, :instruction_type) do + throw(:bad_enum) + end + + def to_int(:nochange, :controller_role) do + controller_role_to_int(:nochange) + catch + _class, _reason -> :nochange + end + + def to_int(:equal, :controller_role) do + controller_role_to_int(:equal) + catch + _class, _reason -> :equal + end + + def to_int(:master, :controller_role) do + controller_role_to_int(:master) + catch + _class, _reason -> :master + end + + def to_int(:slave, :controller_role) do + controller_role_to_int(:slave) + catch + _class, _reason -> :slave + end + + def to_int(_int, :controller_role) do + throw(:bad_enum) + end + + def to_int(:other, :nx_role) do + nx_role_to_int(:other) + catch + _class, _reason -> :other + end + + def to_int(:master, :nx_role) do + nx_role_to_int(:master) + catch + _class, _reason -> :master + end + + def to_int(:slave, :nx_role) do + nx_role_to_int(:slave) + catch + _class, _reason -> :slave + end + + def to_int(_int, :nx_role) do + throw(:bad_enum) + end + + def to_int(:standard, :packet_in_format) do + packet_in_format_to_int(:standard) + catch + _class, _reason -> :standard + end + + def to_int(:nxt_packet_in, :packet_in_format) do + packet_in_format_to_int(:nxt_packet_in) + catch + _class, _reason -> :nxt_packet_in + end + + def to_int(:nxt_packet_in2, :packet_in_format) do + packet_in_format_to_int(:nxt_packet_in2) + catch + _class, _reason -> :nxt_packet_in2 + end + + def to_int(_int, :packet_in_format) do + throw(:bad_enum) + end + + def to_int(:openflow10, :flow_format) do + flow_format_to_int(:openflow10) + catch + _class, _reason -> :openflow10 + end + + def to_int(:nxm, :flow_format) do + flow_format_to_int(:nxm) + catch + _class, _reason -> :nxm + end + + def to_int(_int, :flow_format) do + throw(:bad_enum) + end + + def to_int(:packet, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:packet) + catch + _class, _reason -> :packet + end + + def to_int(:full_len, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:full_len) + catch + _class, _reason -> :full_len + end + + def to_int(:buffer_id, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:buffer_id) + catch + _class, _reason -> :buffer_id + end + + def to_int(:table_id, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:table_id) + catch + _class, _reason -> :table_id + end + + def to_int(:cookie, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:cookie) + catch + _class, _reason -> :cookie + end + + def to_int(:reason, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:reason) + catch + _class, _reason -> :reason + end + + def to_int(:metadata, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:metadata) + catch + _class, _reason -> :metadata + end + + def to_int(:userdata, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:userdata) + catch + _class, _reason -> :userdata + end + + def to_int(:continuation, :packet_in2_prop_type) do + packet_in2_prop_type_to_int(:continuation) + catch + _class, _reason -> :continuation + end + + def to_int(_int, :packet_in2_prop_type) do + throw(:bad_enum) + end + + def to_int(:bridge, :continuation_prop_type) do + continuation_prop_type_to_int(:bridge) + catch + _class, _reason -> :bridge + end + + def to_int(:stack, :continuation_prop_type) do + continuation_prop_type_to_int(:stack) + catch + _class, _reason -> :stack + end + + def to_int(:mirrors, :continuation_prop_type) do + continuation_prop_type_to_int(:mirrors) + catch + _class, _reason -> :mirrors + end + + def to_int(:conntracked, :continuation_prop_type) do + continuation_prop_type_to_int(:conntracked) + catch + _class, _reason -> :conntracked + end + + def to_int(:table_id, :continuation_prop_type) do + continuation_prop_type_to_int(:table_id) + catch + _class, _reason -> :table_id + end + + def to_int(:cookie, :continuation_prop_type) do + continuation_prop_type_to_int(:cookie) + catch + _class, _reason -> :cookie + end + + def to_int(:actions, :continuation_prop_type) do + continuation_prop_type_to_int(:actions) + catch + _class, _reason -> :actions + end + + def to_int(:action_set, :continuation_prop_type) do + continuation_prop_type_to_int(:action_set) + catch + _class, _reason -> :action_set + end + + def to_int(_int, :continuation_prop_type) do + throw(:bad_enum) + end + + def to_int(:initial, :flow_monitor_flag) do + flow_monitor_flag_to_int(:initial) + catch + _class, _reason -> :initial + end + + def to_int(:add, :flow_monitor_flag) do + flow_monitor_flag_to_int(:add) + catch + _class, _reason -> :add + end + + def to_int(:delete, :flow_monitor_flag) do + flow_monitor_flag_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(:modify, :flow_monitor_flag) do + flow_monitor_flag_to_int(:modify) + catch + _class, _reason -> :modify + end + + def to_int(:actions, :flow_monitor_flag) do + flow_monitor_flag_to_int(:actions) + catch + _class, _reason -> :actions + end + + def to_int(:own, :flow_monitor_flag) do + flow_monitor_flag_to_int(:own) + catch + _class, _reason -> :own + end + + def to_int(_int, :flow_monitor_flag) do + throw(:bad_enum) + end + + def to_int(:added, :flow_update_event) do + flow_update_event_to_int(:added) + catch + _class, _reason -> :added + end + + def to_int(:deleted, :flow_update_event) do + flow_update_event_to_int(:deleted) + catch + _class, _reason -> :deleted + end + + def to_int(:modified, :flow_update_event) do + flow_update_event_to_int(:modified) + catch + _class, _reason -> :modified + end + + def to_int(:abbrev, :flow_update_event) do + flow_update_event_to_int(:abbrev) + catch + _class, _reason -> :abbrev + end + + def to_int(_int, :flow_update_event) do + throw(:bad_enum) + end + + def to_int(:add, :tlv_table_mod_command) do + tlv_table_mod_command_to_int(:add) + catch + _class, _reason -> :add + end + + def to_int(:delete, :tlv_table_mod_command) do + tlv_table_mod_command_to_int(:delete) + catch + _class, _reason -> :delete + end + + def to_int(:clear, :tlv_table_mod_command) do + tlv_table_mod_command_to_int(:clear) + catch + _class, _reason -> :clear + end + + def to_int(_int, :tlv_table_mod_command) do + throw(:bad_enum) + end + + def to_int(:instructions, :table_feature_prop_type) do + table_feature_prop_type_to_int(:instructions) + catch + _class, _reason -> :instructions + end + + def to_int(:instructions_miss, :table_feature_prop_type) do + table_feature_prop_type_to_int(:instructions_miss) + catch + _class, _reason -> :instructions_miss + end + + def to_int(:next_tables, :table_feature_prop_type) do + table_feature_prop_type_to_int(:next_tables) + catch + _class, _reason -> :next_tables + end + + def to_int(:next_tables_miss, :table_feature_prop_type) do + table_feature_prop_type_to_int(:next_tables_miss) + catch + _class, _reason -> :next_tables_miss + end + + def to_int(:write_actions, :table_feature_prop_type) do + table_feature_prop_type_to_int(:write_actions) + catch + _class, _reason -> :write_actions + end + + def to_int(:write_actions_miss, :table_feature_prop_type) do + table_feature_prop_type_to_int(:write_actions_miss) + catch + _class, _reason -> :write_actions_miss + end + + def to_int(:apply_actions, :table_feature_prop_type) do + table_feature_prop_type_to_int(:apply_actions) + catch + _class, _reason -> :apply_actions + end + + def to_int(:apply_actions_miss, :table_feature_prop_type) do + table_feature_prop_type_to_int(:apply_actions_miss) + catch + _class, _reason -> :apply_actions_miss + end + + def to_int(:match, :table_feature_prop_type) do + table_feature_prop_type_to_int(:match) + catch + _class, _reason -> :match + end + + def to_int(:wildcards, :table_feature_prop_type) do + table_feature_prop_type_to_int(:wildcards) + catch + _class, _reason -> :wildcards + end + + def to_int(:write_setfield, :table_feature_prop_type) do + table_feature_prop_type_to_int(:write_setfield) + catch + _class, _reason -> :write_setfield + end + + def to_int(:write_setfield_miss, :table_feature_prop_type) do + table_feature_prop_type_to_int(:write_setfield_miss) + catch + _class, _reason -> :write_setfield_miss + end + + def to_int(:apply_setfield, :table_feature_prop_type) do + table_feature_prop_type_to_int(:apply_setfield) + catch + _class, _reason -> :apply_setfield + end + + def to_int(:apply_setfield_miss, :table_feature_prop_type) do + table_feature_prop_type_to_int(:apply_setfield_miss) + catch + _class, _reason -> :apply_setfield_miss + end + + def to_int(:experimenter, :table_feature_prop_type) do + table_feature_prop_type_to_int(:experimenter) + catch + _class, _reason -> :experimenter + end + + def to_int(:experimenter_miss, :table_feature_prop_type) do + table_feature_prop_type_to_int(:experimenter_miss) + catch + _class, _reason -> :experimenter_miss + end + + def to_int(_int, :table_feature_prop_type) do + throw(:bad_enum) + end + + def to_atom(0x0, :openflow_codec) do + openflow_codec_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :openflow_codec) do + openflow_codec_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :openflow_codec) do + openflow_codec_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :openflow_codec) do + openflow_codec_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :openflow_codec) do + openflow_codec_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :openflow_codec) do + openflow_codec_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :openflow_codec) do + openflow_codec_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :openflow_codec) do + openflow_codec_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :openflow_codec) do + openflow_codec_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :openflow_codec) do + openflow_codec_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :openflow_codec) do + openflow_codec_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :openflow_codec) do + openflow_codec_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :openflow_codec) do + openflow_codec_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :openflow_codec) do + openflow_codec_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xE, :openflow_codec) do + openflow_codec_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(0xF, :openflow_codec) do + openflow_codec_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0x10, :openflow_codec) do + openflow_codec_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x11, :openflow_codec) do + openflow_codec_to_atom(0x11) + catch + _class, _reason -> 17 + end + + def to_atom(0x12, :openflow_codec) do + openflow_codec_to_atom(0x12) + catch + _class, _reason -> 18 + end + + def to_atom(0x13, :openflow_codec) do + openflow_codec_to_atom(0x13) + catch + _class, _reason -> 19 + end + + def to_atom(0x14, :openflow_codec) do + openflow_codec_to_atom(0x14) + catch + _class, _reason -> 20 + end + + def to_atom(0x15, :openflow_codec) do + openflow_codec_to_atom(0x15) + catch + _class, _reason -> 21 + end + + def to_atom(0x18, :openflow_codec) do + openflow_codec_to_atom(0x18) + catch + _class, _reason -> 24 + end + + def to_atom(0x19, :openflow_codec) do + openflow_codec_to_atom(0x19) + catch + _class, _reason -> 25 + end + + def to_atom(0x1A, :openflow_codec) do + openflow_codec_to_atom(0x1A) + catch + _class, _reason -> 26 + end + + def to_atom(0x1B, :openflow_codec) do + openflow_codec_to_atom(0x1B) + catch + _class, _reason -> 27 + end + + def to_atom(0x1C, :openflow_codec) do + openflow_codec_to_atom(0x1C) + catch + _class, _reason -> 28 + end + + def to_atom(0x1D, :openflow_codec) do + openflow_codec_to_atom(0x1D) + catch + _class, _reason -> 29 + end + + def to_atom(_, :openflow_codec) do + throw(:bad_enum) + end + + def to_atom(0x2320, :experimenter_id) do + experimenter_id_to_atom(0x2320) + catch + _class, _reason -> 8992 + end + + def to_atom(0x4F4E4600, :experimenter_id) do + experimenter_id_to_atom(0x4F4E4600) + catch + _class, _reason -> 1_330_529_792 + end + + def to_atom(_, :experimenter_id) do + throw(:bad_enum) + end + + def to_atom(0x10, :nicira_ext_message) do + nicira_ext_message_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x14, :nicira_ext_message) do + nicira_ext_message_to_atom(0x14) + catch + _class, _reason -> 20 + end + + def to_atom(0x15, :nicira_ext_message) do + nicira_ext_message_to_atom(0x15) + catch + _class, _reason -> 21 + end + + def to_atom(0x16, :nicira_ext_message) do + nicira_ext_message_to_atom(0x16) + catch + _class, _reason -> 22 + end + + def to_atom(0x17, :nicira_ext_message) do + nicira_ext_message_to_atom(0x17) + catch + _class, _reason -> 23 + end + + def to_atom(0x18, :nicira_ext_message) do + nicira_ext_message_to_atom(0x18) + catch + _class, _reason -> 24 + end + + def to_atom(0x19, :nicira_ext_message) do + nicira_ext_message_to_atom(0x19) + catch + _class, _reason -> 25 + end + + def to_atom(0x1A, :nicira_ext_message) do + nicira_ext_message_to_atom(0x1A) + catch + _class, _reason -> 26 + end + + def to_atom(0x1B, :nicira_ext_message) do + nicira_ext_message_to_atom(0x1B) + catch + _class, _reason -> 27 + end + + def to_atom(0x1C, :nicira_ext_message) do + nicira_ext_message_to_atom(0x1C) + catch + _class, _reason -> 28 + end + + def to_atom(0x1D, :nicira_ext_message) do + nicira_ext_message_to_atom(0x1D) + catch + _class, _reason -> 29 + end + + def to_atom(0x1E, :nicira_ext_message) do + nicira_ext_message_to_atom(0x1E) + catch + _class, _reason -> 30 + end + + def to_atom(_, :nicira_ext_message) do + throw(:bad_enum) + end + + def to_atom(0x8FC, :onf_ext_message) do + onf_ext_message_to_atom(0x8FC) + catch + _class, _reason -> 2300 + end + + def to_atom(0x8FD, :onf_ext_message) do + onf_ext_message_to_atom(0x8FD) + catch + _class, _reason -> 2301 + end + + def to_atom(_, :onf_ext_message) do + throw(:bad_enum) + end + + def to_atom(0x1, :multipart_request_flags) do + multipart_request_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(_, :multipart_request_flags) do + throw(:bad_enum) + end + + def to_atom(0x1, :multipart_reply_flags) do + multipart_reply_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(_, :multipart_reply_flags) do + throw(:bad_enum) + end + + def to_atom(0x0, :multipart_request_codec) do + multipart_request_codec_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :multipart_request_codec) do + multipart_request_codec_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :multipart_request_codec) do + multipart_request_codec_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :multipart_request_codec) do + multipart_request_codec_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :multipart_request_codec) do + multipart_request_codec_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :multipart_request_codec) do + multipart_request_codec_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :multipart_request_codec) do + multipart_request_codec_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :multipart_request_codec) do + multipart_request_codec_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :multipart_request_codec) do + multipart_request_codec_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :multipart_request_codec) do + multipart_request_codec_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :multipart_request_codec) do + multipart_request_codec_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :multipart_request_codec) do + multipart_request_codec_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :multipart_request_codec) do + multipart_request_codec_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :multipart_request_codec) do + multipart_request_codec_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xFFFF, :multipart_request_codec) do + multipart_request_codec_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :multipart_request_codec) do + throw(:bad_enum) + end + + def to_atom(0x0, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xFFFF, :multipart_reply_codec) do + multipart_reply_codec_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :multipart_reply_codec) do + throw(:bad_enum) + end + + def to_atom(0x0, :nicira_ext_stats) do + nicira_ext_stats_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nicira_ext_stats) do + nicira_ext_stats_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nicira_ext_stats) do + nicira_ext_stats_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :nicira_ext_stats) do + nicira_ext_stats_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :nicira_ext_stats) do + nicira_ext_stats_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(_, :nicira_ext_stats) do + throw(:bad_enum) + end + + def to_atom(0x1, :hello_elem) do + hello_elem_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(_, :hello_elem) do + throw(:bad_enum) + end + + def to_atom(0x0, :error_type) do + error_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :error_type) do + error_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :error_type) do + error_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :error_type) do + error_type_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :error_type) do + error_type_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :error_type) do + error_type_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :error_type) do + error_type_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :error_type) do + error_type_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :error_type) do + error_type_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :error_type) do + error_type_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :error_type) do + error_type_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :error_type) do + error_type_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :error_type) do + error_type_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :error_type) do + error_type_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xFFFF, :error_type) do + error_type_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :error_type) do + throw(:bad_enum) + end + + def to_atom(0x0, :hello_failed) do + hello_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :hello_failed) do + hello_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(_, :hello_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :bad_request) do + bad_request_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :bad_request) do + bad_request_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :bad_request) do + bad_request_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :bad_request) do + bad_request_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :bad_request) do + bad_request_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :bad_request) do + bad_request_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :bad_request) do + bad_request_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :bad_request) do + bad_request_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :bad_request) do + bad_request_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :bad_request) do + bad_request_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :bad_request) do + bad_request_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :bad_request) do + bad_request_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :bad_request) do + bad_request_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :bad_request) do + bad_request_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(_, :bad_request) do + throw(:bad_enum) + end + + def to_atom(0x0, :bad_action) do + bad_action_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :bad_action) do + bad_action_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :bad_action) do + bad_action_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :bad_action) do + bad_action_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :bad_action) do + bad_action_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :bad_action) do + bad_action_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :bad_action) do + bad_action_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :bad_action) do + bad_action_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :bad_action) do + bad_action_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :bad_action) do + bad_action_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :bad_action) do + bad_action_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :bad_action) do + bad_action_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :bad_action) do + bad_action_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :bad_action) do + bad_action_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xE, :bad_action) do + bad_action_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(0xF, :bad_action) do + bad_action_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(_, :bad_action) do + throw(:bad_enum) + end + + def to_atom(0x0, :bad_instruction) do + bad_instruction_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :bad_instruction) do + bad_instruction_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :bad_instruction) do + bad_instruction_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :bad_instruction) do + bad_instruction_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :bad_instruction) do + bad_instruction_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :bad_instruction) do + bad_instruction_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :bad_instruction) do + bad_instruction_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :bad_instruction) do + bad_instruction_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :bad_instruction) do + bad_instruction_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(_, :bad_instruction) do + throw(:bad_enum) + end + + def to_atom(0x0, :bad_match) do + bad_match_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :bad_match) do + bad_match_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :bad_match) do + bad_match_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :bad_match) do + bad_match_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :bad_match) do + bad_match_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :bad_match) do + bad_match_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :bad_match) do + bad_match_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :bad_match) do + bad_match_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :bad_match) do + bad_match_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :bad_match) do + bad_match_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :bad_match) do + bad_match_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :bad_match) do + bad_match_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(_, :bad_match) do + throw(:bad_enum) + end + + def to_atom(0x0, :flow_mod_failed) do + flow_mod_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :flow_mod_failed) do + flow_mod_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :flow_mod_failed) do + flow_mod_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :flow_mod_failed) do + flow_mod_failed_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :flow_mod_failed) do + flow_mod_failed_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :flow_mod_failed) do + flow_mod_failed_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :flow_mod_failed) do + flow_mod_failed_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :flow_mod_failed) do + flow_mod_failed_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(_, :flow_mod_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :group_mod_failed) do + group_mod_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :group_mod_failed) do + group_mod_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :group_mod_failed) do + group_mod_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :group_mod_failed) do + group_mod_failed_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :group_mod_failed) do + group_mod_failed_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :group_mod_failed) do + group_mod_failed_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :group_mod_failed) do + group_mod_failed_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :group_mod_failed) do + group_mod_failed_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :group_mod_failed) do + group_mod_failed_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :group_mod_failed) do + group_mod_failed_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :group_mod_failed) do + group_mod_failed_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :group_mod_failed) do + group_mod_failed_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :group_mod_failed) do + group_mod_failed_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :group_mod_failed) do + group_mod_failed_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xE, :group_mod_failed) do + group_mod_failed_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(_, :group_mod_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :port_mod_failed) do + port_mod_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :port_mod_failed) do + port_mod_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :port_mod_failed) do + port_mod_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :port_mod_failed) do + port_mod_failed_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :port_mod_failed) do + port_mod_failed_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(_, :port_mod_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :table_mod_failed) do + table_mod_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :table_mod_failed) do + table_mod_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :table_mod_failed) do + table_mod_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :table_mod_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :queue_op_failed) do + queue_op_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :queue_op_failed) do + queue_op_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :queue_op_failed) do + queue_op_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :queue_op_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :switch_config_failed) do + switch_config_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :switch_config_failed) do + switch_config_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :switch_config_failed) do + switch_config_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :switch_config_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :role_request_failed) do + role_request_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :role_request_failed) do + role_request_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :role_request_failed) do + role_request_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :role_request_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :meter_mod_failed) do + meter_mod_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :meter_mod_failed) do + meter_mod_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :meter_mod_failed) do + meter_mod_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :meter_mod_failed) do + meter_mod_failed_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :meter_mod_failed) do + meter_mod_failed_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :meter_mod_failed) do + meter_mod_failed_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :meter_mod_failed) do + meter_mod_failed_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :meter_mod_failed) do + meter_mod_failed_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :meter_mod_failed) do + meter_mod_failed_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :meter_mod_failed) do + meter_mod_failed_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :meter_mod_failed) do + meter_mod_failed_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :meter_mod_failed) do + meter_mod_failed_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(_, :meter_mod_failed) do + throw(:bad_enum) + end + + def to_atom(0x0, :table_features_failed) do + table_features_failed_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :table_features_failed) do + table_features_failed_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :table_features_failed) do + table_features_failed_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :table_features_failed) do + table_features_failed_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :table_features_failed) do + table_features_failed_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :table_features_failed) do + table_features_failed_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(_, :table_features_failed) do + throw(:bad_enum) + end + + def to_atom(0x1, :switch_capabilities) do + switch_capabilities_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :switch_capabilities) do + switch_capabilities_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :switch_capabilities) do + switch_capabilities_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :switch_capabilities) do + switch_capabilities_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x20, :switch_capabilities) do + switch_capabilities_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x40, :switch_capabilities) do + switch_capabilities_to_atom(0x40) + catch + _class, _reason -> 64 + end + + def to_atom(0x80, :switch_capabilities) do + switch_capabilities_to_atom(0x80) + catch + _class, _reason -> 128 + end + + def to_atom(0x100, :switch_capabilities) do + switch_capabilities_to_atom(0x100) + catch + _class, _reason -> 256 + end + + def to_atom(_, :switch_capabilities) do + throw(:bad_enum) + end + + def to_atom(0x1, :config_flags) do + config_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :config_flags) do + config_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :config_flags) do + throw(:bad_enum) + end + + def to_atom(0xFFE5, :controller_max_len) do + controller_max_len_to_atom(0xFFE5) + catch + _class, _reason -> 65509 + end + + def to_atom(0xFFFF, :controller_max_len) do + controller_max_len_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :controller_max_len) do + throw(:bad_enum) + end + + def to_atom(0x2320, :experimenter_oxm_vendors) do + experimenter_oxm_vendors_to_atom(0x2320) + catch + _class, _reason -> 8992 + end + + def to_atom(0x2428, :experimenter_oxm_vendors) do + experimenter_oxm_vendors_to_atom(0x2428) + catch + _class, _reason -> 9256 + end + + def to_atom(0x4F4E4600, :experimenter_oxm_vendors) do + experimenter_oxm_vendors_to_atom(0x4F4E4600) + catch + _class, _reason -> 1_330_529_792 + end + + def to_atom(_, :experimenter_oxm_vendors) do + throw(:bad_enum) + end + + def to_atom(0x0, :match_type) do + match_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :match_type) do + match_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(_, :match_type) do + throw(:bad_enum) + end + + def to_atom(0x0, :oxm_class) do + oxm_class_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :oxm_class) do + oxm_class_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x8000, :oxm_class) do + oxm_class_to_atom(0x8000) + catch + _class, _reason -> 32768 + end + + def to_atom(0x8001, :oxm_class) do + oxm_class_to_atom(0x8001) + catch + _class, _reason -> 32769 + end + + def to_atom(0xFFFF, :oxm_class) do + oxm_class_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :oxm_class) do + throw(:bad_enum) + end + + def to_atom(0x0, :nxm_0) do + nxm_0_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nxm_0) do + nxm_0_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nxm_0) do + nxm_0_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :nxm_0) do + nxm_0_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :nxm_0) do + nxm_0_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :nxm_0) do + nxm_0_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :nxm_0) do + nxm_0_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :nxm_0) do + nxm_0_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :nxm_0) do + nxm_0_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :nxm_0) do + nxm_0_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :nxm_0) do + nxm_0_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :nxm_0) do + nxm_0_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :nxm_0) do + nxm_0_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :nxm_0) do + nxm_0_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xE, :nxm_0) do + nxm_0_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(0xF, :nxm_0) do + nxm_0_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0x10, :nxm_0) do + nxm_0_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x11, :nxm_0) do + nxm_0_to_atom(0x11) + catch + _class, _reason -> 17 + end + + def to_atom(0x22, :nxm_0) do + nxm_0_to_atom(0x22) + catch + _class, _reason -> 34 + end + + def to_atom(_, :nxm_0) do + throw(:bad_enum) + end + + def to_atom(0x0, :nxm_1) do + nxm_1_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nxm_1) do + nxm_1_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nxm_1) do + nxm_1_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :nxm_1) do + nxm_1_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :nxm_1) do + nxm_1_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :nxm_1) do + nxm_1_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :nxm_1) do + nxm_1_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :nxm_1) do + nxm_1_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :nxm_1) do + nxm_1_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :nxm_1) do + nxm_1_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :nxm_1) do + nxm_1_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :nxm_1) do + nxm_1_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :nxm_1) do + nxm_1_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :nxm_1) do + nxm_1_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xE, :nxm_1) do + nxm_1_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(0xF, :nxm_1) do + nxm_1_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0x10, :nxm_1) do + nxm_1_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x11, :nxm_1) do + nxm_1_to_atom(0x11) + catch + _class, _reason -> 17 + end + + def to_atom(0x12, :nxm_1) do + nxm_1_to_atom(0x12) + catch + _class, _reason -> 18 + end + + def to_atom(0x13, :nxm_1) do + nxm_1_to_atom(0x13) + catch + _class, _reason -> 19 + end + + def to_atom(0x14, :nxm_1) do + nxm_1_to_atom(0x14) + catch + _class, _reason -> 20 + end + + def to_atom(0x15, :nxm_1) do + nxm_1_to_atom(0x15) + catch + _class, _reason -> 21 + end + + def to_atom(0x16, :nxm_1) do + nxm_1_to_atom(0x16) + catch + _class, _reason -> 22 + end + + def to_atom(0x17, :nxm_1) do + nxm_1_to_atom(0x17) + catch + _class, _reason -> 23 + end + + def to_atom(0x18, :nxm_1) do + nxm_1_to_atom(0x18) + catch + _class, _reason -> 24 + end + + def to_atom(0x19, :nxm_1) do + nxm_1_to_atom(0x19) + catch + _class, _reason -> 25 + end + + def to_atom(0x1A, :nxm_1) do + nxm_1_to_atom(0x1A) + catch + _class, _reason -> 26 + end + + def to_atom(0x1B, :nxm_1) do + nxm_1_to_atom(0x1B) + catch + _class, _reason -> 27 + end + + def to_atom(0x1C, :nxm_1) do + nxm_1_to_atom(0x1C) + catch + _class, _reason -> 28 + end + + def to_atom(0x1D, :nxm_1) do + nxm_1_to_atom(0x1D) + catch + _class, _reason -> 29 + end + + def to_atom(0x1E, :nxm_1) do + nxm_1_to_atom(0x1E) + catch + _class, _reason -> 30 + end + + def to_atom(0x1F, :nxm_1) do + nxm_1_to_atom(0x1F) + catch + _class, _reason -> 31 + end + + def to_atom(0x20, :nxm_1) do + nxm_1_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x21, :nxm_1) do + nxm_1_to_atom(0x21) + catch + _class, _reason -> 33 + end + + def to_atom(0x23, :nxm_1) do + nxm_1_to_atom(0x23) + catch + _class, _reason -> 35 + end + + def to_atom(0x24, :nxm_1) do + nxm_1_to_atom(0x24) + catch + _class, _reason -> 36 + end + + def to_atom(0x25, :nxm_1) do + nxm_1_to_atom(0x25) + catch + _class, _reason -> 37 + end + + def to_atom(0x26, :nxm_1) do + nxm_1_to_atom(0x26) + catch + _class, _reason -> 38 + end + + def to_atom(0x27, :nxm_1) do + nxm_1_to_atom(0x27) + catch + _class, _reason -> 39 + end + + def to_atom(0x28, :nxm_1) do + nxm_1_to_atom(0x28) + catch + _class, _reason -> 40 + end + + def to_atom(0x29, :nxm_1) do + nxm_1_to_atom(0x29) + catch + _class, _reason -> 41 + end + + def to_atom(0x2A, :nxm_1) do + nxm_1_to_atom(0x2A) + catch + _class, _reason -> 42 + end + + def to_atom(0x2B, :nxm_1) do + nxm_1_to_atom(0x2B) + catch + _class, _reason -> 43 + end + + def to_atom(0x2C, :nxm_1) do + nxm_1_to_atom(0x2C) + catch + _class, _reason -> 44 + end + + def to_atom(0x2D, :nxm_1) do + nxm_1_to_atom(0x2D) + catch + _class, _reason -> 45 + end + + def to_atom(0x2E, :nxm_1) do + nxm_1_to_atom(0x2E) + catch + _class, _reason -> 46 + end + + def to_atom(0x2F, :nxm_1) do + nxm_1_to_atom(0x2F) + catch + _class, _reason -> 47 + end + + def to_atom(0x30, :nxm_1) do + nxm_1_to_atom(0x30) + catch + _class, _reason -> 48 + end + + def to_atom(0x31, :nxm_1) do + nxm_1_to_atom(0x31) + catch + _class, _reason -> 49 + end + + def to_atom(0x32, :nxm_1) do + nxm_1_to_atom(0x32) + catch + _class, _reason -> 50 + end + + def to_atom(0x33, :nxm_1) do + nxm_1_to_atom(0x33) + catch + _class, _reason -> 51 + end + + def to_atom(0x34, :nxm_1) do + nxm_1_to_atom(0x34) + catch + _class, _reason -> 52 + end + + def to_atom(0x35, :nxm_1) do + nxm_1_to_atom(0x35) + catch + _class, _reason -> 53 + end + + def to_atom(0x36, :nxm_1) do + nxm_1_to_atom(0x36) + catch + _class, _reason -> 54 + end + + def to_atom(0x37, :nxm_1) do + nxm_1_to_atom(0x37) + catch + _class, _reason -> 55 + end + + def to_atom(0x38, :nxm_1) do + nxm_1_to_atom(0x38) + catch + _class, _reason -> 56 + end + + def to_atom(0x39, :nxm_1) do + nxm_1_to_atom(0x39) + catch + _class, _reason -> 57 + end + + def to_atom(0x3A, :nxm_1) do + nxm_1_to_atom(0x3A) + catch + _class, _reason -> 58 + end + + def to_atom(0x3B, :nxm_1) do + nxm_1_to_atom(0x3B) + catch + _class, _reason -> 59 + end + + def to_atom(0x3C, :nxm_1) do + nxm_1_to_atom(0x3C) + catch + _class, _reason -> 60 + end + + def to_atom(0x3D, :nxm_1) do + nxm_1_to_atom(0x3D) + catch + _class, _reason -> 61 + end + + def to_atom(0x3E, :nxm_1) do + nxm_1_to_atom(0x3E) + catch + _class, _reason -> 62 + end + + def to_atom(0x3F, :nxm_1) do + nxm_1_to_atom(0x3F) + catch + _class, _reason -> 63 + end + + def to_atom(0x40, :nxm_1) do + nxm_1_to_atom(0x40) + catch + _class, _reason -> 64 + end + + def to_atom(0x41, :nxm_1) do + nxm_1_to_atom(0x41) + catch + _class, _reason -> 65 + end + + def to_atom(0x42, :nxm_1) do + nxm_1_to_atom(0x42) + catch + _class, _reason -> 66 + end + + def to_atom(0x43, :nxm_1) do + nxm_1_to_atom(0x43) + catch + _class, _reason -> 67 + end + + def to_atom(0x44, :nxm_1) do + nxm_1_to_atom(0x44) + catch + _class, _reason -> 68 + end + + def to_atom(0x45, :nxm_1) do + nxm_1_to_atom(0x45) + catch + _class, _reason -> 69 + end + + def to_atom(0x46, :nxm_1) do + nxm_1_to_atom(0x46) + catch + _class, _reason -> 70 + end + + def to_atom(0x47, :nxm_1) do + nxm_1_to_atom(0x47) + catch + _class, _reason -> 71 + end + + def to_atom(0x48, :nxm_1) do + nxm_1_to_atom(0x48) + catch + _class, _reason -> 72 + end + + def to_atom(0x49, :nxm_1) do + nxm_1_to_atom(0x49) + catch + _class, _reason -> 73 + end + + def to_atom(0x4A, :nxm_1) do + nxm_1_to_atom(0x4A) + catch + _class, _reason -> 74 + end + + def to_atom(0x4B, :nxm_1) do + nxm_1_to_atom(0x4B) + catch + _class, _reason -> 75 + end + + def to_atom(0x4C, :nxm_1) do + nxm_1_to_atom(0x4C) + catch + _class, _reason -> 76 + end + + def to_atom(0x4D, :nxm_1) do + nxm_1_to_atom(0x4D) + catch + _class, _reason -> 77 + end + + def to_atom(0x4E, :nxm_1) do + nxm_1_to_atom(0x4E) + catch + _class, _reason -> 78 + end + + def to_atom(0x4F, :nxm_1) do + nxm_1_to_atom(0x4F) + catch + _class, _reason -> 79 + end + + def to_atom(0x50, :nxm_1) do + nxm_1_to_atom(0x50) + catch + _class, _reason -> 80 + end + + def to_atom(0x51, :nxm_1) do + nxm_1_to_atom(0x51) + catch + _class, _reason -> 81 + end + + def to_atom(0x52, :nxm_1) do + nxm_1_to_atom(0x52) + catch + _class, _reason -> 82 + end + + def to_atom(0x53, :nxm_1) do + nxm_1_to_atom(0x53) + catch + _class, _reason -> 83 + end + + def to_atom(0x54, :nxm_1) do + nxm_1_to_atom(0x54) + catch + _class, _reason -> 84 + end + + def to_atom(0x55, :nxm_1) do + nxm_1_to_atom(0x55) + catch + _class, _reason -> 85 + end + + def to_atom(0x56, :nxm_1) do + nxm_1_to_atom(0x56) + catch + _class, _reason -> 86 + end + + def to_atom(0x57, :nxm_1) do + nxm_1_to_atom(0x57) + catch + _class, _reason -> 87 + end + + def to_atom(0x58, :nxm_1) do + nxm_1_to_atom(0x58) + catch + _class, _reason -> 88 + end + + def to_atom(0x59, :nxm_1) do + nxm_1_to_atom(0x59) + catch + _class, _reason -> 89 + end + + def to_atom(0x5A, :nxm_1) do + nxm_1_to_atom(0x5A) + catch + _class, _reason -> 90 + end + + def to_atom(0x5B, :nxm_1) do + nxm_1_to_atom(0x5B) + catch + _class, _reason -> 91 + end + + def to_atom(0x5C, :nxm_1) do + nxm_1_to_atom(0x5C) + catch + _class, _reason -> 92 + end + + def to_atom(0x5D, :nxm_1) do + nxm_1_to_atom(0x5D) + catch + _class, _reason -> 93 + end + + def to_atom(0x5E, :nxm_1) do + nxm_1_to_atom(0x5E) + catch + _class, _reason -> 94 + end + + def to_atom(0x5F, :nxm_1) do + nxm_1_to_atom(0x5F) + catch + _class, _reason -> 95 + end + + def to_atom(0x60, :nxm_1) do + nxm_1_to_atom(0x60) + catch + _class, _reason -> 96 + end + + def to_atom(0x61, :nxm_1) do + nxm_1_to_atom(0x61) + catch + _class, _reason -> 97 + end + + def to_atom(0x62, :nxm_1) do + nxm_1_to_atom(0x62) + catch + _class, _reason -> 98 + end + + def to_atom(0x63, :nxm_1) do + nxm_1_to_atom(0x63) + catch + _class, _reason -> 99 + end + + def to_atom(0x64, :nxm_1) do + nxm_1_to_atom(0x64) + catch + _class, _reason -> 100 + end + + def to_atom(0x65, :nxm_1) do + nxm_1_to_atom(0x65) + catch + _class, _reason -> 101 + end + + def to_atom(0x66, :nxm_1) do + nxm_1_to_atom(0x66) + catch + _class, _reason -> 102 + end + + def to_atom(0x67, :nxm_1) do + nxm_1_to_atom(0x67) + catch + _class, _reason -> 103 + end + + def to_atom(0x68, :nxm_1) do + nxm_1_to_atom(0x68) + catch + _class, _reason -> 104 + end + + def to_atom(0x69, :nxm_1) do + nxm_1_to_atom(0x69) + catch + _class, _reason -> 105 + end + + def to_atom(0x6A, :nxm_1) do + nxm_1_to_atom(0x6A) + catch + _class, _reason -> 106 + end + + def to_atom(0x6B, :nxm_1) do + nxm_1_to_atom(0x6B) + catch + _class, _reason -> 107 + end + + def to_atom(0x6C, :nxm_1) do + nxm_1_to_atom(0x6C) + catch + _class, _reason -> 108 + end + + def to_atom(0x6D, :nxm_1) do + nxm_1_to_atom(0x6D) + catch + _class, _reason -> 109 + end + + def to_atom(0x6E, :nxm_1) do + nxm_1_to_atom(0x6E) + catch + _class, _reason -> 110 + end + + def to_atom(0x6F, :nxm_1) do + nxm_1_to_atom(0x6F) + catch + _class, _reason -> 111 + end + + def to_atom(0x70, :nxm_1) do + nxm_1_to_atom(0x70) + catch + _class, _reason -> 112 + end + + def to_atom(0x71, :nxm_1) do + nxm_1_to_atom(0x71) + catch + _class, _reason -> 113 + end + + def to_atom(0x72, :nxm_1) do + nxm_1_to_atom(0x72) + catch + _class, _reason -> 114 + end + + def to_atom(0x73, :nxm_1) do + nxm_1_to_atom(0x73) + catch + _class, _reason -> 115 + end + + def to_atom(0x74, :nxm_1) do + nxm_1_to_atom(0x74) + catch + _class, _reason -> 116 + end + + def to_atom(0x75, :nxm_1) do + nxm_1_to_atom(0x75) + catch + _class, _reason -> 117 + end + + def to_atom(0x76, :nxm_1) do + nxm_1_to_atom(0x76) + catch + _class, _reason -> 118 + end + + def to_atom(0x77, :nxm_1) do + nxm_1_to_atom(0x77) + catch + _class, _reason -> 119 + end + + def to_atom(0x78, :nxm_1) do + nxm_1_to_atom(0x78) + catch + _class, _reason -> 120 + end + + def to_atom(0x79, :nxm_1) do + nxm_1_to_atom(0x79) + catch + _class, _reason -> 121 + end + + def to_atom(0x7A, :nxm_1) do + nxm_1_to_atom(0x7A) + catch + _class, _reason -> 122 + end + + def to_atom(0x7B, :nxm_1) do + nxm_1_to_atom(0x7B) + catch + _class, _reason -> 123 + end + + def to_atom(0x7C, :nxm_1) do + nxm_1_to_atom(0x7C) + catch + _class, _reason -> 124 + end + + def to_atom(0x7D, :nxm_1) do + nxm_1_to_atom(0x7D) + catch + _class, _reason -> 125 + end + + def to_atom(_, :nxm_1) do + throw(:bad_enum) + end + + def to_atom(0x0, :openflow_basic) do + openflow_basic_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :openflow_basic) do + openflow_basic_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :openflow_basic) do + openflow_basic_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :openflow_basic) do + openflow_basic_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :openflow_basic) do + openflow_basic_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :openflow_basic) do + openflow_basic_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :openflow_basic) do + openflow_basic_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :openflow_basic) do + openflow_basic_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :openflow_basic) do + openflow_basic_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :openflow_basic) do + openflow_basic_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :openflow_basic) do + openflow_basic_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xB, :openflow_basic) do + openflow_basic_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :openflow_basic) do + openflow_basic_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :openflow_basic) do + openflow_basic_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xE, :openflow_basic) do + openflow_basic_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(0xF, :openflow_basic) do + openflow_basic_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0x10, :openflow_basic) do + openflow_basic_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x11, :openflow_basic) do + openflow_basic_to_atom(0x11) + catch + _class, _reason -> 17 + end + + def to_atom(0x12, :openflow_basic) do + openflow_basic_to_atom(0x12) + catch + _class, _reason -> 18 + end + + def to_atom(0x13, :openflow_basic) do + openflow_basic_to_atom(0x13) + catch + _class, _reason -> 19 + end + + def to_atom(0x14, :openflow_basic) do + openflow_basic_to_atom(0x14) + catch + _class, _reason -> 20 + end + + def to_atom(0x15, :openflow_basic) do + openflow_basic_to_atom(0x15) + catch + _class, _reason -> 21 + end + + def to_atom(0x16, :openflow_basic) do + openflow_basic_to_atom(0x16) + catch + _class, _reason -> 22 + end + + def to_atom(0x17, :openflow_basic) do + openflow_basic_to_atom(0x17) + catch + _class, _reason -> 23 + end + + def to_atom(0x18, :openflow_basic) do + openflow_basic_to_atom(0x18) + catch + _class, _reason -> 24 + end + + def to_atom(0x19, :openflow_basic) do + openflow_basic_to_atom(0x19) + catch + _class, _reason -> 25 + end + + def to_atom(0x1A, :openflow_basic) do + openflow_basic_to_atom(0x1A) + catch + _class, _reason -> 26 + end + + def to_atom(0x1B, :openflow_basic) do + openflow_basic_to_atom(0x1B) + catch + _class, _reason -> 27 + end + + def to_atom(0x1C, :openflow_basic) do + openflow_basic_to_atom(0x1C) + catch + _class, _reason -> 28 + end + + def to_atom(0x1D, :openflow_basic) do + openflow_basic_to_atom(0x1D) + catch + _class, _reason -> 29 + end + + def to_atom(0x1E, :openflow_basic) do + openflow_basic_to_atom(0x1E) + catch + _class, _reason -> 30 + end + + def to_atom(0x1F, :openflow_basic) do + openflow_basic_to_atom(0x1F) + catch + _class, _reason -> 31 + end + + def to_atom(0x20, :openflow_basic) do + openflow_basic_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x21, :openflow_basic) do + openflow_basic_to_atom(0x21) + catch + _class, _reason -> 33 + end + + def to_atom(0x22, :openflow_basic) do + openflow_basic_to_atom(0x22) + catch + _class, _reason -> 34 + end + + def to_atom(0x23, :openflow_basic) do + openflow_basic_to_atom(0x23) + catch + _class, _reason -> 35 + end + + def to_atom(0x24, :openflow_basic) do + openflow_basic_to_atom(0x24) + catch + _class, _reason -> 36 + end + + def to_atom(0x25, :openflow_basic) do + openflow_basic_to_atom(0x25) + catch + _class, _reason -> 37 + end + + def to_atom(0x26, :openflow_basic) do + openflow_basic_to_atom(0x26) + catch + _class, _reason -> 38 + end + + def to_atom(0x27, :openflow_basic) do + openflow_basic_to_atom(0x27) + catch + _class, _reason -> 39 + end + + def to_atom(0x29, :openflow_basic) do + openflow_basic_to_atom(0x29) + catch + _class, _reason -> 41 + end + + def to_atom(0x2A, :openflow_basic) do + openflow_basic_to_atom(0x2A) + catch + _class, _reason -> 42 + end + + def to_atom(0x2B, :openflow_basic) do + openflow_basic_to_atom(0x2B) + catch + _class, _reason -> 43 + end + + def to_atom(0x2C, :openflow_basic) do + openflow_basic_to_atom(0x2C) + catch + _class, _reason -> 44 + end + + def to_atom(0x2D, :openflow_basic) do + openflow_basic_to_atom(0x2D) + catch + _class, _reason -> 45 + end + + def to_atom(0x2E, :openflow_basic) do + openflow_basic_to_atom(0x2E) + catch + _class, _reason -> 46 + end + + def to_atom(0x2F, :openflow_basic) do + openflow_basic_to_atom(0x2F) + catch + _class, _reason -> 47 + end + + def to_atom(0x30, :openflow_basic) do + openflow_basic_to_atom(0x30) + catch + _class, _reason -> 48 + end + + def to_atom(0x31, :openflow_basic) do + openflow_basic_to_atom(0x31) + catch + _class, _reason -> 49 + end + + def to_atom(0x32, :openflow_basic) do + openflow_basic_to_atom(0x32) + catch + _class, _reason -> 50 + end + + def to_atom(0x33, :openflow_basic) do + openflow_basic_to_atom(0x33) + catch + _class, _reason -> 51 + end + + def to_atom(0x34, :openflow_basic) do + openflow_basic_to_atom(0x34) + catch + _class, _reason -> 52 + end + + def to_atom(0x35, :openflow_basic) do + openflow_basic_to_atom(0x35) + catch + _class, _reason -> 53 + end + + def to_atom(0x36, :openflow_basic) do + openflow_basic_to_atom(0x36) + catch + _class, _reason -> 54 + end + + def to_atom(0x37, :openflow_basic) do + openflow_basic_to_atom(0x37) + catch + _class, _reason -> 55 + end + + def to_atom(0x38, :openflow_basic) do + openflow_basic_to_atom(0x38) + catch + _class, _reason -> 56 + end + + def to_atom(0x39, :openflow_basic) do + openflow_basic_to_atom(0x39) + catch + _class, _reason -> 57 + end + + def to_atom(0x3A, :openflow_basic) do + openflow_basic_to_atom(0x3A) + catch + _class, _reason -> 58 + end + + def to_atom(0x3B, :openflow_basic) do + openflow_basic_to_atom(0x3B) + catch + _class, _reason -> 59 + end + + def to_atom(0x3C, :openflow_basic) do + openflow_basic_to_atom(0x3C) + catch + _class, _reason -> 60 + end + + def to_atom(0x3D, :openflow_basic) do + openflow_basic_to_atom(0x3D) + catch + _class, _reason -> 61 + end + + def to_atom(0x3E, :openflow_basic) do + openflow_basic_to_atom(0x3E) + catch + _class, _reason -> 62 + end + + def to_atom(0x3F, :openflow_basic) do + openflow_basic_to_atom(0x3F) + catch + _class, _reason -> 63 + end + + def to_atom(0x40, :openflow_basic) do + openflow_basic_to_atom(0x40) + catch + _class, _reason -> 64 + end + + def to_atom(0x41, :openflow_basic) do + openflow_basic_to_atom(0x41) + catch + _class, _reason -> 65 + end + + def to_atom(0x42, :openflow_basic) do + openflow_basic_to_atom(0x42) + catch + _class, _reason -> 66 + end + + def to_atom(0x43, :openflow_basic) do + openflow_basic_to_atom(0x43) + catch + _class, _reason -> 67 + end + + def to_atom(_, :openflow_basic) do + throw(:bad_enum) + end + + def to_atom(0x1000, :vlan_id) do + vlan_id_to_atom(0x1000) + catch + _class, _reason -> 4096 + end + + def to_atom(0x0, :vlan_id) do + vlan_id_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(_, :vlan_id) do + throw(:bad_enum) + end + + def to_atom(0x1, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x20, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x40, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x40) + catch + _class, _reason -> 64 + end + + def to_atom(0x80, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x80) + catch + _class, _reason -> 128 + end + + def to_atom(0x100, :ipv6exthdr_flags) do + ipv6exthdr_flags_to_atom(0x100) + catch + _class, _reason -> 256 + end + + def to_atom(_, :ipv6exthdr_flags) do + throw(:bad_enum) + end + + def to_atom(0x1, :tcp_flags) do + tcp_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :tcp_flags) do + tcp_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :tcp_flags) do + tcp_flags_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :tcp_flags) do + tcp_flags_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :tcp_flags) do + tcp_flags_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x20, :tcp_flags) do + tcp_flags_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x40, :tcp_flags) do + tcp_flags_to_atom(0x40) + catch + _class, _reason -> 64 + end + + def to_atom(0x80, :tcp_flags) do + tcp_flags_to_atom(0x80) + catch + _class, _reason -> 128 + end + + def to_atom(0x100, :tcp_flags) do + tcp_flags_to_atom(0x100) + catch + _class, _reason -> 256 + end + + def to_atom(_, :tcp_flags) do + throw(:bad_enum) + end + + def to_atom(0x1, :ct_state_flags) do + ct_state_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :ct_state_flags) do + ct_state_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :ct_state_flags) do + ct_state_flags_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :ct_state_flags) do + ct_state_flags_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :ct_state_flags) do + ct_state_flags_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x20, :ct_state_flags) do + ct_state_flags_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x40, :ct_state_flags) do + ct_state_flags_to_atom(0x40) + catch + _class, _reason -> 64 + end + + def to_atom(0x80, :ct_state_flags) do + ct_state_flags_to_atom(0x80) + catch + _class, _reason -> 128 + end + + def to_atom(_, :ct_state_flags) do + throw(:bad_enum) + end + + def to_atom(0x0, :packet_register) do + packet_register_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :packet_register) do + packet_register_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :packet_register) do + packet_register_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :packet_register) do + packet_register_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :packet_register) do + packet_register_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :packet_register) do + packet_register_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :packet_register) do + packet_register_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :packet_register) do + packet_register_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(_, :packet_register) do + throw(:bad_enum) + end + + def to_atom(0x1, :nicira_ext_match) do + nicira_ext_match_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nicira_ext_match) do + nicira_ext_match_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :nicira_ext_match) do + nicira_ext_match_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :nicira_ext_match) do + nicira_ext_match_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :nicira_ext_match) do + nicira_ext_match_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :nicira_ext_match) do + nicira_ext_match_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :nicira_ext_match) do + nicira_ext_match_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :nicira_ext_match) do + nicira_ext_match_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :nicira_ext_match) do + nicira_ext_match_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(_, :nicira_ext_match) do + throw(:bad_enum) + end + + def to_atom(0x0, :hp_ext_match) do + hp_ext_match_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :hp_ext_match) do + hp_ext_match_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :hp_ext_match) do + hp_ext_match_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :hp_ext_match) do + hp_ext_match_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :hp_ext_match) do + hp_ext_match_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :hp_ext_match) do + hp_ext_match_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :hp_ext_match) do + hp_ext_match_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :hp_ext_match) do + hp_ext_match_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :hp_ext_match) do + hp_ext_match_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(_, :hp_ext_match) do + throw(:bad_enum) + end + + def to_atom(0x1, :hp_custom_match_type) do + hp_custom_match_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :hp_custom_match_type) do + hp_custom_match_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :hp_custom_match_type) do + hp_custom_match_type_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(_, :hp_custom_match_type) do + throw(:bad_enum) + end + + def to_atom(0x2A, :onf_ext_match) do + onf_ext_match_to_atom(0x2A) + catch + _class, _reason -> 42 + end + + def to_atom(0x2B, :onf_ext_match) do + onf_ext_match_to_atom(0x2B) + catch + _class, _reason -> 43 + end + + def to_atom(0xA00, :onf_ext_match) do + onf_ext_match_to_atom(0xA00) + catch + _class, _reason -> 2560 + end + + def to_atom(_, :onf_ext_match) do + throw(:bad_enum) + end + + def to_atom(0xFFFFFFFF, :buffer_id) do + buffer_id_to_atom(0xFFFFFFFF) + catch + _class, _reason -> 4_294_967_295 + end + + def to_atom(_, :buffer_id) do + throw(:bad_enum) + end + + def to_atom(0x1, :port_config) do + port_config_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x4, :port_config) do + port_config_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x20, :port_config) do + port_config_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x40, :port_config) do + port_config_to_atom(0x40) + catch + _class, _reason -> 64 + end + + def to_atom(_, :port_config) do + throw(:bad_enum) + end + + def to_atom(0x1, :port_state) do + port_state_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :port_state) do + port_state_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :port_state) do + port_state_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(_, :port_state) do + throw(:bad_enum) + end + + def to_atom(0x1, :port_features) do + port_features_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :port_features) do + port_features_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :port_features) do + port_features_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :port_features) do + port_features_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :port_features) do + port_features_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x20, :port_features) do + port_features_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x40, :port_features) do + port_features_to_atom(0x40) + catch + _class, _reason -> 64 + end + + def to_atom(0x80, :port_features) do + port_features_to_atom(0x80) + catch + _class, _reason -> 128 + end + + def to_atom(0x100, :port_features) do + port_features_to_atom(0x100) + catch + _class, _reason -> 256 + end + + def to_atom(0x200, :port_features) do + port_features_to_atom(0x200) + catch + _class, _reason -> 512 + end + + def to_atom(0x400, :port_features) do + port_features_to_atom(0x400) + catch + _class, _reason -> 1024 + end + + def to_atom(0x800, :port_features) do + port_features_to_atom(0x800) + catch + _class, _reason -> 2048 + end + + def to_atom(0x1000, :port_features) do + port_features_to_atom(0x1000) + catch + _class, _reason -> 4096 + end + + def to_atom(0x2000, :port_features) do + port_features_to_atom(0x2000) + catch + _class, _reason -> 8192 + end + + def to_atom(0x4000, :port_features) do + port_features_to_atom(0x4000) + catch + _class, _reason -> 16384 + end + + def to_atom(0x8000, :port_features) do + port_features_to_atom(0x8000) + catch + _class, _reason -> 32768 + end + + def to_atom(_, :port_features) do + throw(:bad_enum) + end + + def to_atom(0xFF00, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFF00) + catch + _class, _reason -> 65280 + end + + def to_atom(0xFFF8, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFFF8) + catch + _class, _reason -> 65528 + end + + def to_atom(0xFFF9, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFFF9) + catch + _class, _reason -> 65529 + end + + def to_atom(0xFFFA, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFFFA) + catch + _class, _reason -> 65530 + end + + def to_atom(0xFFFB, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFFFB) + catch + _class, _reason -> 65531 + end + + def to_atom(0xFFFC, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFFFC) + catch + _class, _reason -> 65532 + end + + def to_atom(0xFFFD, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFFFD) + catch + _class, _reason -> 65533 + end + + def to_atom(0xFFFE, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFFFE) + catch + _class, _reason -> 65534 + end + + def to_atom(0xFFFF, :openflow10_port_no) do + openflow10_port_no_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :openflow10_port_no) do + throw(:bad_enum) + end + + def to_atom(0xFFFFFF00, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFF00) + catch + _class, _reason -> 4_294_967_040 + end + + def to_atom(0xFFFFFFF8, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFFF8) + catch + _class, _reason -> 4_294_967_288 + end + + def to_atom(0xFFFFFFF9, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFFF9) + catch + _class, _reason -> 4_294_967_289 + end + + def to_atom(0xFFFFFFFA, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFFFA) + catch + _class, _reason -> 4_294_967_290 + end + + def to_atom(0xFFFFFFFB, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFFFB) + catch + _class, _reason -> 4_294_967_291 + end + + def to_atom(0xFFFFFFFC, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFFFC) + catch + _class, _reason -> 4_294_967_292 + end + + def to_atom(0xFFFFFFFD, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFFFD) + catch + _class, _reason -> 4_294_967_293 + end + + def to_atom(0xFFFFFFFE, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFFFE) + catch + _class, _reason -> 4_294_967_294 + end + + def to_atom(0xFFFFFFFF, :openflow13_port_no) do + openflow13_port_no_to_atom(0xFFFFFFFF) + catch + _class, _reason -> 4_294_967_295 + end + + def to_atom(_, :openflow13_port_no) do + throw(:bad_enum) + end + + def to_atom(0x0, :packet_in_reason) do + packet_in_reason_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :packet_in_reason) do + packet_in_reason_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :packet_in_reason) do + packet_in_reason_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :packet_in_reason) do + packet_in_reason_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :packet_in_reason) do + packet_in_reason_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :packet_in_reason) do + packet_in_reason_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(_, :packet_in_reason) do + throw(:bad_enum) + end + + def to_atom(0x0, :flow_mod_command) do + flow_mod_command_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :flow_mod_command) do + flow_mod_command_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :flow_mod_command) do + flow_mod_command_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :flow_mod_command) do + flow_mod_command_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :flow_mod_command) do + flow_mod_command_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(_, :flow_mod_command) do + throw(:bad_enum) + end + + def to_atom(0x1, :flow_mod_flags) do + flow_mod_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :flow_mod_flags) do + flow_mod_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :flow_mod_flags) do + flow_mod_flags_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :flow_mod_flags) do + flow_mod_flags_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :flow_mod_flags) do + flow_mod_flags_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(_, :flow_mod_flags) do + throw(:bad_enum) + end + + def to_atom(0x0, :flow_removed_reason) do + flow_removed_reason_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :flow_removed_reason) do + flow_removed_reason_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :flow_removed_reason) do + flow_removed_reason_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :flow_removed_reason) do + flow_removed_reason_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :flow_removed_reason) do + flow_removed_reason_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :flow_removed_reason) do + flow_removed_reason_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(_, :flow_removed_reason) do + throw(:bad_enum) + end + + def to_atom(0x0, :port_reason) do + port_reason_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :port_reason) do + port_reason_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :port_reason) do + port_reason_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :port_reason) do + throw(:bad_enum) + end + + def to_atom(0x0, :group_mod_command) do + group_mod_command_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :group_mod_command) do + group_mod_command_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :group_mod_command) do + group_mod_command_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :group_mod_command) do + throw(:bad_enum) + end + + def to_atom(0x0, :group_type) do + group_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :group_type) do + group_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :group_type) do + group_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :group_type) do + group_type_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(_, :group_type) do + throw(:bad_enum) + end + + def to_atom(0xFFFFFF00, :group_id) do + group_id_to_atom(0xFFFFFF00) + catch + _class, _reason -> 4_294_967_040 + end + + def to_atom(0xFFFFFFFC, :group_id) do + group_id_to_atom(0xFFFFFFFC) + catch + _class, _reason -> 4_294_967_292 + end + + def to_atom(0xFFFFFFFF, :group_id) do + group_id_to_atom(0xFFFFFFFF) + catch + _class, _reason -> 4_294_967_295 + end + + def to_atom(_, :group_id) do + throw(:bad_enum) + end + + def to_atom(0x1, :group_capabilities) do + group_capabilities_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :group_capabilities) do + group_capabilities_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :group_capabilities) do + group_capabilities_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :group_capabilities) do + group_capabilities_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(_, :group_capabilities) do + throw(:bad_enum) + end + + def to_atom(0xFE, :table_id) do + table_id_to_atom(0xFE) + catch + _class, _reason -> 254 + end + + def to_atom(0xFF, :table_id) do + table_id_to_atom(0xFF) + catch + _class, _reason -> 255 + end + + def to_atom(_, :table_id) do + throw(:bad_enum) + end + + def to_atom(0xFFFFFFFF, :queue_id) do + queue_id_to_atom(0xFFFFFFFF) + catch + _class, _reason -> 4_294_967_295 + end + + def to_atom(_, :queue_id) do + throw(:bad_enum) + end + + def to_atom(0x0, :meter_mod_command) do + meter_mod_command_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :meter_mod_command) do + meter_mod_command_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :meter_mod_command) do + meter_mod_command_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :meter_mod_command) do + throw(:bad_enum) + end + + def to_atom(0xFFFF0000, :meter_id) do + meter_id_to_atom(0xFFFF0000) + catch + _class, _reason -> 4_294_901_760 + end + + def to_atom(0xFFFFFFFD, :meter_id) do + meter_id_to_atom(0xFFFFFFFD) + catch + _class, _reason -> 4_294_967_293 + end + + def to_atom(0xFFFFFFFE, :meter_id) do + meter_id_to_atom(0xFFFFFFFE) + catch + _class, _reason -> 4_294_967_294 + end + + def to_atom(0xFFFFFFFF, :meter_id) do + meter_id_to_atom(0xFFFFFFFF) + catch + _class, _reason -> 4_294_967_295 + end + + def to_atom(_, :meter_id) do + throw(:bad_enum) + end + + def to_atom(0x1, :meter_flags) do + meter_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :meter_flags) do + meter_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :meter_flags) do + meter_flags_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :meter_flags) do + meter_flags_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(_, :meter_flags) do + throw(:bad_enum) + end + + def to_atom(0x1, :meter_band_type) do + meter_band_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :meter_band_type) do + meter_band_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0xFFFF, :meter_band_type) do + meter_band_type_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :meter_band_type) do + throw(:bad_enum) + end + + def to_atom(0x0, :table_config) do + table_config_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :table_config) do + table_config_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :table_config) do + table_config_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :table_config) do + table_config_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :table_config) do + table_config_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :table_config) do + table_config_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(_, :table_config) do + throw(:bad_enum) + end + + def to_atom(0x0, :action_type) do + action_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0xB, :action_type) do + action_type_to_atom(0xB) + catch + _class, _reason -> 11 + end + + def to_atom(0xC, :action_type) do + action_type_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xF, :action_type) do + action_type_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0x10, :action_type) do + action_type_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x11, :action_type) do + action_type_to_atom(0x11) + catch + _class, _reason -> 17 + end + + def to_atom(0x12, :action_type) do + action_type_to_atom(0x12) + catch + _class, _reason -> 18 + end + + def to_atom(0x13, :action_type) do + action_type_to_atom(0x13) + catch + _class, _reason -> 19 + end + + def to_atom(0x14, :action_type) do + action_type_to_atom(0x14) + catch + _class, _reason -> 20 + end + + def to_atom(0x15, :action_type) do + action_type_to_atom(0x15) + catch + _class, _reason -> 21 + end + + def to_atom(0x16, :action_type) do + action_type_to_atom(0x16) + catch + _class, _reason -> 22 + end + + def to_atom(0x17, :action_type) do + action_type_to_atom(0x17) + catch + _class, _reason -> 23 + end + + def to_atom(0x18, :action_type) do + action_type_to_atom(0x18) + catch + _class, _reason -> 24 + end + + def to_atom(0x19, :action_type) do + action_type_to_atom(0x19) + catch + _class, _reason -> 25 + end + + def to_atom(0x1A, :action_type) do + action_type_to_atom(0x1A) + catch + _class, _reason -> 26 + end + + def to_atom(0x1B, :action_type) do + action_type_to_atom(0x1B) + catch + _class, _reason -> 27 + end + + def to_atom(0x1C, :action_type) do + action_type_to_atom(0x1C) + catch + _class, _reason -> 28 + end + + def to_atom(0x1D, :action_type) do + action_type_to_atom(0x1D) + catch + _class, _reason -> 29 + end + + def to_atom(0x1E, :action_type) do + action_type_to_atom(0x1E) + catch + _class, _reason -> 30 + end + + def to_atom(0x1F, :action_type) do + action_type_to_atom(0x1F) + catch + _class, _reason -> 31 + end + + def to_atom(0xFFFF, :action_type) do + action_type_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :action_type) do + throw(:bad_enum) + end + + def to_atom(0x2320, :action_vendor) do + action_vendor_to_atom(0x2320) + catch + _class, _reason -> 8992 + end + + def to_atom(0x4F4E4600, :action_vendor) do + action_vendor_to_atom(0x4F4E4600) + catch + _class, _reason -> 1_330_529_792 + end + + def to_atom(_, :action_vendor) do + throw(:bad_enum) + end + + def to_atom(0xC80, :onf_ext_action) do + onf_ext_action_to_atom(0xC80) + catch + _class, _reason -> 3200 + end + + def to_atom(_, :onf_ext_action) do + throw(:bad_enum) + end + + def to_atom(0x1, :nicira_ext_action) do + nicira_ext_action_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nicira_ext_action) do + nicira_ext_action_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :nicira_ext_action) do + nicira_ext_action_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :nicira_ext_action) do + nicira_ext_action_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :nicira_ext_action) do + nicira_ext_action_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :nicira_ext_action) do + nicira_ext_action_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :nicira_ext_action) do + nicira_ext_action_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x9, :nicira_ext_action) do + nicira_ext_action_to_atom(0x9) + catch + _class, _reason -> 9 + end + + def to_atom(0xA, :nicira_ext_action) do + nicira_ext_action_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xC, :nicira_ext_action) do + nicira_ext_action_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :nicira_ext_action) do + nicira_ext_action_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xE, :nicira_ext_action) do + nicira_ext_action_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(0xF, :nicira_ext_action) do + nicira_ext_action_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0x10, :nicira_ext_action) do + nicira_ext_action_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x11, :nicira_ext_action) do + nicira_ext_action_to_atom(0x11) + catch + _class, _reason -> 17 + end + + def to_atom(0x12, :nicira_ext_action) do + nicira_ext_action_to_atom(0x12) + catch + _class, _reason -> 18 + end + + def to_atom(0x13, :nicira_ext_action) do + nicira_ext_action_to_atom(0x13) + catch + _class, _reason -> 19 + end + + def to_atom(0x14, :nicira_ext_action) do + nicira_ext_action_to_atom(0x14) + catch + _class, _reason -> 20 + end + + def to_atom(0x15, :nicira_ext_action) do + nicira_ext_action_to_atom(0x15) + catch + _class, _reason -> 21 + end + + def to_atom(0x16, :nicira_ext_action) do + nicira_ext_action_to_atom(0x16) + catch + _class, _reason -> 22 + end + + def to_atom(0x17, :nicira_ext_action) do + nicira_ext_action_to_atom(0x17) + catch + _class, _reason -> 23 + end + + def to_atom(0x18, :nicira_ext_action) do + nicira_ext_action_to_atom(0x18) + catch + _class, _reason -> 24 + end + + def to_atom(0x19, :nicira_ext_action) do + nicira_ext_action_to_atom(0x19) + catch + _class, _reason -> 25 + end + + def to_atom(0x1A, :nicira_ext_action) do + nicira_ext_action_to_atom(0x1A) + catch + _class, _reason -> 26 + end + + def to_atom(0x1B, :nicira_ext_action) do + nicira_ext_action_to_atom(0x1B) + catch + _class, _reason -> 27 + end + + def to_atom(0x1C, :nicira_ext_action) do + nicira_ext_action_to_atom(0x1C) + catch + _class, _reason -> 28 + end + + def to_atom(0x1D, :nicira_ext_action) do + nicira_ext_action_to_atom(0x1D) + catch + _class, _reason -> 29 + end + + def to_atom(0x1E, :nicira_ext_action) do + nicira_ext_action_to_atom(0x1E) + catch + _class, _reason -> 30 + end + + def to_atom(0x1F, :nicira_ext_action) do + nicira_ext_action_to_atom(0x1F) + catch + _class, _reason -> 31 + end + + def to_atom(0x20, :nicira_ext_action) do + nicira_ext_action_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(0x21, :nicira_ext_action) do + nicira_ext_action_to_atom(0x21) + catch + _class, _reason -> 33 + end + + def to_atom(0x22, :nicira_ext_action) do + nicira_ext_action_to_atom(0x22) + catch + _class, _reason -> 34 + end + + def to_atom(0x23, :nicira_ext_action) do + nicira_ext_action_to_atom(0x23) + catch + _class, _reason -> 35 + end + + def to_atom(0x24, :nicira_ext_action) do + nicira_ext_action_to_atom(0x24) + catch + _class, _reason -> 36 + end + + def to_atom(0x25, :nicira_ext_action) do + nicira_ext_action_to_atom(0x25) + catch + _class, _reason -> 37 + end + + def to_atom(0x26, :nicira_ext_action) do + nicira_ext_action_to_atom(0x26) + catch + _class, _reason -> 38 + end + + def to_atom(0x27, :nicira_ext_action) do + nicira_ext_action_to_atom(0x27) + catch + _class, _reason -> 39 + end + + def to_atom(0x28, :nicira_ext_action) do + nicira_ext_action_to_atom(0x28) + catch + _class, _reason -> 40 + end + + def to_atom(0x29, :nicira_ext_action) do + nicira_ext_action_to_atom(0x29) + catch + _class, _reason -> 41 + end + + def to_atom(0x2A, :nicira_ext_action) do + nicira_ext_action_to_atom(0x2A) + catch + _class, _reason -> 42 + end + + def to_atom(0x2B, :nicira_ext_action) do + nicira_ext_action_to_atom(0x2B) + catch + _class, _reason -> 43 + end + + def to_atom(0x2C, :nicira_ext_action) do + nicira_ext_action_to_atom(0x2C) + catch + _class, _reason -> 44 + end + + def to_atom(0x2D, :nicira_ext_action) do + nicira_ext_action_to_atom(0x2D) + catch + _class, _reason -> 45 + end + + def to_atom(0x2E, :nicira_ext_action) do + nicira_ext_action_to_atom(0x2E) + catch + _class, _reason -> 46 + end + + def to_atom(0x2F, :nicira_ext_action) do + nicira_ext_action_to_atom(0x2F) + catch + _class, _reason -> 47 + end + + def to_atom(0xFF, :nicira_ext_action) do + nicira_ext_action_to_atom(0xFF) + catch + _class, _reason -> 255 + end + + def to_atom(_, :nicira_ext_action) do + throw(:bad_enum) + end + + def to_atom(0x0, :nx_mp_algorithm) do + nx_mp_algorithm_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nx_mp_algorithm) do + nx_mp_algorithm_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_mp_algorithm) do + nx_mp_algorithm_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :nx_mp_algorithm) do + nx_mp_algorithm_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(_, :nx_mp_algorithm) do + throw(:bad_enum) + end + + def to_atom(0x0, :nx_hash_fields) do + nx_hash_fields_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nx_hash_fields) do + nx_hash_fields_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_hash_fields) do + nx_hash_fields_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :nx_hash_fields) do + nx_hash_fields_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :nx_hash_fields) do + nx_hash_fields_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :nx_hash_fields) do + nx_hash_fields_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(_, :nx_hash_fields) do + throw(:bad_enum) + end + + def to_atom(0x0, :nx_bd_algorithm) do + nx_bd_algorithm_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nx_bd_algorithm) do + nx_bd_algorithm_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(_, :nx_bd_algorithm) do + throw(:bad_enum) + end + + def to_atom(0x1, :nx_learn_flag) do + nx_learn_flag_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_learn_flag) do + nx_learn_flag_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :nx_learn_flag) do + nx_learn_flag_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(_, :nx_learn_flag) do + throw(:bad_enum) + end + + def to_atom(0x1, :nx_conntrack_flags) do + nx_conntrack_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_conntrack_flags) do + nx_conntrack_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :nx_conntrack_flags) do + throw(:bad_enum) + end + + def to_atom(0x1, :nx_nat_flags) do + nx_nat_flags_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_nat_flags) do + nx_nat_flags_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :nx_nat_flags) do + nx_nat_flags_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :nx_nat_flags) do + nx_nat_flags_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :nx_nat_flags) do + nx_nat_flags_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(_, :nx_nat_flags) do + throw(:bad_enum) + end + + def to_atom(0x1, :nx_nat_range) do + nx_nat_range_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_nat_range) do + nx_nat_range_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :nx_nat_range) do + nx_nat_range_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :nx_nat_range) do + nx_nat_range_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :nx_nat_range) do + nx_nat_range_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x20, :nx_nat_range) do + nx_nat_range_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(_, :nx_nat_range) do + throw(:bad_enum) + end + + def to_atom(0x0, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :nx_action_controller2_prop_type) do + nx_action_controller2_prop_type_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(_, :nx_action_controller2_prop_type) do + throw(:bad_enum) + end + + def to_atom(0x0, :nx_action_sample_direction) do + nx_action_sample_direction_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nx_action_sample_direction) do + nx_action_sample_direction_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_action_sample_direction) do + nx_action_sample_direction_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :nx_action_sample_direction) do + throw(:bad_enum) + end + + def to_atom(0x0, :nx_flow_spec_type) do + nx_flow_spec_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nx_flow_spec_type) do + nx_flow_spec_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_flow_spec_type) do + nx_flow_spec_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :nx_flow_spec_type) do + throw(:bad_enum) + end + + def to_atom(0x1, :instruction_type) do + instruction_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :instruction_type) do + instruction_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :instruction_type) do + instruction_type_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :instruction_type) do + instruction_type_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :instruction_type) do + instruction_type_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :instruction_type) do + instruction_type_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0xFFFF, :instruction_type) do + instruction_type_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :instruction_type) do + throw(:bad_enum) + end + + def to_atom(0x0, :controller_role) do + controller_role_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :controller_role) do + controller_role_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :controller_role) do + controller_role_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :controller_role) do + controller_role_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(_, :controller_role) do + throw(:bad_enum) + end + + def to_atom(0x0, :nx_role) do + nx_role_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :nx_role) do + nx_role_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :nx_role) do + nx_role_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :nx_role) do + throw(:bad_enum) + end + + def to_atom(0x0, :packet_in_format) do + packet_in_format_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :packet_in_format) do + packet_in_format_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :packet_in_format) do + packet_in_format_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :packet_in_format) do + throw(:bad_enum) + end + + def to_atom(0x0, :flow_format) do + flow_format_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :flow_format) do + flow_format_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(_, :flow_format) do + throw(:bad_enum) + end + + def to_atom(0x0, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :packet_in2_prop_type) do + packet_in2_prop_type_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(_, :packet_in2_prop_type) do + throw(:bad_enum) + end + + def to_atom(0x8000, :continuation_prop_type) do + continuation_prop_type_to_atom(0x8000) + catch + _class, _reason -> 32768 + end + + def to_atom(0x8001, :continuation_prop_type) do + continuation_prop_type_to_atom(0x8001) + catch + _class, _reason -> 32769 + end + + def to_atom(0x8002, :continuation_prop_type) do + continuation_prop_type_to_atom(0x8002) + catch + _class, _reason -> 32770 + end + + def to_atom(0x8003, :continuation_prop_type) do + continuation_prop_type_to_atom(0x8003) + catch + _class, _reason -> 32771 + end + + def to_atom(0x8004, :continuation_prop_type) do + continuation_prop_type_to_atom(0x8004) + catch + _class, _reason -> 32772 + end + + def to_atom(0x8005, :continuation_prop_type) do + continuation_prop_type_to_atom(0x8005) + catch + _class, _reason -> 32773 + end + + def to_atom(0x8006, :continuation_prop_type) do + continuation_prop_type_to_atom(0x8006) + catch + _class, _reason -> 32774 + end + + def to_atom(0x8007, :continuation_prop_type) do + continuation_prop_type_to_atom(0x8007) + catch + _class, _reason -> 32775 + end + + def to_atom(_, :continuation_prop_type) do + throw(:bad_enum) + end + + def to_atom(0x1, :flow_monitor_flag) do + flow_monitor_flag_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :flow_monitor_flag) do + flow_monitor_flag_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x4, :flow_monitor_flag) do + flow_monitor_flag_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x8, :flow_monitor_flag) do + flow_monitor_flag_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0x10, :flow_monitor_flag) do + flow_monitor_flag_to_atom(0x10) + catch + _class, _reason -> 16 + end + + def to_atom(0x20, :flow_monitor_flag) do + flow_monitor_flag_to_atom(0x20) + catch + _class, _reason -> 32 + end + + def to_atom(_, :flow_monitor_flag) do + throw(:bad_enum) + end + + def to_atom(0x0, :flow_update_event) do + flow_update_event_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :flow_update_event) do + flow_update_event_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :flow_update_event) do + flow_update_event_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :flow_update_event) do + flow_update_event_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(_, :flow_update_event) do + throw(:bad_enum) + end + + def to_atom(0x0, :tlv_table_mod_command) do + tlv_table_mod_command_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :tlv_table_mod_command) do + tlv_table_mod_command_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :tlv_table_mod_command) do + tlv_table_mod_command_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(_, :tlv_table_mod_command) do + throw(:bad_enum) + end + + def to_atom(0x0, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x0) + catch + _class, _reason -> 0 + end + + def to_atom(0x1, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x1) + catch + _class, _reason -> 1 + end + + def to_atom(0x2, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x2) + catch + _class, _reason -> 2 + end + + def to_atom(0x3, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x3) + catch + _class, _reason -> 3 + end + + def to_atom(0x4, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x4) + catch + _class, _reason -> 4 + end + + def to_atom(0x5, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x5) + catch + _class, _reason -> 5 + end + + def to_atom(0x6, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x6) + catch + _class, _reason -> 6 + end + + def to_atom(0x7, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x7) + catch + _class, _reason -> 7 + end + + def to_atom(0x8, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0x8) + catch + _class, _reason -> 8 + end + + def to_atom(0xA, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0xA) + catch + _class, _reason -> 10 + end + + def to_atom(0xC, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0xC) + catch + _class, _reason -> 12 + end + + def to_atom(0xD, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0xD) + catch + _class, _reason -> 13 + end + + def to_atom(0xE, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0xE) + catch + _class, _reason -> 14 + end + + def to_atom(0xF, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0xF) + catch + _class, _reason -> 15 + end + + def to_atom(0xFFFE, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0xFFFE) + catch + _class, _reason -> 65534 + end + + def to_atom(0xFFFF, :table_feature_prop_type) do + table_feature_prop_type_to_atom(0xFFFF) + catch + _class, _reason -> 65535 + end + + def to_atom(_, :table_feature_prop_type) do + throw(:bad_enum) + end + + def openflow_codec_to_int(Openflow.Hello), do: 0x0 + def openflow_codec_to_int(Openflow.ErrorMsg), do: 0x1 + def openflow_codec_to_int(Openflow.Echo.Request), do: 0x2 + def openflow_codec_to_int(Openflow.Echo.Reply), do: 0x3 + def openflow_codec_to_int(Openflow.Experimenter), do: 0x4 + def openflow_codec_to_int(Openflow.Features.Request), do: 0x5 + def openflow_codec_to_int(Openflow.Features.Reply), do: 0x6 + def openflow_codec_to_int(Openflow.GetConfig.Request), do: 0x7 + def openflow_codec_to_int(Openflow.GetConfig.Reply), do: 0x8 + def openflow_codec_to_int(Openflow.SetConfig), do: 0x9 + def openflow_codec_to_int(Openflow.PacketIn), do: 0xA + def openflow_codec_to_int(Openflow.FlowRemoved), do: 0xB + def openflow_codec_to_int(Openflow.PortStatus), do: 0xC + def openflow_codec_to_int(Openflow.PacketOut), do: 0xD + def openflow_codec_to_int(Openflow.FlowMod), do: 0xE + def openflow_codec_to_int(Openflow.GroupMod), do: 0xF + def openflow_codec_to_int(Openflow.PortMod), do: 0x10 + def openflow_codec_to_int(Openflow.TableMod), do: 0x11 + def openflow_codec_to_int(Openflow.Multipart.Request), do: 0x12 + def openflow_codec_to_int(Openflow.Multipart.Reply), do: 0x13 + def openflow_codec_to_int(Openflow.Barrier.Request), do: 0x14 + def openflow_codec_to_int(Openflow.Barrier.Reply), do: 0x15 + def openflow_codec_to_int(Openflow.Role.Request), do: 0x18 + def openflow_codec_to_int(Openflow.Role.Reply), do: 0x19 + def openflow_codec_to_int(Openflow.GetAsync.Request), do: 0x1A + def openflow_codec_to_int(Openflow.GetAsync.Reply), do: 0x1B + def openflow_codec_to_int(Openflow.SetAsync), do: 0x1C + def openflow_codec_to_int(Openflow.MeterMod), do: 0x1D + def openflow_codec_to_int(_), do: throw(:bad_enum) + def openflow_codec_to_atom(0x0), do: Openflow.Hello + def openflow_codec_to_atom(0x1), do: Openflow.ErrorMsg + def openflow_codec_to_atom(0x2), do: Openflow.Echo.Request + def openflow_codec_to_atom(0x3), do: Openflow.Echo.Reply + def openflow_codec_to_atom(0x4), do: Openflow.Experimenter + def openflow_codec_to_atom(0x5), do: Openflow.Features.Request + def openflow_codec_to_atom(0x6), do: Openflow.Features.Reply + def openflow_codec_to_atom(0x7), do: Openflow.GetConfig.Request + def openflow_codec_to_atom(0x8), do: Openflow.GetConfig.Reply + def openflow_codec_to_atom(0x9), do: Openflow.SetConfig + def openflow_codec_to_atom(0xA), do: Openflow.PacketIn + def openflow_codec_to_atom(0xB), do: Openflow.FlowRemoved + def openflow_codec_to_atom(0xC), do: Openflow.PortStatus + def openflow_codec_to_atom(0xD), do: Openflow.PacketOut + def openflow_codec_to_atom(0xE), do: Openflow.FlowMod + def openflow_codec_to_atom(0xF), do: Openflow.GroupMod + def openflow_codec_to_atom(0x10), do: Openflow.PortMod + def openflow_codec_to_atom(0x11), do: Openflow.TableMod + def openflow_codec_to_atom(0x12), do: Openflow.Multipart.Request + def openflow_codec_to_atom(0x13), do: Openflow.Multipart.Reply + def openflow_codec_to_atom(0x14), do: Openflow.Barrier.Request + def openflow_codec_to_atom(0x15), do: Openflow.Barrier.Reply + def openflow_codec_to_atom(0x18), do: Openflow.Role.Request + def openflow_codec_to_atom(0x19), do: Openflow.Role.Reply + def openflow_codec_to_atom(0x1A), do: Openflow.GetAsync.Request + def openflow_codec_to_atom(0x1B), do: Openflow.GetAsync.Reply + def openflow_codec_to_atom(0x1C), do: Openflow.SetAsync + def openflow_codec_to_atom(0x1D), do: Openflow.MeterMod + def openflow_codec_to_atom(_), do: throw(:bad_enum) + def experimenter_id_to_int(:nicira_ext_message), do: 0x2320 + def experimenter_id_to_int(:onf_ext_message), do: 0x4F4E4600 + def experimenter_id_to_int(_), do: throw(:bad_enum) + def experimenter_id_to_atom(0x2320), do: :nicira_ext_message + def experimenter_id_to_atom(0x4F4E4600), do: :onf_ext_message + def experimenter_id_to_atom(_), do: throw(:bad_enum) + def nicira_ext_message_to_int(Openflow.NxSetPacketInFormat), do: 0x10 + def nicira_ext_message_to_int(Openflow.NxSetControllerId), do: 0x14 + def nicira_ext_message_to_int(Openflow.NxFlowMonitor.Cancel), do: 0x15 + def nicira_ext_message_to_int(Openflow.NxFlowMonitor.Paused), do: 0x16 + def nicira_ext_message_to_int(Openflow.NxFlowMonitor.Resumed), do: 0x17 + def nicira_ext_message_to_int(Openflow.NxTLVTableMod), do: 0x18 + def nicira_ext_message_to_int(Openflow.NxTLVTable.Request), do: 0x19 + def nicira_ext_message_to_int(Openflow.NxTLVTable.Reply), do: 0x1A + def nicira_ext_message_to_int(Openflow.NxSetAsyncConfig2), do: 0x1B + def nicira_ext_message_to_int(Openflow.NxResume), do: 0x1C + def nicira_ext_message_to_int(Openflow.NxCtFlushZone), do: 0x1D + def nicira_ext_message_to_int(Openflow.NxPacketIn2), do: 0x1E + def nicira_ext_message_to_int(_), do: throw(:bad_enum) + def nicira_ext_message_to_atom(0x10), do: Openflow.NxSetPacketInFormat + def nicira_ext_message_to_atom(0x14), do: Openflow.NxSetControllerId + def nicira_ext_message_to_atom(0x15), do: Openflow.NxFlowMonitor.Cancel + def nicira_ext_message_to_atom(0x16), do: Openflow.NxFlowMonitor.Paused + def nicira_ext_message_to_atom(0x17), do: Openflow.NxFlowMonitor.Resumed + def nicira_ext_message_to_atom(0x18), do: Openflow.NxTLVTableMod + def nicira_ext_message_to_atom(0x19), do: Openflow.NxTLVTable.Request + def nicira_ext_message_to_atom(0x1A), do: Openflow.NxTLVTable.Reply + def nicira_ext_message_to_atom(0x1B), do: Openflow.NxSetAsyncConfig2 + def nicira_ext_message_to_atom(0x1C), do: Openflow.NxResume + def nicira_ext_message_to_atom(0x1D), do: Openflow.NxCtFlushZone + def nicira_ext_message_to_atom(0x1E), do: Openflow.NxPacketIn2 + def nicira_ext_message_to_atom(_), do: throw(:bad_enum) + def onf_ext_message_to_int(Openflow.OnfBundleControl), do: 0x8FC + def onf_ext_message_to_int(Openflow.OnfBundleAddMessage), do: 0x8FD + def onf_ext_message_to_int(_), do: throw(:bad_enum) + def onf_ext_message_to_atom(0x8FC), do: Openflow.OnfBundleControl + def onf_ext_message_to_atom(0x8FD), do: Openflow.OnfBundleAddMessage + def onf_ext_message_to_atom(_), do: throw(:bad_enum) + def multipart_request_flags_to_int(:more), do: 0x1 + def multipart_request_flags_to_int(_), do: throw(:bad_enum) + def multipart_request_flags_to_atom(0x1), do: :more + def multipart_request_flags_to_atom(_), do: throw(:bad_enum) + def multipart_reply_flags_to_int(:more), do: 0x1 + def multipart_reply_flags_to_int(_), do: throw(:bad_enum) + def multipart_reply_flags_to_atom(0x1), do: :more + def multipart_reply_flags_to_atom(_), do: throw(:bad_enum) + def multipart_request_codec_to_int(Openflow.Multipart.Desc.Request), do: 0x0 + def multipart_request_codec_to_int(Openflow.Multipart.Flow.Request), do: 0x1 + def multipart_request_codec_to_int(Openflow.Multipart.Aggregate.Request), do: 0x2 + def multipart_request_codec_to_int(Openflow.Multipart.Table.Request), do: 0x3 + def multipart_request_codec_to_int(Openflow.Multipart.PortStats.Request), do: 0x4 + def multipart_request_codec_to_int(Openflow.Multipart.Queue.Request), do: 0x5 + def multipart_request_codec_to_int(Openflow.Multipart.Group.Request), do: 0x6 + def multipart_request_codec_to_int(Openflow.Multipart.GroupDesc.Request), do: 0x7 + def multipart_request_codec_to_int(Openflow.Multipart.GroupFeatures.Request), do: 0x8 + def multipart_request_codec_to_int(Openflow.Multipart.Meter.Request), do: 0x9 + def multipart_request_codec_to_int(Openflow.Multipart.MeterConfig.Request), do: 0xA + def multipart_request_codec_to_int(Openflow.Multipart.MeterFeatures.Request), do: 0xB + def multipart_request_codec_to_int(Openflow.Multipart.TableFeatures.Request), do: 0xC + def multipart_request_codec_to_int(Openflow.Multipart.PortDesc.Request), do: 0xD + def multipart_request_codec_to_int(Openflow.Multipart.Experimenter.Request), do: 0xFFFF + def multipart_request_codec_to_int(_), do: throw(:bad_enum) + def multipart_request_codec_to_atom(0x0), do: Openflow.Multipart.Desc.Request + def multipart_request_codec_to_atom(0x1), do: Openflow.Multipart.Flow.Request + def multipart_request_codec_to_atom(0x2), do: Openflow.Multipart.Aggregate.Request + def multipart_request_codec_to_atom(0x3), do: Openflow.Multipart.Table.Request + def multipart_request_codec_to_atom(0x4), do: Openflow.Multipart.PortStats.Request + def multipart_request_codec_to_atom(0x5), do: Openflow.Multipart.Queue.Request + def multipart_request_codec_to_atom(0x6), do: Openflow.Multipart.Group.Request + def multipart_request_codec_to_atom(0x7), do: Openflow.Multipart.GroupDesc.Request + def multipart_request_codec_to_atom(0x8), do: Openflow.Multipart.GroupFeatures.Request + def multipart_request_codec_to_atom(0x9), do: Openflow.Multipart.Meter.Request + def multipart_request_codec_to_atom(0xA), do: Openflow.Multipart.MeterConfig.Request + def multipart_request_codec_to_atom(0xB), do: Openflow.Multipart.MeterFeatures.Request + def multipart_request_codec_to_atom(0xC), do: Openflow.Multipart.TableFeatures.Request + def multipart_request_codec_to_atom(0xD), do: Openflow.Multipart.PortDesc.Request + def multipart_request_codec_to_atom(0xFFFF), do: Openflow.Multipart.Experimenter.Request + def multipart_request_codec_to_atom(_), do: throw(:bad_enum) + def multipart_reply_codec_to_int(Openflow.Multipart.Desc.Reply), do: 0x0 + def multipart_reply_codec_to_int(Openflow.Multipart.Flow.Reply), do: 0x1 + def multipart_reply_codec_to_int(Openflow.Multipart.Aggregate.Reply), do: 0x2 + def multipart_reply_codec_to_int(Openflow.Multipart.Table.Reply), do: 0x3 + def multipart_reply_codec_to_int(Openflow.Multipart.PortStats.Reply), do: 0x4 + def multipart_reply_codec_to_int(Openflow.Multipart.Queue.Reply), do: 0x5 + def multipart_reply_codec_to_int(Openflow.Multipart.Group.Reply), do: 0x6 + def multipart_reply_codec_to_int(Openflow.Multipart.GroupDesc.Reply), do: 0x7 + def multipart_reply_codec_to_int(Openflow.Multipart.GroupFeatures.Reply), do: 0x8 + def multipart_reply_codec_to_int(Openflow.Multipart.Meter.Reply), do: 0x9 + def multipart_reply_codec_to_int(Openflow.Multipart.MeterConfig.Reply), do: 0xA + def multipart_reply_codec_to_int(Openflow.Multipart.MeterFeatures.Reply), do: 0xB + def multipart_reply_codec_to_int(Openflow.Multipart.TableFeatures.Reply), do: 0xC + def multipart_reply_codec_to_int(Openflow.Multipart.PortDesc.Reply), do: 0xD + def multipart_reply_codec_to_int(Openflow.Multipart.Experimenter.Reply), do: 0xFFFF + def multipart_reply_codec_to_int(_), do: throw(:bad_enum) + def multipart_reply_codec_to_atom(0x0), do: Openflow.Multipart.Desc.Reply + def multipart_reply_codec_to_atom(0x1), do: Openflow.Multipart.Flow.Reply + def multipart_reply_codec_to_atom(0x2), do: Openflow.Multipart.Aggregate.Reply + def multipart_reply_codec_to_atom(0x3), do: Openflow.Multipart.Table.Reply + def multipart_reply_codec_to_atom(0x4), do: Openflow.Multipart.PortStats.Reply + def multipart_reply_codec_to_atom(0x5), do: Openflow.Multipart.Queue.Reply + def multipart_reply_codec_to_atom(0x6), do: Openflow.Multipart.Group.Reply + def multipart_reply_codec_to_atom(0x7), do: Openflow.Multipart.GroupDesc.Reply + def multipart_reply_codec_to_atom(0x8), do: Openflow.Multipart.GroupFeatures.Reply + def multipart_reply_codec_to_atom(0x9), do: Openflow.Multipart.Meter.Reply + def multipart_reply_codec_to_atom(0xA), do: Openflow.Multipart.MeterConfig.Reply + def multipart_reply_codec_to_atom(0xB), do: Openflow.Multipart.MeterFeatures.Reply + def multipart_reply_codec_to_atom(0xC), do: Openflow.Multipart.TableFeatures.Reply + def multipart_reply_codec_to_atom(0xD), do: Openflow.Multipart.PortDesc.Reply + def multipart_reply_codec_to_atom(0xFFFF), do: Openflow.Multipart.Experimenter.Reply + def multipart_reply_codec_to_atom(_), do: throw(:bad_enum) + def nicira_ext_stats_to_int(Openflow.Multipart.NxFlow), do: 0x0 + def nicira_ext_stats_to_int(Openflow.Multipart.NxAggregate), do: 0x1 + def nicira_ext_stats_to_int(Openflow.Multipart.NxFlowMonitor), do: 0x2 + def nicira_ext_stats_to_int(Openflow.Multipart.NxIPFIXBridge), do: 0x3 + def nicira_ext_stats_to_int(Openflow.Multipart.NxIPFIXFlow), do: 0x4 + def nicira_ext_stats_to_int(_), do: throw(:bad_enum) + def nicira_ext_stats_to_atom(0x0), do: Openflow.Multipart.NxFlow + def nicira_ext_stats_to_atom(0x1), do: Openflow.Multipart.NxAggregate + def nicira_ext_stats_to_atom(0x2), do: Openflow.Multipart.NxFlowMonitor + def nicira_ext_stats_to_atom(0x3), do: Openflow.Multipart.NxIPFIXBridge + def nicira_ext_stats_to_atom(0x4), do: Openflow.Multipart.NxIPFIXFlow + def nicira_ext_stats_to_atom(_), do: throw(:bad_enum) + def hello_elem_to_int(:versionbitmap), do: 0x1 + def hello_elem_to_int(_), do: throw(:bad_enum) + def hello_elem_to_atom(0x1), do: :versionbitmap + def hello_elem_to_atom(_), do: throw(:bad_enum) + def error_type_to_int(:hello_failed), do: 0x0 + def error_type_to_int(:bad_request), do: 0x1 + def error_type_to_int(:bad_action), do: 0x2 + def error_type_to_int(:bad_instruction), do: 0x3 + def error_type_to_int(:bad_match), do: 0x4 + def error_type_to_int(:flow_mod_failed), do: 0x5 + def error_type_to_int(:group_mod_failed), do: 0x6 + def error_type_to_int(:port_mod_failed), do: 0x7 + def error_type_to_int(:table_mod_failed), do: 0x8 + def error_type_to_int(:queue_op_failed), do: 0x9 + def error_type_to_int(:switch_config_failed), do: 0xA + def error_type_to_int(:role_request_failed), do: 0xB + def error_type_to_int(:meter_mod_failed), do: 0xC + def error_type_to_int(:table_features_failed), do: 0xD + def error_type_to_int(:experimenter), do: 0xFFFF + def error_type_to_int(_), do: throw(:bad_enum) + def error_type_to_atom(0x0), do: :hello_failed + def error_type_to_atom(0x1), do: :bad_request + def error_type_to_atom(0x2), do: :bad_action + def error_type_to_atom(0x3), do: :bad_instruction + def error_type_to_atom(0x4), do: :bad_match + def error_type_to_atom(0x5), do: :flow_mod_failed + def error_type_to_atom(0x6), do: :group_mod_failed + def error_type_to_atom(0x7), do: :port_mod_failed + def error_type_to_atom(0x8), do: :table_mod_failed + def error_type_to_atom(0x9), do: :queue_op_failed + def error_type_to_atom(0xA), do: :switch_config_failed + def error_type_to_atom(0xB), do: :role_request_failed + def error_type_to_atom(0xC), do: :meter_mod_failed + def error_type_to_atom(0xD), do: :table_features_failed + def error_type_to_atom(0xFFFF), do: :experimenter + def error_type_to_atom(_), do: throw(:bad_enum) + def hello_failed_to_int(:inconpatible), do: 0x0 + def hello_failed_to_int(:eperm), do: 0x1 + def hello_failed_to_int(_), do: throw(:bad_enum) + def hello_failed_to_atom(0x0), do: :inconpatible + def hello_failed_to_atom(0x1), do: :eperm + def hello_failed_to_atom(_), do: throw(:bad_enum) + def bad_request_to_int(:bad_version), do: 0x0 + def bad_request_to_int(:bad_type), do: 0x1 + def bad_request_to_int(:bad_multipart), do: 0x2 + def bad_request_to_int(:bad_experimeter), do: 0x3 + def bad_request_to_int(:bad_exp_type), do: 0x4 + def bad_request_to_int(:eperm), do: 0x5 + def bad_request_to_int(:bad_len), do: 0x6 + def bad_request_to_int(:buffer_empty), do: 0x7 + def bad_request_to_int(:buffer_unknown), do: 0x8 + def bad_request_to_int(:bad_table_id), do: 0x9 + def bad_request_to_int(:is_slave), do: 0xA + def bad_request_to_int(:bad_port), do: 0xB + def bad_request_to_int(:bad_packet), do: 0xC + def bad_request_to_int(:multipart_buffer_overflow), do: 0xD + def bad_request_to_int(_), do: throw(:bad_enum) + def bad_request_to_atom(0x0), do: :bad_version + def bad_request_to_atom(0x1), do: :bad_type + def bad_request_to_atom(0x2), do: :bad_multipart + def bad_request_to_atom(0x3), do: :bad_experimeter + def bad_request_to_atom(0x4), do: :bad_exp_type + def bad_request_to_atom(0x5), do: :eperm + def bad_request_to_atom(0x6), do: :bad_len + def bad_request_to_atom(0x7), do: :buffer_empty + def bad_request_to_atom(0x8), do: :buffer_unknown + def bad_request_to_atom(0x9), do: :bad_table_id + def bad_request_to_atom(0xA), do: :is_slave + def bad_request_to_atom(0xB), do: :bad_port + def bad_request_to_atom(0xC), do: :bad_packet + def bad_request_to_atom(0xD), do: :multipart_buffer_overflow + def bad_request_to_atom(_), do: throw(:bad_enum) + def bad_action_to_int(:bad_type), do: 0x0 + def bad_action_to_int(:bad_len), do: 0x1 + def bad_action_to_int(:bad_experimeter), do: 0x2 + def bad_action_to_int(:bad_exp_type), do: 0x3 + def bad_action_to_int(:bad_out_port), do: 0x4 + def bad_action_to_int(:bad_argument), do: 0x5 + def bad_action_to_int(:eperm), do: 0x6 + def bad_action_to_int(:too_many), do: 0x7 + def bad_action_to_int(:bad_queue), do: 0x8 + def bad_action_to_int(:bad_out_group), do: 0x9 + def bad_action_to_int(:match_inconsistent), do: 0xA + def bad_action_to_int(:unsupported_order), do: 0xB + def bad_action_to_int(:bad_tag), do: 0xC + def bad_action_to_int(:bad_set_type), do: 0xD + def bad_action_to_int(:bad_set_len), do: 0xE + def bad_action_to_int(:bad_set_argument), do: 0xF + def bad_action_to_int(_), do: throw(:bad_enum) + def bad_action_to_atom(0x0), do: :bad_type + def bad_action_to_atom(0x1), do: :bad_len + def bad_action_to_atom(0x2), do: :bad_experimeter + def bad_action_to_atom(0x3), do: :bad_exp_type + def bad_action_to_atom(0x4), do: :bad_out_port + def bad_action_to_atom(0x5), do: :bad_argument + def bad_action_to_atom(0x6), do: :eperm + def bad_action_to_atom(0x7), do: :too_many + def bad_action_to_atom(0x8), do: :bad_queue + def bad_action_to_atom(0x9), do: :bad_out_group + def bad_action_to_atom(0xA), do: :match_inconsistent + def bad_action_to_atom(0xB), do: :unsupported_order + def bad_action_to_atom(0xC), do: :bad_tag + def bad_action_to_atom(0xD), do: :bad_set_type + def bad_action_to_atom(0xE), do: :bad_set_len + def bad_action_to_atom(0xF), do: :bad_set_argument + def bad_action_to_atom(_), do: throw(:bad_enum) + def bad_instruction_to_int(:unknown_instruction), do: 0x0 + def bad_instruction_to_int(:unsupported_instruction), do: 0x1 + def bad_instruction_to_int(:bad_table_id), do: 0x2 + def bad_instruction_to_int(:unsupported_metadata), do: 0x3 + def bad_instruction_to_int(:unsupported_metadata_mask), do: 0x4 + def bad_instruction_to_int(:bad_experimeter), do: 0x5 + def bad_instruction_to_int(:bad_exp_type), do: 0x6 + def bad_instruction_to_int(:bad_len), do: 0x7 + def bad_instruction_to_int(:eperm), do: 0x8 + def bad_instruction_to_int(_), do: throw(:bad_enum) + def bad_instruction_to_atom(0x0), do: :unknown_instruction + def bad_instruction_to_atom(0x1), do: :unsupported_instruction + def bad_instruction_to_atom(0x2), do: :bad_table_id + def bad_instruction_to_atom(0x3), do: :unsupported_metadata + def bad_instruction_to_atom(0x4), do: :unsupported_metadata_mask + def bad_instruction_to_atom(0x5), do: :bad_experimeter + def bad_instruction_to_atom(0x6), do: :bad_exp_type + def bad_instruction_to_atom(0x7), do: :bad_len + def bad_instruction_to_atom(0x8), do: :eperm + def bad_instruction_to_atom(_), do: throw(:bad_enum) + def bad_match_to_int(:bad_type), do: 0x0 + def bad_match_to_int(:bad_len), do: 0x1 + def bad_match_to_int(:bad_tag), do: 0x2 + def bad_match_to_int(:bad_dl_addr_mask), do: 0x3 + def bad_match_to_int(:bad_nw_addr_mask), do: 0x4 + def bad_match_to_int(:bad_wildcards), do: 0x5 + def bad_match_to_int(:bad_field), do: 0x6 + def bad_match_to_int(:bad_value), do: 0x7 + def bad_match_to_int(:bad_mask), do: 0x8 + def bad_match_to_int(:bad_prereq), do: 0x9 + def bad_match_to_int(:dup_field), do: 0xA + def bad_match_to_int(:eperm), do: 0xB + def bad_match_to_int(_), do: throw(:bad_enum) + def bad_match_to_atom(0x0), do: :bad_type + def bad_match_to_atom(0x1), do: :bad_len + def bad_match_to_atom(0x2), do: :bad_tag + def bad_match_to_atom(0x3), do: :bad_dl_addr_mask + def bad_match_to_atom(0x4), do: :bad_nw_addr_mask + def bad_match_to_atom(0x5), do: :bad_wildcards + def bad_match_to_atom(0x6), do: :bad_field + def bad_match_to_atom(0x7), do: :bad_value + def bad_match_to_atom(0x8), do: :bad_mask + def bad_match_to_atom(0x9), do: :bad_prereq + def bad_match_to_atom(0xA), do: :dup_field + def bad_match_to_atom(0xB), do: :eperm + def bad_match_to_atom(_), do: throw(:bad_enum) + def flow_mod_failed_to_int(:unknown), do: 0x0 + def flow_mod_failed_to_int(:table_full), do: 0x1 + def flow_mod_failed_to_int(:bad_table_id), do: 0x2 + def flow_mod_failed_to_int(:overlap), do: 0x3 + def flow_mod_failed_to_int(:eperm), do: 0x4 + def flow_mod_failed_to_int(:bad_timeout), do: 0x5 + def flow_mod_failed_to_int(:bad_command), do: 0x6 + def flow_mod_failed_to_int(:bad_flags), do: 0x7 + def flow_mod_failed_to_int(_), do: throw(:bad_enum) + def flow_mod_failed_to_atom(0x0), do: :unknown + def flow_mod_failed_to_atom(0x1), do: :table_full + def flow_mod_failed_to_atom(0x2), do: :bad_table_id + def flow_mod_failed_to_atom(0x3), do: :overlap + def flow_mod_failed_to_atom(0x4), do: :eperm + def flow_mod_failed_to_atom(0x5), do: :bad_timeout + def flow_mod_failed_to_atom(0x6), do: :bad_command + def flow_mod_failed_to_atom(0x7), do: :bad_flags + def flow_mod_failed_to_atom(_), do: throw(:bad_enum) + def group_mod_failed_to_int(:group_exists), do: 0x0 + def group_mod_failed_to_int(:invalid_group), do: 0x1 + def group_mod_failed_to_int(:weight_unsupported), do: 0x2 + def group_mod_failed_to_int(:out_of_groups), do: 0x3 + def group_mod_failed_to_int(:ouf_of_buckets), do: 0x4 + def group_mod_failed_to_int(:chaining_unsupported), do: 0x5 + def group_mod_failed_to_int(:watch_unsupported), do: 0x6 + def group_mod_failed_to_int(:loop), do: 0x7 + def group_mod_failed_to_int(:unknown_group), do: 0x8 + def group_mod_failed_to_int(:chained_group), do: 0x9 + def group_mod_failed_to_int(:bad_type), do: 0xA + def group_mod_failed_to_int(:bad_command), do: 0xB + def group_mod_failed_to_int(:bad_bucket), do: 0xC + def group_mod_failed_to_int(:bad_watch), do: 0xD + def group_mod_failed_to_int(:eperm), do: 0xE + def group_mod_failed_to_int(_), do: throw(:bad_enum) + def group_mod_failed_to_atom(0x0), do: :group_exists + def group_mod_failed_to_atom(0x1), do: :invalid_group + def group_mod_failed_to_atom(0x2), do: :weight_unsupported + def group_mod_failed_to_atom(0x3), do: :out_of_groups + def group_mod_failed_to_atom(0x4), do: :ouf_of_buckets + def group_mod_failed_to_atom(0x5), do: :chaining_unsupported + def group_mod_failed_to_atom(0x6), do: :watch_unsupported + def group_mod_failed_to_atom(0x7), do: :loop + def group_mod_failed_to_atom(0x8), do: :unknown_group + def group_mod_failed_to_atom(0x9), do: :chained_group + def group_mod_failed_to_atom(0xA), do: :bad_type + def group_mod_failed_to_atom(0xB), do: :bad_command + def group_mod_failed_to_atom(0xC), do: :bad_bucket + def group_mod_failed_to_atom(0xD), do: :bad_watch + def group_mod_failed_to_atom(0xE), do: :eperm + def group_mod_failed_to_atom(_), do: throw(:bad_enum) + def port_mod_failed_to_int(:bad_port), do: 0x0 + def port_mod_failed_to_int(:bad_hw_addr), do: 0x1 + def port_mod_failed_to_int(:bad_config), do: 0x2 + def port_mod_failed_to_int(:bad_advertise), do: 0x3 + def port_mod_failed_to_int(:eperm), do: 0x4 + def port_mod_failed_to_int(_), do: throw(:bad_enum) + def port_mod_failed_to_atom(0x0), do: :bad_port + def port_mod_failed_to_atom(0x1), do: :bad_hw_addr + def port_mod_failed_to_atom(0x2), do: :bad_config + def port_mod_failed_to_atom(0x3), do: :bad_advertise + def port_mod_failed_to_atom(0x4), do: :eperm + def port_mod_failed_to_atom(_), do: throw(:bad_enum) + def table_mod_failed_to_int(:bad_table), do: 0x0 + def table_mod_failed_to_int(:bad_config), do: 0x1 + def table_mod_failed_to_int(:eperm), do: 0x2 + def table_mod_failed_to_int(_), do: throw(:bad_enum) + def table_mod_failed_to_atom(0x0), do: :bad_table + def table_mod_failed_to_atom(0x1), do: :bad_config + def table_mod_failed_to_atom(0x2), do: :eperm + def table_mod_failed_to_atom(_), do: throw(:bad_enum) + def queue_op_failed_to_int(:bad_port), do: 0x0 + def queue_op_failed_to_int(:bad_queue), do: 0x1 + def queue_op_failed_to_int(:eperm), do: 0x2 + def queue_op_failed_to_int(_), do: throw(:bad_enum) + def queue_op_failed_to_atom(0x0), do: :bad_port + def queue_op_failed_to_atom(0x1), do: :bad_queue + def queue_op_failed_to_atom(0x2), do: :eperm + def queue_op_failed_to_atom(_), do: throw(:bad_enum) + def switch_config_failed_to_int(:bad_flags), do: 0x0 + def switch_config_failed_to_int(:bad_len), do: 0x1 + def switch_config_failed_to_int(:eperm), do: 0x2 + def switch_config_failed_to_int(_), do: throw(:bad_enum) + def switch_config_failed_to_atom(0x0), do: :bad_flags + def switch_config_failed_to_atom(0x1), do: :bad_len + def switch_config_failed_to_atom(0x2), do: :eperm + def switch_config_failed_to_atom(_), do: throw(:bad_enum) + def role_request_failed_to_int(:stale), do: 0x0 + def role_request_failed_to_int(:unsup), do: 0x1 + def role_request_failed_to_int(:bad_role), do: 0x2 + def role_request_failed_to_int(_), do: throw(:bad_enum) + def role_request_failed_to_atom(0x0), do: :stale + def role_request_failed_to_atom(0x1), do: :unsup + def role_request_failed_to_atom(0x2), do: :bad_role + def role_request_failed_to_atom(_), do: throw(:bad_enum) + def meter_mod_failed_to_int(:unknown), do: 0x0 + def meter_mod_failed_to_int(:meter_exists), do: 0x1 + def meter_mod_failed_to_int(:invalid_meter), do: 0x2 + def meter_mod_failed_to_int(:unknown_meter), do: 0x3 + def meter_mod_failed_to_int(:bad_command), do: 0x4 + def meter_mod_failed_to_int(:bad_flags), do: 0x5 + def meter_mod_failed_to_int(:bad_rate), do: 0x6 + def meter_mod_failed_to_int(:bad_burst), do: 0x7 + def meter_mod_failed_to_int(:bad_band), do: 0x8 + def meter_mod_failed_to_int(:bad_band_value), do: 0x9 + def meter_mod_failed_to_int(:out_of_meters), do: 0xA + def meter_mod_failed_to_int(:out_of_bands), do: 0xB + def meter_mod_failed_to_int(_), do: throw(:bad_enum) + def meter_mod_failed_to_atom(0x0), do: :unknown + def meter_mod_failed_to_atom(0x1), do: :meter_exists + def meter_mod_failed_to_atom(0x2), do: :invalid_meter + def meter_mod_failed_to_atom(0x3), do: :unknown_meter + def meter_mod_failed_to_atom(0x4), do: :bad_command + def meter_mod_failed_to_atom(0x5), do: :bad_flags + def meter_mod_failed_to_atom(0x6), do: :bad_rate + def meter_mod_failed_to_atom(0x7), do: :bad_burst + def meter_mod_failed_to_atom(0x8), do: :bad_band + def meter_mod_failed_to_atom(0x9), do: :bad_band_value + def meter_mod_failed_to_atom(0xA), do: :out_of_meters + def meter_mod_failed_to_atom(0xB), do: :out_of_bands + def meter_mod_failed_to_atom(_), do: throw(:bad_enum) + def table_features_failed_to_int(:bad_table), do: 0x0 + def table_features_failed_to_int(:bad_metadata), do: 0x1 + def table_features_failed_to_int(:bad_type), do: 0x2 + def table_features_failed_to_int(:bad_len), do: 0x3 + def table_features_failed_to_int(:bad_argument), do: 0x4 + def table_features_failed_to_int(:eperm), do: 0x5 + def table_features_failed_to_int(_), do: throw(:bad_enum) + def table_features_failed_to_atom(0x0), do: :bad_table + def table_features_failed_to_atom(0x1), do: :bad_metadata + def table_features_failed_to_atom(0x2), do: :bad_type + def table_features_failed_to_atom(0x3), do: :bad_len + def table_features_failed_to_atom(0x4), do: :bad_argument + def table_features_failed_to_atom(0x5), do: :eperm + def table_features_failed_to_atom(_), do: throw(:bad_enum) + def switch_capabilities_to_int(:flow_stats), do: 0x1 + def switch_capabilities_to_int(:table_stats), do: 0x2 + def switch_capabilities_to_int(:port_stats), do: 0x4 + def switch_capabilities_to_int(:group_stats), do: 0x8 + def switch_capabilities_to_int(:ip_reasm), do: 0x20 + def switch_capabilities_to_int(:queue_stats), do: 0x40 + def switch_capabilities_to_int(:arp_match_ip), do: 0x80 + def switch_capabilities_to_int(:port_blocked), do: 0x100 + def switch_capabilities_to_int(_), do: throw(:bad_enum) + def switch_capabilities_to_atom(0x1), do: :flow_stats + def switch_capabilities_to_atom(0x2), do: :table_stats + def switch_capabilities_to_atom(0x4), do: :port_stats + def switch_capabilities_to_atom(0x8), do: :group_stats + def switch_capabilities_to_atom(0x20), do: :ip_reasm + def switch_capabilities_to_atom(0x40), do: :queue_stats + def switch_capabilities_to_atom(0x80), do: :arp_match_ip + def switch_capabilities_to_atom(0x100), do: :port_blocked + def switch_capabilities_to_atom(_), do: throw(:bad_enum) + def config_flags_to_int(:drop), do: 0x1 + def config_flags_to_int(:reasm), do: 0x2 + def config_flags_to_int(_), do: throw(:bad_enum) + def config_flags_to_atom(0x1), do: :drop + def config_flags_to_atom(0x2), do: :reasm + def config_flags_to_atom(_), do: throw(:bad_enum) + def controller_max_len_to_int(:max), do: 0xFFE5 + def controller_max_len_to_int(:no_buffer), do: 0xFFFF + def controller_max_len_to_int(_), do: throw(:bad_enum) + def controller_max_len_to_atom(0xFFE5), do: :max + def controller_max_len_to_atom(0xFFFF), do: :no_buffer + def controller_max_len_to_atom(_), do: throw(:bad_enum) + def experimenter_oxm_vendors_to_int(:nicira_ext_match), do: 0x2320 + def experimenter_oxm_vendors_to_int(:hp_ext_match), do: 0x2428 + def experimenter_oxm_vendors_to_int(:onf_ext_match), do: 0x4F4E4600 + def experimenter_oxm_vendors_to_int(_), do: throw(:bad_enum) + def experimenter_oxm_vendors_to_atom(0x2320), do: :nicira_ext_match + def experimenter_oxm_vendors_to_atom(0x2428), do: :hp_ext_match + def experimenter_oxm_vendors_to_atom(0x4F4E4600), do: :onf_ext_match + def experimenter_oxm_vendors_to_atom(_), do: throw(:bad_enum) + def match_type_to_int(:standard), do: 0x0 + def match_type_to_int(:oxm), do: 0x1 + def match_type_to_int(_), do: throw(:bad_enum) + def match_type_to_atom(0x0), do: :standard + def match_type_to_atom(0x1), do: :oxm + def match_type_to_atom(_), do: throw(:bad_enum) + def oxm_class_to_int(:nxm_0), do: 0x0 + def oxm_class_to_int(:nxm_1), do: 0x1 + def oxm_class_to_int(:openflow_basic), do: 0x8000 + def oxm_class_to_int(:packet_register), do: 0x8001 + def oxm_class_to_int(:experimenter), do: 0xFFFF + def oxm_class_to_int(_), do: throw(:bad_enum) + def oxm_class_to_atom(0x0), do: :nxm_0 + def oxm_class_to_atom(0x1), do: :nxm_1 + def oxm_class_to_atom(0x8000), do: :openflow_basic + def oxm_class_to_atom(0x8001), do: :packet_register + def oxm_class_to_atom(0xFFFF), do: :experimenter + def oxm_class_to_atom(_), do: throw(:bad_enum) + def nxm_0_to_int(:nx_in_port), do: 0x0 + def nxm_0_to_int(:nx_eth_dst), do: 0x1 + def nxm_0_to_int(:nx_eth_src), do: 0x2 + def nxm_0_to_int(:nx_eth_type), do: 0x3 + def nxm_0_to_int(:nx_vlan_tci), do: 0x4 + def nxm_0_to_int(:nx_ip_tos), do: 0x5 + def nxm_0_to_int(:nx_ip_proto), do: 0x6 + def nxm_0_to_int(:nx_ipv4_src), do: 0x7 + def nxm_0_to_int(:nx_ipv4_dst), do: 0x8 + def nxm_0_to_int(:nx_tcp_src), do: 0x9 + def nxm_0_to_int(:nx_tcp_dst), do: 0xA + def nxm_0_to_int(:nx_udp_src), do: 0xB + def nxm_0_to_int(:nx_udp_dst), do: 0xC + def nxm_0_to_int(:nx_icmpv4_type), do: 0xD + def nxm_0_to_int(:nx_icmpv4_code), do: 0xE + def nxm_0_to_int(:nx_arp_op), do: 0xF + def nxm_0_to_int(:nx_arp_spa), do: 0x10 + def nxm_0_to_int(:nx_arp_tpa), do: 0x11 + def nxm_0_to_int(:nx_tcp_flags), do: 0x22 + def nxm_0_to_int(_), do: throw(:bad_enum) + def nxm_0_to_atom(0x0), do: :nx_in_port + def nxm_0_to_atom(0x1), do: :nx_eth_dst + def nxm_0_to_atom(0x2), do: :nx_eth_src + def nxm_0_to_atom(0x3), do: :nx_eth_type + def nxm_0_to_atom(0x4), do: :nx_vlan_tci + def nxm_0_to_atom(0x5), do: :nx_ip_tos + def nxm_0_to_atom(0x6), do: :nx_ip_proto + def nxm_0_to_atom(0x7), do: :nx_ipv4_src + def nxm_0_to_atom(0x8), do: :nx_ipv4_dst + def nxm_0_to_atom(0x9), do: :nx_tcp_src + def nxm_0_to_atom(0xA), do: :nx_tcp_dst + def nxm_0_to_atom(0xB), do: :nx_udp_src + def nxm_0_to_atom(0xC), do: :nx_udp_dst + def nxm_0_to_atom(0xD), do: :nx_icmpv4_type + def nxm_0_to_atom(0xE), do: :nx_icmpv4_code + def nxm_0_to_atom(0xF), do: :nx_arp_op + def nxm_0_to_atom(0x10), do: :nx_arp_spa + def nxm_0_to_atom(0x11), do: :nx_arp_tpa + def nxm_0_to_atom(0x22), do: :nx_tcp_flags + def nxm_0_to_atom(_), do: throw(:bad_enum) + def nxm_1_to_int(:reg0), do: 0x0 + def nxm_1_to_int(:reg1), do: 0x1 + def nxm_1_to_int(:reg2), do: 0x2 + def nxm_1_to_int(:reg3), do: 0x3 + def nxm_1_to_int(:reg4), do: 0x4 + def nxm_1_to_int(:reg5), do: 0x5 + def nxm_1_to_int(:reg6), do: 0x6 + def nxm_1_to_int(:reg7), do: 0x7 + def nxm_1_to_int(:reg8), do: 0x8 + def nxm_1_to_int(:reg9), do: 0x9 + def nxm_1_to_int(:reg10), do: 0xA + def nxm_1_to_int(:reg11), do: 0xB + def nxm_1_to_int(:reg12), do: 0xC + def nxm_1_to_int(:reg13), do: 0xD + def nxm_1_to_int(:reg14), do: 0xE + def nxm_1_to_int(:reg15), do: 0xF + def nxm_1_to_int(:tun_id), do: 0x10 + def nxm_1_to_int(:nx_arp_sha), do: 0x11 + def nxm_1_to_int(:nx_arp_tha), do: 0x12 + def nxm_1_to_int(:nx_ipv6_src), do: 0x13 + def nxm_1_to_int(:nx_ipv6_dst), do: 0x14 + def nxm_1_to_int(:nx_icmpv6_type), do: 0x15 + def nxm_1_to_int(:nx_icmpv6_code), do: 0x16 + def nxm_1_to_int(:nx_ipv6_nd_target), do: 0x17 + def nxm_1_to_int(:nx_ipv6_nd_sll), do: 0x18 + def nxm_1_to_int(:nx_ipv6_nd_tll), do: 0x19 + def nxm_1_to_int(:nx_ip_frag), do: 0x1A + def nxm_1_to_int(:nx_ipv6_label), do: 0x1B + def nxm_1_to_int(:nx_ip_ecn), do: 0x1C + def nxm_1_to_int(:nx_ip_ttl), do: 0x1D + def nxm_1_to_int(:nx_mpls_ttl), do: 0x1E + def nxm_1_to_int(:tun_src), do: 0x1F + def nxm_1_to_int(:tun_dst), do: 0x20 + def nxm_1_to_int(:pkt_mark), do: 0x21 + def nxm_1_to_int(:dp_hash), do: 0x23 + def nxm_1_to_int(:recirc_id), do: 0x24 + def nxm_1_to_int(:conj_id), do: 0x25 + def nxm_1_to_int(:tun_gbp_id), do: 0x26 + def nxm_1_to_int(:tun_gbp_flags), do: 0x27 + def nxm_1_to_int(:tun_metadata0), do: 0x28 + def nxm_1_to_int(:tun_metadata1), do: 0x29 + def nxm_1_to_int(:tun_metadata2), do: 0x2A + def nxm_1_to_int(:tun_metadata3), do: 0x2B + def nxm_1_to_int(:tun_metadata4), do: 0x2C + def nxm_1_to_int(:tun_metadata5), do: 0x2D + def nxm_1_to_int(:tun_metadata6), do: 0x2E + def nxm_1_to_int(:tun_metadata7), do: 0x2F + def nxm_1_to_int(:tun_metadata8), do: 0x30 + def nxm_1_to_int(:tun_metadata9), do: 0x31 + def nxm_1_to_int(:tun_metadata10), do: 0x32 + def nxm_1_to_int(:tun_metadata11), do: 0x33 + def nxm_1_to_int(:tun_metadata12), do: 0x34 + def nxm_1_to_int(:tun_metadata13), do: 0x35 + def nxm_1_to_int(:tun_metadata14), do: 0x36 + def nxm_1_to_int(:tun_metadata15), do: 0x37 + def nxm_1_to_int(:tun_metadata16), do: 0x38 + def nxm_1_to_int(:tun_metadata17), do: 0x39 + def nxm_1_to_int(:tun_metadata18), do: 0x3A + def nxm_1_to_int(:tun_metadata19), do: 0x3B + def nxm_1_to_int(:tun_metadata20), do: 0x3C + def nxm_1_to_int(:tun_metadata21), do: 0x3D + def nxm_1_to_int(:tun_metadata22), do: 0x3E + def nxm_1_to_int(:tun_metadata23), do: 0x3F + def nxm_1_to_int(:tun_metadata24), do: 0x40 + def nxm_1_to_int(:tun_metadata25), do: 0x41 + def nxm_1_to_int(:tun_metadata26), do: 0x42 + def nxm_1_to_int(:tun_metadata27), do: 0x43 + def nxm_1_to_int(:tun_metadata28), do: 0x44 + def nxm_1_to_int(:tun_metadata29), do: 0x45 + def nxm_1_to_int(:tun_metadata30), do: 0x46 + def nxm_1_to_int(:tun_metadata31), do: 0x47 + def nxm_1_to_int(:tun_metadata32), do: 0x48 + def nxm_1_to_int(:tun_metadata33), do: 0x49 + def nxm_1_to_int(:tun_metadata34), do: 0x4A + def nxm_1_to_int(:tun_metadata35), do: 0x4B + def nxm_1_to_int(:tun_metadata36), do: 0x4C + def nxm_1_to_int(:tun_metadata37), do: 0x4D + def nxm_1_to_int(:tun_metadata38), do: 0x4E + def nxm_1_to_int(:tun_metadata39), do: 0x4F + def nxm_1_to_int(:tun_metadata40), do: 0x50 + def nxm_1_to_int(:tun_metadata41), do: 0x51 + def nxm_1_to_int(:tun_metadata42), do: 0x52 + def nxm_1_to_int(:tun_metadata43), do: 0x53 + def nxm_1_to_int(:tun_metadata44), do: 0x54 + def nxm_1_to_int(:tun_metadata45), do: 0x55 + def nxm_1_to_int(:tun_metadata46), do: 0x56 + def nxm_1_to_int(:tun_metadata47), do: 0x57 + def nxm_1_to_int(:tun_metadata48), do: 0x58 + def nxm_1_to_int(:tun_metadata49), do: 0x59 + def nxm_1_to_int(:tun_metadata50), do: 0x5A + def nxm_1_to_int(:tun_metadata51), do: 0x5B + def nxm_1_to_int(:tun_metadata52), do: 0x5C + def nxm_1_to_int(:tun_metadata53), do: 0x5D + def nxm_1_to_int(:tun_metadata54), do: 0x5E + def nxm_1_to_int(:tun_metadata55), do: 0x5F + def nxm_1_to_int(:tun_metadata56), do: 0x60 + def nxm_1_to_int(:tun_metadata57), do: 0x61 + def nxm_1_to_int(:tun_metadata58), do: 0x62 + def nxm_1_to_int(:tun_metadata59), do: 0x63 + def nxm_1_to_int(:tun_metadata60), do: 0x64 + def nxm_1_to_int(:tun_metadata61), do: 0x65 + def nxm_1_to_int(:tun_metadata62), do: 0x66 + def nxm_1_to_int(:tun_metadata63), do: 0x67 + def nxm_1_to_int(:tun_flags), do: 0x68 + def nxm_1_to_int(:ct_state), do: 0x69 + def nxm_1_to_int(:ct_zone), do: 0x6A + def nxm_1_to_int(:ct_mark), do: 0x6B + def nxm_1_to_int(:ct_label), do: 0x6C + def nxm_1_to_int(:tun_ipv6_src), do: 0x6D + def nxm_1_to_int(:tun_ipv6_dst), do: 0x6E + def nxm_1_to_int(:xxreg0), do: 0x6F + def nxm_1_to_int(:xxreg1), do: 0x70 + def nxm_1_to_int(:xxreg2), do: 0x71 + def nxm_1_to_int(:xxreg3), do: 0x72 + def nxm_1_to_int(:xxreg4), do: 0x73 + def nxm_1_to_int(:xxreg5), do: 0x74 + def nxm_1_to_int(:xxreg6), do: 0x75 + def nxm_1_to_int(:xxreg7), do: 0x76 + def nxm_1_to_int(:ct_nw_proto), do: 0x77 + def nxm_1_to_int(:ct_nw_src), do: 0x78 + def nxm_1_to_int(:ct_nw_dst), do: 0x79 + def nxm_1_to_int(:ct_ipv6_src), do: 0x7A + def nxm_1_to_int(:ct_ipv6_dst), do: 0x7B + def nxm_1_to_int(:ct_tp_src), do: 0x7C + def nxm_1_to_int(:ct_tp_dst), do: 0x7D + def nxm_1_to_int(_), do: throw(:bad_enum) + def nxm_1_to_atom(0x0), do: :reg0 + def nxm_1_to_atom(0x1), do: :reg1 + def nxm_1_to_atom(0x2), do: :reg2 + def nxm_1_to_atom(0x3), do: :reg3 + def nxm_1_to_atom(0x4), do: :reg4 + def nxm_1_to_atom(0x5), do: :reg5 + def nxm_1_to_atom(0x6), do: :reg6 + def nxm_1_to_atom(0x7), do: :reg7 + def nxm_1_to_atom(0x8), do: :reg8 + def nxm_1_to_atom(0x9), do: :reg9 + def nxm_1_to_atom(0xA), do: :reg10 + def nxm_1_to_atom(0xB), do: :reg11 + def nxm_1_to_atom(0xC), do: :reg12 + def nxm_1_to_atom(0xD), do: :reg13 + def nxm_1_to_atom(0xE), do: :reg14 + def nxm_1_to_atom(0xF), do: :reg15 + def nxm_1_to_atom(0x10), do: :tun_id + def nxm_1_to_atom(0x11), do: :nx_arp_sha + def nxm_1_to_atom(0x12), do: :nx_arp_tha + def nxm_1_to_atom(0x13), do: :nx_ipv6_src + def nxm_1_to_atom(0x14), do: :nx_ipv6_dst + def nxm_1_to_atom(0x15), do: :nx_icmpv6_type + def nxm_1_to_atom(0x16), do: :nx_icmpv6_code + def nxm_1_to_atom(0x17), do: :nx_ipv6_nd_target + def nxm_1_to_atom(0x18), do: :nx_ipv6_nd_sll + def nxm_1_to_atom(0x19), do: :nx_ipv6_nd_tll + def nxm_1_to_atom(0x1A), do: :nx_ip_frag + def nxm_1_to_atom(0x1B), do: :nx_ipv6_label + def nxm_1_to_atom(0x1C), do: :nx_ip_ecn + def nxm_1_to_atom(0x1D), do: :nx_ip_ttl + def nxm_1_to_atom(0x1E), do: :nx_mpls_ttl + def nxm_1_to_atom(0x1F), do: :tun_src + def nxm_1_to_atom(0x20), do: :tun_dst + def nxm_1_to_atom(0x21), do: :pkt_mark + def nxm_1_to_atom(0x23), do: :dp_hash + def nxm_1_to_atom(0x24), do: :recirc_id + def nxm_1_to_atom(0x25), do: :conj_id + def nxm_1_to_atom(0x26), do: :tun_gbp_id + def nxm_1_to_atom(0x27), do: :tun_gbp_flags + def nxm_1_to_atom(0x28), do: :tun_metadata0 + def nxm_1_to_atom(0x29), do: :tun_metadata1 + def nxm_1_to_atom(0x2A), do: :tun_metadata2 + def nxm_1_to_atom(0x2B), do: :tun_metadata3 + def nxm_1_to_atom(0x2C), do: :tun_metadata4 + def nxm_1_to_atom(0x2D), do: :tun_metadata5 + def nxm_1_to_atom(0x2E), do: :tun_metadata6 + def nxm_1_to_atom(0x2F), do: :tun_metadata7 + def nxm_1_to_atom(0x30), do: :tun_metadata8 + def nxm_1_to_atom(0x31), do: :tun_metadata9 + def nxm_1_to_atom(0x32), do: :tun_metadata10 + def nxm_1_to_atom(0x33), do: :tun_metadata11 + def nxm_1_to_atom(0x34), do: :tun_metadata12 + def nxm_1_to_atom(0x35), do: :tun_metadata13 + def nxm_1_to_atom(0x36), do: :tun_metadata14 + def nxm_1_to_atom(0x37), do: :tun_metadata15 + def nxm_1_to_atom(0x38), do: :tun_metadata16 + def nxm_1_to_atom(0x39), do: :tun_metadata17 + def nxm_1_to_atom(0x3A), do: :tun_metadata18 + def nxm_1_to_atom(0x3B), do: :tun_metadata19 + def nxm_1_to_atom(0x3C), do: :tun_metadata20 + def nxm_1_to_atom(0x3D), do: :tun_metadata21 + def nxm_1_to_atom(0x3E), do: :tun_metadata22 + def nxm_1_to_atom(0x3F), do: :tun_metadata23 + def nxm_1_to_atom(0x40), do: :tun_metadata24 + def nxm_1_to_atom(0x41), do: :tun_metadata25 + def nxm_1_to_atom(0x42), do: :tun_metadata26 + def nxm_1_to_atom(0x43), do: :tun_metadata27 + def nxm_1_to_atom(0x44), do: :tun_metadata28 + def nxm_1_to_atom(0x45), do: :tun_metadata29 + def nxm_1_to_atom(0x46), do: :tun_metadata30 + def nxm_1_to_atom(0x47), do: :tun_metadata31 + def nxm_1_to_atom(0x48), do: :tun_metadata32 + def nxm_1_to_atom(0x49), do: :tun_metadata33 + def nxm_1_to_atom(0x4A), do: :tun_metadata34 + def nxm_1_to_atom(0x4B), do: :tun_metadata35 + def nxm_1_to_atom(0x4C), do: :tun_metadata36 + def nxm_1_to_atom(0x4D), do: :tun_metadata37 + def nxm_1_to_atom(0x4E), do: :tun_metadata38 + def nxm_1_to_atom(0x4F), do: :tun_metadata39 + def nxm_1_to_atom(0x50), do: :tun_metadata40 + def nxm_1_to_atom(0x51), do: :tun_metadata41 + def nxm_1_to_atom(0x52), do: :tun_metadata42 + def nxm_1_to_atom(0x53), do: :tun_metadata43 + def nxm_1_to_atom(0x54), do: :tun_metadata44 + def nxm_1_to_atom(0x55), do: :tun_metadata45 + def nxm_1_to_atom(0x56), do: :tun_metadata46 + def nxm_1_to_atom(0x57), do: :tun_metadata47 + def nxm_1_to_atom(0x58), do: :tun_metadata48 + def nxm_1_to_atom(0x59), do: :tun_metadata49 + def nxm_1_to_atom(0x5A), do: :tun_metadata50 + def nxm_1_to_atom(0x5B), do: :tun_metadata51 + def nxm_1_to_atom(0x5C), do: :tun_metadata52 + def nxm_1_to_atom(0x5D), do: :tun_metadata53 + def nxm_1_to_atom(0x5E), do: :tun_metadata54 + def nxm_1_to_atom(0x5F), do: :tun_metadata55 + def nxm_1_to_atom(0x60), do: :tun_metadata56 + def nxm_1_to_atom(0x61), do: :tun_metadata57 + def nxm_1_to_atom(0x62), do: :tun_metadata58 + def nxm_1_to_atom(0x63), do: :tun_metadata59 + def nxm_1_to_atom(0x64), do: :tun_metadata60 + def nxm_1_to_atom(0x65), do: :tun_metadata61 + def nxm_1_to_atom(0x66), do: :tun_metadata62 + def nxm_1_to_atom(0x67), do: :tun_metadata63 + def nxm_1_to_atom(0x68), do: :tun_flags + def nxm_1_to_atom(0x69), do: :ct_state + def nxm_1_to_atom(0x6A), do: :ct_zone + def nxm_1_to_atom(0x6B), do: :ct_mark + def nxm_1_to_atom(0x6C), do: :ct_label + def nxm_1_to_atom(0x6D), do: :tun_ipv6_src + def nxm_1_to_atom(0x6E), do: :tun_ipv6_dst + def nxm_1_to_atom(0x6F), do: :xxreg0 + def nxm_1_to_atom(0x70), do: :xxreg1 + def nxm_1_to_atom(0x71), do: :xxreg2 + def nxm_1_to_atom(0x72), do: :xxreg3 + def nxm_1_to_atom(0x73), do: :xxreg4 + def nxm_1_to_atom(0x74), do: :xxreg5 + def nxm_1_to_atom(0x75), do: :xxreg6 + def nxm_1_to_atom(0x76), do: :xxreg7 + def nxm_1_to_atom(0x77), do: :ct_nw_proto + def nxm_1_to_atom(0x78), do: :ct_nw_src + def nxm_1_to_atom(0x79), do: :ct_nw_dst + def nxm_1_to_atom(0x7A), do: :ct_ipv6_src + def nxm_1_to_atom(0x7B), do: :ct_ipv6_dst + def nxm_1_to_atom(0x7C), do: :ct_tp_src + def nxm_1_to_atom(0x7D), do: :ct_tp_dst + def nxm_1_to_atom(_), do: throw(:bad_enum) + def openflow_basic_to_int(:in_port), do: 0x0 + def openflow_basic_to_int(:in_phy_port), do: 0x1 + def openflow_basic_to_int(:metadata), do: 0x2 + def openflow_basic_to_int(:eth_dst), do: 0x3 + def openflow_basic_to_int(:eth_src), do: 0x4 + def openflow_basic_to_int(:eth_type), do: 0x5 + def openflow_basic_to_int(:vlan_vid), do: 0x6 + def openflow_basic_to_int(:vlan_pcp), do: 0x7 + def openflow_basic_to_int(:ip_dscp), do: 0x8 + def openflow_basic_to_int(:ip_ecn), do: 0x9 + def openflow_basic_to_int(:ip_proto), do: 0xA + def openflow_basic_to_int(:ipv4_src), do: 0xB + def openflow_basic_to_int(:ipv4_dst), do: 0xC + def openflow_basic_to_int(:tcp_src), do: 0xD + def openflow_basic_to_int(:tcp_dst), do: 0xE + def openflow_basic_to_int(:udp_src), do: 0xF + def openflow_basic_to_int(:udp_dst), do: 0x10 + def openflow_basic_to_int(:sctp_src), do: 0x11 + def openflow_basic_to_int(:sctp_dst), do: 0x12 + def openflow_basic_to_int(:icmpv4_type), do: 0x13 + def openflow_basic_to_int(:icmpv4_code), do: 0x14 + def openflow_basic_to_int(:arp_op), do: 0x15 + def openflow_basic_to_int(:arp_spa), do: 0x16 + def openflow_basic_to_int(:arp_tpa), do: 0x17 + def openflow_basic_to_int(:arp_sha), do: 0x18 + def openflow_basic_to_int(:arp_tha), do: 0x19 + def openflow_basic_to_int(:ipv6_src), do: 0x1A + def openflow_basic_to_int(:ipv6_dst), do: 0x1B + def openflow_basic_to_int(:ipv6_flabel), do: 0x1C + def openflow_basic_to_int(:icmpv6_type), do: 0x1D + def openflow_basic_to_int(:icmpv6_code), do: 0x1E + def openflow_basic_to_int(:ipv6_nd_target), do: 0x1F + def openflow_basic_to_int(:ipv6_nd_sll), do: 0x20 + def openflow_basic_to_int(:ipv6_nd_tll), do: 0x21 + def openflow_basic_to_int(:mpls_label), do: 0x22 + def openflow_basic_to_int(:mpls_tc), do: 0x23 + def openflow_basic_to_int(:mpls_bos), do: 0x24 + def openflow_basic_to_int(:pbb_isid), do: 0x25 + def openflow_basic_to_int(:tunnel_id), do: 0x26 + def openflow_basic_to_int(:ipv6_exthdr), do: 0x27 + def openflow_basic_to_int(:pbb_uca), do: 0x29 + def openflow_basic_to_int(:packet_type), do: 0x2A + def openflow_basic_to_int(:gre_flags), do: 0x2B + def openflow_basic_to_int(:gre_ver), do: 0x2C + def openflow_basic_to_int(:gre_protocol), do: 0x2D + def openflow_basic_to_int(:gre_key), do: 0x2E + def openflow_basic_to_int(:gre_seqnum), do: 0x2F + def openflow_basic_to_int(:lisp_flags), do: 0x30 + def openflow_basic_to_int(:lisp_nonce), do: 0x31 + def openflow_basic_to_int(:lisp_id), do: 0x32 + def openflow_basic_to_int(:vxlan_flags), do: 0x33 + def openflow_basic_to_int(:vxlan_vni), do: 0x34 + def openflow_basic_to_int(:mpls_data_first_nibble), do: 0x35 + def openflow_basic_to_int(:mpls_ach_version), do: 0x36 + def openflow_basic_to_int(:mpls_ach_channel), do: 0x37 + def openflow_basic_to_int(:mpls_pw_metadata), do: 0x38 + def openflow_basic_to_int(:mpls_cw_flags), do: 0x39 + def openflow_basic_to_int(:mpls_cw_fragment), do: 0x3A + def openflow_basic_to_int(:mpls_cw_len), do: 0x3B + def openflow_basic_to_int(:mpls_cw_seq_num), do: 0x3C + def openflow_basic_to_int(:gtpu_flags), do: 0x3D + def openflow_basic_to_int(:gtpu_ver), do: 0x3E + def openflow_basic_to_int(:gtpu_msg_type), do: 0x3F + def openflow_basic_to_int(:gtpu_teid), do: 0x40 + def openflow_basic_to_int(:gtpu_extn_hdr), do: 0x41 + def openflow_basic_to_int(:gtpu_extn_udp_port), do: 0x42 + def openflow_basic_to_int(:gtpu_extn_sci), do: 0x43 + def openflow_basic_to_int(_), do: throw(:bad_enum) + def openflow_basic_to_atom(0x0), do: :in_port + def openflow_basic_to_atom(0x1), do: :in_phy_port + def openflow_basic_to_atom(0x2), do: :metadata + def openflow_basic_to_atom(0x3), do: :eth_dst + def openflow_basic_to_atom(0x4), do: :eth_src + def openflow_basic_to_atom(0x5), do: :eth_type + def openflow_basic_to_atom(0x6), do: :vlan_vid + def openflow_basic_to_atom(0x7), do: :vlan_pcp + def openflow_basic_to_atom(0x8), do: :ip_dscp + def openflow_basic_to_atom(0x9), do: :ip_ecn + def openflow_basic_to_atom(0xA), do: :ip_proto + def openflow_basic_to_atom(0xB), do: :ipv4_src + def openflow_basic_to_atom(0xC), do: :ipv4_dst + def openflow_basic_to_atom(0xD), do: :tcp_src + def openflow_basic_to_atom(0xE), do: :tcp_dst + def openflow_basic_to_atom(0xF), do: :udp_src + def openflow_basic_to_atom(0x10), do: :udp_dst + def openflow_basic_to_atom(0x11), do: :sctp_src + def openflow_basic_to_atom(0x12), do: :sctp_dst + def openflow_basic_to_atom(0x13), do: :icmpv4_type + def openflow_basic_to_atom(0x14), do: :icmpv4_code + def openflow_basic_to_atom(0x15), do: :arp_op + def openflow_basic_to_atom(0x16), do: :arp_spa + def openflow_basic_to_atom(0x17), do: :arp_tpa + def openflow_basic_to_atom(0x18), do: :arp_sha + def openflow_basic_to_atom(0x19), do: :arp_tha + def openflow_basic_to_atom(0x1A), do: :ipv6_src + def openflow_basic_to_atom(0x1B), do: :ipv6_dst + def openflow_basic_to_atom(0x1C), do: :ipv6_flabel + def openflow_basic_to_atom(0x1D), do: :icmpv6_type + def openflow_basic_to_atom(0x1E), do: :icmpv6_code + def openflow_basic_to_atom(0x1F), do: :ipv6_nd_target + def openflow_basic_to_atom(0x20), do: :ipv6_nd_sll + def openflow_basic_to_atom(0x21), do: :ipv6_nd_tll + def openflow_basic_to_atom(0x22), do: :mpls_label + def openflow_basic_to_atom(0x23), do: :mpls_tc + def openflow_basic_to_atom(0x24), do: :mpls_bos + def openflow_basic_to_atom(0x25), do: :pbb_isid + def openflow_basic_to_atom(0x26), do: :tunnel_id + def openflow_basic_to_atom(0x27), do: :ipv6_exthdr + def openflow_basic_to_atom(0x29), do: :pbb_uca + def openflow_basic_to_atom(0x2A), do: :packet_type + def openflow_basic_to_atom(0x2B), do: :gre_flags + def openflow_basic_to_atom(0x2C), do: :gre_ver + def openflow_basic_to_atom(0x2D), do: :gre_protocol + def openflow_basic_to_atom(0x2E), do: :gre_key + def openflow_basic_to_atom(0x2F), do: :gre_seqnum + def openflow_basic_to_atom(0x30), do: :lisp_flags + def openflow_basic_to_atom(0x31), do: :lisp_nonce + def openflow_basic_to_atom(0x32), do: :lisp_id + def openflow_basic_to_atom(0x33), do: :vxlan_flags + def openflow_basic_to_atom(0x34), do: :vxlan_vni + def openflow_basic_to_atom(0x35), do: :mpls_data_first_nibble + def openflow_basic_to_atom(0x36), do: :mpls_ach_version + def openflow_basic_to_atom(0x37), do: :mpls_ach_channel + def openflow_basic_to_atom(0x38), do: :mpls_pw_metadata + def openflow_basic_to_atom(0x39), do: :mpls_cw_flags + def openflow_basic_to_atom(0x3A), do: :mpls_cw_fragment + def openflow_basic_to_atom(0x3B), do: :mpls_cw_len + def openflow_basic_to_atom(0x3C), do: :mpls_cw_seq_num + def openflow_basic_to_atom(0x3D), do: :gtpu_flags + def openflow_basic_to_atom(0x3E), do: :gtpu_ver + def openflow_basic_to_atom(0x3F), do: :gtpu_msg_type + def openflow_basic_to_atom(0x40), do: :gtpu_teid + def openflow_basic_to_atom(0x41), do: :gtpu_extn_hdr + def openflow_basic_to_atom(0x42), do: :gtpu_extn_udp_port + def openflow_basic_to_atom(0x43), do: :gtpu_extn_sci + def openflow_basic_to_atom(_), do: throw(:bad_enum) + def vlan_id_to_int(:present), do: 0x1000 + def vlan_id_to_int(:none), do: 0x0 + def vlan_id_to_int(_), do: throw(:bad_enum) + def vlan_id_to_atom(0x1000), do: :present + def vlan_id_to_atom(0x0), do: :none + def vlan_id_to_atom(_), do: throw(:bad_enum) + def ipv6exthdr_flags_to_int(:nonext), do: 0x1 + def ipv6exthdr_flags_to_int(:esp), do: 0x2 + def ipv6exthdr_flags_to_int(:auth), do: 0x4 + def ipv6exthdr_flags_to_int(:dest), do: 0x8 + def ipv6exthdr_flags_to_int(:frag), do: 0x10 + def ipv6exthdr_flags_to_int(:router), do: 0x20 + def ipv6exthdr_flags_to_int(:hop), do: 0x40 + def ipv6exthdr_flags_to_int(:unrep), do: 0x80 + def ipv6exthdr_flags_to_int(:unseq), do: 0x100 + def ipv6exthdr_flags_to_int(_), do: throw(:bad_enum) + def ipv6exthdr_flags_to_atom(0x1), do: :nonext + def ipv6exthdr_flags_to_atom(0x2), do: :esp + def ipv6exthdr_flags_to_atom(0x4), do: :auth + def ipv6exthdr_flags_to_atom(0x8), do: :dest + def ipv6exthdr_flags_to_atom(0x10), do: :frag + def ipv6exthdr_flags_to_atom(0x20), do: :router + def ipv6exthdr_flags_to_atom(0x40), do: :hop + def ipv6exthdr_flags_to_atom(0x80), do: :unrep + def ipv6exthdr_flags_to_atom(0x100), do: :unseq + def ipv6exthdr_flags_to_atom(_), do: throw(:bad_enum) + def tcp_flags_to_int(:fin), do: 0x1 + def tcp_flags_to_int(:syn), do: 0x2 + def tcp_flags_to_int(:rst), do: 0x4 + def tcp_flags_to_int(:psh), do: 0x8 + def tcp_flags_to_int(:ack), do: 0x10 + def tcp_flags_to_int(:urg), do: 0x20 + def tcp_flags_to_int(:ece), do: 0x40 + def tcp_flags_to_int(:cwr), do: 0x80 + def tcp_flags_to_int(:ns), do: 0x100 + def tcp_flags_to_int(_), do: throw(:bad_enum) + def tcp_flags_to_atom(0x1), do: :fin + def tcp_flags_to_atom(0x2), do: :syn + def tcp_flags_to_atom(0x4), do: :rst + def tcp_flags_to_atom(0x8), do: :psh + def tcp_flags_to_atom(0x10), do: :ack + def tcp_flags_to_atom(0x20), do: :urg + def tcp_flags_to_atom(0x40), do: :ece + def tcp_flags_to_atom(0x80), do: :cwr + def tcp_flags_to_atom(0x100), do: :ns + def tcp_flags_to_atom(_), do: throw(:bad_enum) + def ct_state_flags_to_int(:new), do: 0x1 + def ct_state_flags_to_int(:est), do: 0x2 + def ct_state_flags_to_int(:rel), do: 0x4 + def ct_state_flags_to_int(:rep), do: 0x8 + def ct_state_flags_to_int(:inv), do: 0x10 + def ct_state_flags_to_int(:trk), do: 0x20 + def ct_state_flags_to_int(:snat), do: 0x40 + def ct_state_flags_to_int(:dnat), do: 0x80 + def ct_state_flags_to_int(_), do: throw(:bad_enum) + def ct_state_flags_to_atom(0x1), do: :new + def ct_state_flags_to_atom(0x2), do: :est + def ct_state_flags_to_atom(0x4), do: :rel + def ct_state_flags_to_atom(0x8), do: :rep + def ct_state_flags_to_atom(0x10), do: :inv + def ct_state_flags_to_atom(0x20), do: :trk + def ct_state_flags_to_atom(0x40), do: :snat + def ct_state_flags_to_atom(0x80), do: :dnat + def ct_state_flags_to_atom(_), do: throw(:bad_enum) + def packet_register_to_int(:xreg0), do: 0x0 + def packet_register_to_int(:xreg1), do: 0x1 + def packet_register_to_int(:xreg2), do: 0x2 + def packet_register_to_int(:xreg3), do: 0x3 + def packet_register_to_int(:xreg4), do: 0x4 + def packet_register_to_int(:xreg5), do: 0x5 + def packet_register_to_int(:xreg6), do: 0x6 + def packet_register_to_int(:xreg7), do: 0x7 + def packet_register_to_int(_), do: throw(:bad_enum) + def packet_register_to_atom(0x0), do: :xreg0 + def packet_register_to_atom(0x1), do: :xreg1 + def packet_register_to_atom(0x2), do: :xreg2 + def packet_register_to_atom(0x3), do: :xreg3 + def packet_register_to_atom(0x4), do: :xreg4 + def packet_register_to_atom(0x5), do: :xreg5 + def packet_register_to_atom(0x6), do: :xreg6 + def packet_register_to_atom(0x7), do: :xreg7 + def packet_register_to_atom(_), do: throw(:bad_enum) + def nicira_ext_match_to_int(:nsh_flags), do: 0x1 + def nicira_ext_match_to_int(:nsh_mdtype), do: 0x2 + def nicira_ext_match_to_int(:nsh_np), do: 0x3 + def nicira_ext_match_to_int(:nsh_spi), do: 0x4 + def nicira_ext_match_to_int(:nsh_si), do: 0x5 + def nicira_ext_match_to_int(:nsh_c1), do: 0x6 + def nicira_ext_match_to_int(:nsh_c2), do: 0x7 + def nicira_ext_match_to_int(:nsh_c3), do: 0x8 + def nicira_ext_match_to_int(:nsh_c4), do: 0x9 + def nicira_ext_match_to_int(_), do: throw(:bad_enum) + def nicira_ext_match_to_atom(0x1), do: :nsh_flags + def nicira_ext_match_to_atom(0x2), do: :nsh_mdtype + def nicira_ext_match_to_atom(0x3), do: :nsh_np + def nicira_ext_match_to_atom(0x4), do: :nsh_spi + def nicira_ext_match_to_atom(0x5), do: :nsh_si + def nicira_ext_match_to_atom(0x6), do: :nsh_c1 + def nicira_ext_match_to_atom(0x7), do: :nsh_c2 + def nicira_ext_match_to_atom(0x8), do: :nsh_c3 + def nicira_ext_match_to_atom(0x9), do: :nsh_c4 + def nicira_ext_match_to_atom(_), do: throw(:bad_enum) + def hp_ext_match_to_int(:hp_udp_src_port_range), do: 0x0 + def hp_ext_match_to_int(:hp_udp_dst_port_range), do: 0x1 + def hp_ext_match_to_int(:hp_tcp_src_port_range), do: 0x2 + def hp_ext_match_to_int(:hp_tcp_dst_port_range), do: 0x3 + def hp_ext_match_to_int(:hp_tcp_flags), do: 0x4 + def hp_ext_match_to_int(:hp_custom_1), do: 0x5 + def hp_ext_match_to_int(:hp_custom_2), do: 0x6 + def hp_ext_match_to_int(:hp_custom_3), do: 0x7 + def hp_ext_match_to_int(:hp_custom_4), do: 0x8 + def hp_ext_match_to_int(_), do: throw(:bad_enum) + def hp_ext_match_to_atom(0x0), do: :hp_udp_src_port_range + def hp_ext_match_to_atom(0x1), do: :hp_udp_dst_port_range + def hp_ext_match_to_atom(0x2), do: :hp_tcp_src_port_range + def hp_ext_match_to_atom(0x3), do: :hp_tcp_dst_port_range + def hp_ext_match_to_atom(0x4), do: :hp_tcp_flags + def hp_ext_match_to_atom(0x5), do: :hp_custom_1 + def hp_ext_match_to_atom(0x6), do: :hp_custom_2 + def hp_ext_match_to_atom(0x7), do: :hp_custom_3 + def hp_ext_match_to_atom(0x8), do: :hp_custom_4 + def hp_ext_match_to_atom(_), do: throw(:bad_enum) + def hp_custom_match_type_to_int(:l2_start), do: 0x1 + def hp_custom_match_type_to_int(:l3_start), do: 0x2 + def hp_custom_match_type_to_int(:l4_start), do: 0x3 + def hp_custom_match_type_to_int(_), do: throw(:bad_enum) + def hp_custom_match_type_to_atom(0x1), do: :l2_start + def hp_custom_match_type_to_atom(0x2), do: :l3_start + def hp_custom_match_type_to_atom(0x3), do: :l4_start + def hp_custom_match_type_to_atom(_), do: throw(:bad_enum) + def onf_ext_match_to_int(:onf_tcp_flags), do: 0x2A + def onf_ext_match_to_int(:onf_actset_output), do: 0x2B + def onf_ext_match_to_int(:onf_pbb_uca), do: 0xA00 + def onf_ext_match_to_int(_), do: throw(:bad_enum) + def onf_ext_match_to_atom(0x2A), do: :onf_tcp_flags + def onf_ext_match_to_atom(0x2B), do: :onf_actset_output + def onf_ext_match_to_atom(0xA00), do: :onf_pbb_uca + def onf_ext_match_to_atom(_), do: throw(:bad_enum) + def buffer_id_to_int(:no_buffer), do: 0xFFFFFFFF + def buffer_id_to_int(_), do: throw(:bad_enum) + def buffer_id_to_atom(0xFFFFFFFF), do: :no_buffer + def buffer_id_to_atom(_), do: throw(:bad_enum) + def port_config_to_int(:port_down), do: 0x1 + def port_config_to_int(:no_receive), do: 0x4 + def port_config_to_int(:no_forward), do: 0x20 + def port_config_to_int(:no_packet_in), do: 0x40 + def port_config_to_int(_), do: throw(:bad_enum) + def port_config_to_atom(0x1), do: :port_down + def port_config_to_atom(0x4), do: :no_receive + def port_config_to_atom(0x20), do: :no_forward + def port_config_to_atom(0x40), do: :no_packet_in + def port_config_to_atom(_), do: throw(:bad_enum) + def port_state_to_int(:link_down), do: 0x1 + def port_state_to_int(:blocked), do: 0x2 + def port_state_to_int(:live), do: 0x4 + def port_state_to_int(_), do: throw(:bad_enum) + def port_state_to_atom(0x1), do: :link_down + def port_state_to_atom(0x2), do: :blocked + def port_state_to_atom(0x4), do: :live + def port_state_to_atom(_), do: throw(:bad_enum) + def port_features_to_int(:"10mb_hd"), do: 0x1 + def port_features_to_int(:"10mb_fd"), do: 0x2 + def port_features_to_int(:"100mb_hd"), do: 0x4 + def port_features_to_int(:"100mb_fd"), do: 0x8 + def port_features_to_int(:"1gb_hd"), do: 0x10 + def port_features_to_int(:"1gb_fd"), do: 0x20 + def port_features_to_int(:"10gb_fd"), do: 0x40 + def port_features_to_int(:"40gb_fd"), do: 0x80 + def port_features_to_int(:"100gb_fd"), do: 0x100 + def port_features_to_int(:"1tb_fd"), do: 0x200 + def port_features_to_int(:other), do: 0x400 + def port_features_to_int(:copper), do: 0x800 + def port_features_to_int(:fiber), do: 0x1000 + def port_features_to_int(:autoneg), do: 0x2000 + def port_features_to_int(:pause), do: 0x4000 + def port_features_to_int(:pause_asym), do: 0x8000 + def port_features_to_int(_), do: throw(:bad_enum) + def port_features_to_atom(0x1), do: :"10mb_hd" + def port_features_to_atom(0x2), do: :"10mb_fd" + def port_features_to_atom(0x4), do: :"100mb_hd" + def port_features_to_atom(0x8), do: :"100mb_fd" + def port_features_to_atom(0x10), do: :"1gb_hd" + def port_features_to_atom(0x20), do: :"1gb_fd" + def port_features_to_atom(0x40), do: :"10gb_fd" + def port_features_to_atom(0x80), do: :"40gb_fd" + def port_features_to_atom(0x100), do: :"100gb_fd" + def port_features_to_atom(0x200), do: :"1tb_fd" + def port_features_to_atom(0x400), do: :other + def port_features_to_atom(0x800), do: :copper + def port_features_to_atom(0x1000), do: :fiber + def port_features_to_atom(0x2000), do: :autoneg + def port_features_to_atom(0x4000), do: :pause + def port_features_to_atom(0x8000), do: :pause_asym + def port_features_to_atom(_), do: throw(:bad_enum) + def openflow10_port_no_to_int(:max), do: 0xFF00 + def openflow10_port_no_to_int(:in_port), do: 0xFFF8 + def openflow10_port_no_to_int(:table), do: 0xFFF9 + def openflow10_port_no_to_int(:normal), do: 0xFFFA + def openflow10_port_no_to_int(:flood), do: 0xFFFB + def openflow10_port_no_to_int(:all), do: 0xFFFC + def openflow10_port_no_to_int(:controller), do: 0xFFFD + def openflow10_port_no_to_int(:local), do: 0xFFFE + def openflow10_port_no_to_int(:none), do: 0xFFFF + def openflow10_port_no_to_int(_), do: throw(:bad_enum) + def openflow10_port_no_to_atom(0xFF00), do: :max + def openflow10_port_no_to_atom(0xFFF8), do: :in_port + def openflow10_port_no_to_atom(0xFFF9), do: :table + def openflow10_port_no_to_atom(0xFFFA), do: :normal + def openflow10_port_no_to_atom(0xFFFB), do: :flood + def openflow10_port_no_to_atom(0xFFFC), do: :all + def openflow10_port_no_to_atom(0xFFFD), do: :controller + def openflow10_port_no_to_atom(0xFFFE), do: :local + def openflow10_port_no_to_atom(0xFFFF), do: :none + def openflow10_port_no_to_atom(_), do: throw(:bad_enum) + def openflow13_port_no_to_int(:max), do: 0xFFFFFF00 + def openflow13_port_no_to_int(:in_port), do: 0xFFFFFFF8 + def openflow13_port_no_to_int(:table), do: 0xFFFFFFF9 + def openflow13_port_no_to_int(:normal), do: 0xFFFFFFFA + def openflow13_port_no_to_int(:flood), do: 0xFFFFFFFB + def openflow13_port_no_to_int(:all), do: 0xFFFFFFFC + def openflow13_port_no_to_int(:controller), do: 0xFFFFFFFD + def openflow13_port_no_to_int(:local), do: 0xFFFFFFFE + def openflow13_port_no_to_int(:any), do: 0xFFFFFFFF + def openflow13_port_no_to_int(_), do: throw(:bad_enum) + def openflow13_port_no_to_atom(0xFFFFFF00), do: :max + def openflow13_port_no_to_atom(0xFFFFFFF8), do: :in_port + def openflow13_port_no_to_atom(0xFFFFFFF9), do: :table + def openflow13_port_no_to_atom(0xFFFFFFFA), do: :normal + def openflow13_port_no_to_atom(0xFFFFFFFB), do: :flood + def openflow13_port_no_to_atom(0xFFFFFFFC), do: :all + def openflow13_port_no_to_atom(0xFFFFFFFD), do: :controller + def openflow13_port_no_to_atom(0xFFFFFFFE), do: :local + def openflow13_port_no_to_atom(0xFFFFFFFF), do: :any + def openflow13_port_no_to_atom(_), do: throw(:bad_enum) + def packet_in_reason_to_int(:no_match), do: 0x0 + def packet_in_reason_to_int(:action), do: 0x1 + def packet_in_reason_to_int(:invalid_ttl), do: 0x2 + def packet_in_reason_to_int(:action_set), do: 0x3 + def packet_in_reason_to_int(:group), do: 0x4 + def packet_in_reason_to_int(:packet_out), do: 0x5 + def packet_in_reason_to_int(_), do: throw(:bad_enum) + def packet_in_reason_to_atom(0x0), do: :no_match + def packet_in_reason_to_atom(0x1), do: :action + def packet_in_reason_to_atom(0x2), do: :invalid_ttl + def packet_in_reason_to_atom(0x3), do: :action_set + def packet_in_reason_to_atom(0x4), do: :group + def packet_in_reason_to_atom(0x5), do: :packet_out + def packet_in_reason_to_atom(_), do: throw(:bad_enum) + def flow_mod_command_to_int(:add), do: 0x0 + def flow_mod_command_to_int(:modify), do: 0x1 + def flow_mod_command_to_int(:modify_strict), do: 0x2 + def flow_mod_command_to_int(:delete), do: 0x3 + def flow_mod_command_to_int(:delete_strict), do: 0x4 + def flow_mod_command_to_int(_), do: throw(:bad_enum) + def flow_mod_command_to_atom(0x0), do: :add + def flow_mod_command_to_atom(0x1), do: :modify + def flow_mod_command_to_atom(0x2), do: :modify_strict + def flow_mod_command_to_atom(0x3), do: :delete + def flow_mod_command_to_atom(0x4), do: :delete_strict + def flow_mod_command_to_atom(_), do: throw(:bad_enum) + def flow_mod_flags_to_int(:send_flow_rem), do: 0x1 + def flow_mod_flags_to_int(:check_overlap), do: 0x2 + def flow_mod_flags_to_int(:reset_counts), do: 0x4 + def flow_mod_flags_to_int(:no_packet_counts), do: 0x8 + def flow_mod_flags_to_int(:no_byte_counts), do: 0x10 + def flow_mod_flags_to_int(_), do: throw(:bad_enum) + def flow_mod_flags_to_atom(0x1), do: :send_flow_rem + def flow_mod_flags_to_atom(0x2), do: :check_overlap + def flow_mod_flags_to_atom(0x4), do: :reset_counts + def flow_mod_flags_to_atom(0x8), do: :no_packet_counts + def flow_mod_flags_to_atom(0x10), do: :no_byte_counts + def flow_mod_flags_to_atom(_), do: throw(:bad_enum) + def flow_removed_reason_to_int(:idle_timeout), do: 0x0 + def flow_removed_reason_to_int(:hard_timeout), do: 0x1 + def flow_removed_reason_to_int(:delete), do: 0x2 + def flow_removed_reason_to_int(:group_delete), do: 0x3 + def flow_removed_reason_to_int(:meter_delete), do: 0x4 + def flow_removed_reason_to_int(:eviction), do: 0x5 + def flow_removed_reason_to_int(_), do: throw(:bad_enum) + def flow_removed_reason_to_atom(0x0), do: :idle_timeout + def flow_removed_reason_to_atom(0x1), do: :hard_timeout + def flow_removed_reason_to_atom(0x2), do: :delete + def flow_removed_reason_to_atom(0x3), do: :group_delete + def flow_removed_reason_to_atom(0x4), do: :meter_delete + def flow_removed_reason_to_atom(0x5), do: :eviction + def flow_removed_reason_to_atom(_), do: throw(:bad_enum) + def port_reason_to_int(:add), do: 0x0 + def port_reason_to_int(:delete), do: 0x1 + def port_reason_to_int(:modify), do: 0x2 + def port_reason_to_int(_), do: throw(:bad_enum) + def port_reason_to_atom(0x0), do: :add + def port_reason_to_atom(0x1), do: :delete + def port_reason_to_atom(0x2), do: :modify + def port_reason_to_atom(_), do: throw(:bad_enum) + def group_mod_command_to_int(:add), do: 0x0 + def group_mod_command_to_int(:modify), do: 0x1 + def group_mod_command_to_int(:delete), do: 0x2 + def group_mod_command_to_int(_), do: throw(:bad_enum) + def group_mod_command_to_atom(0x0), do: :add + def group_mod_command_to_atom(0x1), do: :modify + def group_mod_command_to_atom(0x2), do: :delete + def group_mod_command_to_atom(_), do: throw(:bad_enum) + def group_type_to_int(:all), do: 0x0 + def group_type_to_int(:select), do: 0x1 + def group_type_to_int(:indirect), do: 0x2 + def group_type_to_int(:fast_failover), do: 0x3 + def group_type_to_int(_), do: throw(:bad_enum) + def group_type_to_atom(0x0), do: :all + def group_type_to_atom(0x1), do: :select + def group_type_to_atom(0x2), do: :indirect + def group_type_to_atom(0x3), do: :fast_failover + def group_type_to_atom(_), do: throw(:bad_enum) + def group_id_to_int(:max), do: 0xFFFFFF00 + def group_id_to_int(:all), do: 0xFFFFFFFC + def group_id_to_int(:any), do: 0xFFFFFFFF + def group_id_to_int(_), do: throw(:bad_enum) + def group_id_to_atom(0xFFFFFF00), do: :max + def group_id_to_atom(0xFFFFFFFC), do: :all + def group_id_to_atom(0xFFFFFFFF), do: :any + def group_id_to_atom(_), do: throw(:bad_enum) + def group_capabilities_to_int(:select_weight), do: 0x1 + def group_capabilities_to_int(:select_liveness), do: 0x2 + def group_capabilities_to_int(:chaining), do: 0x4 + def group_capabilities_to_int(:chaining_checks), do: 0x8 + def group_capabilities_to_int(_), do: throw(:bad_enum) + def group_capabilities_to_atom(0x1), do: :select_weight + def group_capabilities_to_atom(0x2), do: :select_liveness + def group_capabilities_to_atom(0x4), do: :chaining + def group_capabilities_to_atom(0x8), do: :chaining_checks + def group_capabilities_to_atom(_), do: throw(:bad_enum) + def table_id_to_int(:max), do: 0xFE + def table_id_to_int(:all), do: 0xFF + def table_id_to_int(_), do: throw(:bad_enum) + def table_id_to_atom(0xFE), do: :max + def table_id_to_atom(0xFF), do: :all + def table_id_to_atom(_), do: throw(:bad_enum) + def queue_id_to_int(:all), do: 0xFFFFFFFF + def queue_id_to_int(_), do: throw(:bad_enum) + def queue_id_to_atom(0xFFFFFFFF), do: :all + def queue_id_to_atom(_), do: throw(:bad_enum) + def meter_mod_command_to_int(:add), do: 0x0 + def meter_mod_command_to_int(:modify), do: 0x1 + def meter_mod_command_to_int(:delete), do: 0x2 + def meter_mod_command_to_int(_), do: throw(:bad_enum) + def meter_mod_command_to_atom(0x0), do: :add + def meter_mod_command_to_atom(0x1), do: :modify + def meter_mod_command_to_atom(0x2), do: :delete + def meter_mod_command_to_atom(_), do: throw(:bad_enum) + def meter_id_to_int(:max), do: 0xFFFF0000 + def meter_id_to_int(:slowpath), do: 0xFFFFFFFD + def meter_id_to_int(:controller), do: 0xFFFFFFFE + def meter_id_to_int(:all), do: 0xFFFFFFFF + def meter_id_to_int(_), do: throw(:bad_enum) + def meter_id_to_atom(0xFFFF0000), do: :max + def meter_id_to_atom(0xFFFFFFFD), do: :slowpath + def meter_id_to_atom(0xFFFFFFFE), do: :controller + def meter_id_to_atom(0xFFFFFFFF), do: :all + def meter_id_to_atom(_), do: throw(:bad_enum) + def meter_flags_to_int(:kbps), do: 0x1 + def meter_flags_to_int(:pktps), do: 0x2 + def meter_flags_to_int(:burst), do: 0x4 + def meter_flags_to_int(:stats), do: 0x8 + def meter_flags_to_int(_), do: throw(:bad_enum) + def meter_flags_to_atom(0x1), do: :kbps + def meter_flags_to_atom(0x2), do: :pktps + def meter_flags_to_atom(0x4), do: :burst + def meter_flags_to_atom(0x8), do: :stats + def meter_flags_to_atom(_), do: throw(:bad_enum) + def meter_band_type_to_int(Openflow.MeterBand.Drop), do: 0x1 + def meter_band_type_to_int(Openflow.MeterBand.Remark), do: 0x2 + def meter_band_type_to_int(Openflow.MeterBand.Experimenter), do: 0xFFFF + def meter_band_type_to_int(_), do: throw(:bad_enum) + def meter_band_type_to_atom(0x1), do: Openflow.MeterBand.Drop + def meter_band_type_to_atom(0x2), do: Openflow.MeterBand.Remark + def meter_band_type_to_atom(0xFFFF), do: Openflow.MeterBand.Experimenter + def meter_band_type_to_atom(_), do: throw(:bad_enum) + def table_config_to_int(:table_miss_controller), do: 0x0 + def table_config_to_int(:table_miss_continue), do: 0x1 + def table_config_to_int(:table_miss_drop), do: 0x2 + def table_config_to_int(:table_miss_mask), do: 0x3 + def table_config_to_int(:eviction), do: 0x4 + def table_config_to_int(:vacancy_events), do: 0x8 + def table_config_to_int(_), do: throw(:bad_enum) + def table_config_to_atom(0x0), do: :table_miss_controller + def table_config_to_atom(0x1), do: :table_miss_continue + def table_config_to_atom(0x2), do: :table_miss_drop + def table_config_to_atom(0x3), do: :table_miss_mask + def table_config_to_atom(0x4), do: :eviction + def table_config_to_atom(0x8), do: :vacancy_events + def table_config_to_atom(_), do: throw(:bad_enum) + def action_type_to_int(Openflow.Action.Output), do: 0x0 + def action_type_to_int(Openflow.Action.CopyTtlOut), do: 0xB + def action_type_to_int(Openflow.Action.CopyTtlIn), do: 0xC + def action_type_to_int(Openflow.Action.SetMplsTtl), do: 0xF + def action_type_to_int(Openflow.Action.DecMplsTtl), do: 0x10 + def action_type_to_int(Openflow.Action.PushVlan), do: 0x11 + def action_type_to_int(Openflow.Action.PopVlan), do: 0x12 + def action_type_to_int(Openflow.Action.PushMpls), do: 0x13 + def action_type_to_int(Openflow.Action.PopMpls), do: 0x14 + def action_type_to_int(Openflow.Action.SetQueue), do: 0x15 + def action_type_to_int(Openflow.Action.Group), do: 0x16 + def action_type_to_int(Openflow.Action.SetNwTtl), do: 0x17 + def action_type_to_int(Openflow.Action.DecNwTtl), do: 0x18 + def action_type_to_int(Openflow.Action.SetField), do: 0x19 + def action_type_to_int(Openflow.Action.PushPbb), do: 0x1A + def action_type_to_int(Openflow.Action.PopPbb), do: 0x1B + def action_type_to_int(Openflow.Action.Encap), do: 0x1C + def action_type_to_int(Openflow.Action.Decap), do: 0x1D + def action_type_to_int(Openflow.Action.SetSequence), do: 0x1E + def action_type_to_int(Openflow.Action.ValidateSequence), do: 0x1F + def action_type_to_int(Openflow.Action.Experimenter), do: 0xFFFF + def action_type_to_int(_), do: throw(:bad_enum) + def action_type_to_atom(0x0), do: Openflow.Action.Output + def action_type_to_atom(0xB), do: Openflow.Action.CopyTtlOut + def action_type_to_atom(0xC), do: Openflow.Action.CopyTtlIn + def action_type_to_atom(0xF), do: Openflow.Action.SetMplsTtl + def action_type_to_atom(0x10), do: Openflow.Action.DecMplsTtl + def action_type_to_atom(0x11), do: Openflow.Action.PushVlan + def action_type_to_atom(0x12), do: Openflow.Action.PopVlan + def action_type_to_atom(0x13), do: Openflow.Action.PushMpls + def action_type_to_atom(0x14), do: Openflow.Action.PopMpls + def action_type_to_atom(0x15), do: Openflow.Action.SetQueue + def action_type_to_atom(0x16), do: Openflow.Action.Group + def action_type_to_atom(0x17), do: Openflow.Action.SetNwTtl + def action_type_to_atom(0x18), do: Openflow.Action.DecNwTtl + def action_type_to_atom(0x19), do: Openflow.Action.SetField + def action_type_to_atom(0x1A), do: Openflow.Action.PushPbb + def action_type_to_atom(0x1B), do: Openflow.Action.PopPbb + def action_type_to_atom(0x1C), do: Openflow.Action.Encap + def action_type_to_atom(0x1D), do: Openflow.Action.Decap + def action_type_to_atom(0x1E), do: Openflow.Action.SetSequence + def action_type_to_atom(0x1F), do: Openflow.Action.ValidateSequence + def action_type_to_atom(0xFFFF), do: Openflow.Action.Experimenter + def action_type_to_atom(_), do: throw(:bad_enum) + def action_vendor_to_int(:nicira_ext_action), do: 0x2320 + def action_vendor_to_int(:onf_ext_action), do: 0x4F4E4600 + def action_vendor_to_int(_), do: throw(:bad_enum) + def action_vendor_to_atom(0x2320), do: :nicira_ext_action + def action_vendor_to_atom(0x4F4E4600), do: :onf_ext_action + def action_vendor_to_atom(_), do: throw(:bad_enum) + def onf_ext_action_to_int(Openflow.Action.OnfCopyField), do: 0xC80 + def onf_ext_action_to_int(_), do: throw(:bad_enum) + def onf_ext_action_to_atom(0xC80), do: Openflow.Action.OnfCopyField + def onf_ext_action_to_atom(_), do: throw(:bad_enum) + def nicira_ext_action_to_int(Openflow.Action.NxResubmit), do: 0x1 + def nicira_ext_action_to_int(Openflow.Action.NxSetTunnel), do: 0x2 + def nicira_ext_action_to_int(Openflow.Action.NxSetQueue), do: 0x4 + def nicira_ext_action_to_int(Openflow.Action.NxPopQueue), do: 0x5 + def nicira_ext_action_to_int(Openflow.Action.NxRegMove), do: 0x6 + def nicira_ext_action_to_int(Openflow.Action.NxRegLoad), do: 0x7 + def nicira_ext_action_to_int(Openflow.Action.NxNote), do: 0x8 + def nicira_ext_action_to_int(Openflow.Action.NxSetTunnel64), do: 0x9 + def nicira_ext_action_to_int(Openflow.Action.NxMultipath), do: 0xA + def nicira_ext_action_to_int(Openflow.Action.NxBundle), do: 0xC + def nicira_ext_action_to_int(Openflow.Action.NxBundleLoad), do: 0xD + def nicira_ext_action_to_int(Openflow.Action.NxResubmitTable), do: 0xE + def nicira_ext_action_to_int(Openflow.Action.NxOutputReg), do: 0xF + def nicira_ext_action_to_int(Openflow.Action.NxLearn), do: 0x10 + def nicira_ext_action_to_int(Openflow.Action.NxExit), do: 0x11 + def nicira_ext_action_to_int(Openflow.Action.NxDecTtl), do: 0x12 + def nicira_ext_action_to_int(Openflow.Action.NxFinTimeout), do: 0x13 + def nicira_ext_action_to_int(Openflow.Action.NxController), do: 0x14 + def nicira_ext_action_to_int(Openflow.Action.NxDecTtlCntIds), do: 0x15 + def nicira_ext_action_to_int(Openflow.Action.NxWriteMetadata), do: 0x16 + def nicira_ext_action_to_int(Openflow.Action.NxPushMpls), do: 0x17 + def nicira_ext_action_to_int(Openflow.Action.NxPopMpls), do: 0x18 + def nicira_ext_action_to_int(Openflow.Action.NxSetMplsTtl), do: 0x19 + def nicira_ext_action_to_int(Openflow.Action.NxDecMplsTtl), do: 0x1A + def nicira_ext_action_to_int(Openflow.Action.NxStackPush), do: 0x1B + def nicira_ext_action_to_int(Openflow.Action.NxStackPop), do: 0x1C + def nicira_ext_action_to_int(Openflow.Action.NxSample), do: 0x1D + def nicira_ext_action_to_int(Openflow.Action.NxSetMplsLabel), do: 0x1E + def nicira_ext_action_to_int(Openflow.Action.NxSetMplsTc), do: 0x1F + def nicira_ext_action_to_int(Openflow.Action.NxOutputReg2), do: 0x20 + def nicira_ext_action_to_int(Openflow.Action.NxRegLoad2), do: 0x21 + def nicira_ext_action_to_int(Openflow.Action.NxConjunction), do: 0x22 + def nicira_ext_action_to_int(Openflow.Action.NxConntrack), do: 0x23 + def nicira_ext_action_to_int(Openflow.Action.NxNat), do: 0x24 + def nicira_ext_action_to_int(Openflow.Action.NxController2), do: 0x25 + def nicira_ext_action_to_int(Openflow.Action.NxSample2), do: 0x26 + def nicira_ext_action_to_int(Openflow.Action.NxOutputTrunc), do: 0x27 + def nicira_ext_action_to_int(Openflow.Action.NxGroup), do: 0x28 + def nicira_ext_action_to_int(Openflow.Action.NxSample3), do: 0x29 + def nicira_ext_action_to_int(Openflow.Action.NxClone), do: 0x2A + def nicira_ext_action_to_int(Openflow.Action.NxCtClear), do: 0x2B + def nicira_ext_action_to_int(Openflow.Action.NxResubmitTableCt), do: 0x2C + def nicira_ext_action_to_int(Openflow.Action.NxLearn2), do: 0x2D + def nicira_ext_action_to_int(Openflow.Action.NxEncap), do: 0x2E + def nicira_ext_action_to_int(Openflow.Action.NxDecap), do: 0x2F + def nicira_ext_action_to_int(Openflow.Action.NxDebugRecirc), do: 0xFF + def nicira_ext_action_to_int(_), do: throw(:bad_enum) + def nicira_ext_action_to_atom(0x1), do: Openflow.Action.NxResubmit + def nicira_ext_action_to_atom(0x2), do: Openflow.Action.NxSetTunnel + def nicira_ext_action_to_atom(0x4), do: Openflow.Action.NxSetQueue + def nicira_ext_action_to_atom(0x5), do: Openflow.Action.NxPopQueue + def nicira_ext_action_to_atom(0x6), do: Openflow.Action.NxRegMove + def nicira_ext_action_to_atom(0x7), do: Openflow.Action.NxRegLoad + def nicira_ext_action_to_atom(0x8), do: Openflow.Action.NxNote + def nicira_ext_action_to_atom(0x9), do: Openflow.Action.NxSetTunnel64 + def nicira_ext_action_to_atom(0xA), do: Openflow.Action.NxMultipath + def nicira_ext_action_to_atom(0xC), do: Openflow.Action.NxBundle + def nicira_ext_action_to_atom(0xD), do: Openflow.Action.NxBundleLoad + def nicira_ext_action_to_atom(0xE), do: Openflow.Action.NxResubmitTable + def nicira_ext_action_to_atom(0xF), do: Openflow.Action.NxOutputReg + def nicira_ext_action_to_atom(0x10), do: Openflow.Action.NxLearn + def nicira_ext_action_to_atom(0x11), do: Openflow.Action.NxExit + def nicira_ext_action_to_atom(0x12), do: Openflow.Action.NxDecTtl + def nicira_ext_action_to_atom(0x13), do: Openflow.Action.NxFinTimeout + def nicira_ext_action_to_atom(0x14), do: Openflow.Action.NxController + def nicira_ext_action_to_atom(0x15), do: Openflow.Action.NxDecTtlCntIds + def nicira_ext_action_to_atom(0x16), do: Openflow.Action.NxWriteMetadata + def nicira_ext_action_to_atom(0x17), do: Openflow.Action.NxPushMpls + def nicira_ext_action_to_atom(0x18), do: Openflow.Action.NxPopMpls + def nicira_ext_action_to_atom(0x19), do: Openflow.Action.NxSetMplsTtl + def nicira_ext_action_to_atom(0x1A), do: Openflow.Action.NxDecMplsTtl + def nicira_ext_action_to_atom(0x1B), do: Openflow.Action.NxStackPush + def nicira_ext_action_to_atom(0x1C), do: Openflow.Action.NxStackPop + def nicira_ext_action_to_atom(0x1D), do: Openflow.Action.NxSample + def nicira_ext_action_to_atom(0x1E), do: Openflow.Action.NxSetMplsLabel + def nicira_ext_action_to_atom(0x1F), do: Openflow.Action.NxSetMplsTc + def nicira_ext_action_to_atom(0x20), do: Openflow.Action.NxOutputReg2 + def nicira_ext_action_to_atom(0x21), do: Openflow.Action.NxRegLoad2 + def nicira_ext_action_to_atom(0x22), do: Openflow.Action.NxConjunction + def nicira_ext_action_to_atom(0x23), do: Openflow.Action.NxConntrack + def nicira_ext_action_to_atom(0x24), do: Openflow.Action.NxNat + def nicira_ext_action_to_atom(0x25), do: Openflow.Action.NxController2 + def nicira_ext_action_to_atom(0x26), do: Openflow.Action.NxSample2 + def nicira_ext_action_to_atom(0x27), do: Openflow.Action.NxOutputTrunc + def nicira_ext_action_to_atom(0x28), do: Openflow.Action.NxGroup + def nicira_ext_action_to_atom(0x29), do: Openflow.Action.NxSample3 + def nicira_ext_action_to_atom(0x2A), do: Openflow.Action.NxClone + def nicira_ext_action_to_atom(0x2B), do: Openflow.Action.NxCtClear + def nicira_ext_action_to_atom(0x2C), do: Openflow.Action.NxResubmitTableCt + def nicira_ext_action_to_atom(0x2D), do: Openflow.Action.NxLearn2 + def nicira_ext_action_to_atom(0x2E), do: Openflow.Action.NxEncap + def nicira_ext_action_to_atom(0x2F), do: Openflow.Action.NxDecap + def nicira_ext_action_to_atom(0xFF), do: Openflow.Action.NxDebugRecirc + def nicira_ext_action_to_atom(_), do: throw(:bad_enum) + def nx_mp_algorithm_to_int(:modulo_n), do: 0x0 + def nx_mp_algorithm_to_int(:hash_threshold), do: 0x1 + def nx_mp_algorithm_to_int(:highest_random_weight), do: 0x2 + def nx_mp_algorithm_to_int(:iterative_hash), do: 0x3 + def nx_mp_algorithm_to_int(_), do: throw(:bad_enum) + def nx_mp_algorithm_to_atom(0x0), do: :modulo_n + def nx_mp_algorithm_to_atom(0x1), do: :hash_threshold + def nx_mp_algorithm_to_atom(0x2), do: :highest_random_weight + def nx_mp_algorithm_to_atom(0x3), do: :iterative_hash + def nx_mp_algorithm_to_atom(_), do: throw(:bad_enum) + def nx_hash_fields_to_int(:eth_src), do: 0x0 + def nx_hash_fields_to_int(:symmetric_l4), do: 0x1 + def nx_hash_fields_to_int(:symmetric_l3l4), do: 0x2 + def nx_hash_fields_to_int(:symmetric_l3l4_udp), do: 0x3 + def nx_hash_fields_to_int(:nw_src), do: 0x4 + def nx_hash_fields_to_int(:nw_dst), do: 0x5 + def nx_hash_fields_to_int(_), do: throw(:bad_enum) + def nx_hash_fields_to_atom(0x0), do: :eth_src + def nx_hash_fields_to_atom(0x1), do: :symmetric_l4 + def nx_hash_fields_to_atom(0x2), do: :symmetric_l3l4 + def nx_hash_fields_to_atom(0x3), do: :symmetric_l3l4_udp + def nx_hash_fields_to_atom(0x4), do: :nw_src + def nx_hash_fields_to_atom(0x5), do: :nw_dst + def nx_hash_fields_to_atom(_), do: throw(:bad_enum) + def nx_bd_algorithm_to_int(:active_backup), do: 0x0 + def nx_bd_algorithm_to_int(:highest_random_weight), do: 0x1 + def nx_bd_algorithm_to_int(_), do: throw(:bad_enum) + def nx_bd_algorithm_to_atom(0x0), do: :active_backup + def nx_bd_algorithm_to_atom(0x1), do: :highest_random_weight + def nx_bd_algorithm_to_atom(_), do: throw(:bad_enum) + def nx_learn_flag_to_int(:send_flow_rem), do: 0x1 + def nx_learn_flag_to_int(:delete_learned), do: 0x2 + def nx_learn_flag_to_int(:write_result), do: 0x4 + def nx_learn_flag_to_int(_), do: throw(:bad_enum) + def nx_learn_flag_to_atom(0x1), do: :send_flow_rem + def nx_learn_flag_to_atom(0x2), do: :delete_learned + def nx_learn_flag_to_atom(0x4), do: :write_result + def nx_learn_flag_to_atom(_), do: throw(:bad_enum) + def nx_conntrack_flags_to_int(:commit), do: 0x1 + def nx_conntrack_flags_to_int(:force), do: 0x2 + def nx_conntrack_flags_to_int(_), do: throw(:bad_enum) + def nx_conntrack_flags_to_atom(0x1), do: :commit + def nx_conntrack_flags_to_atom(0x2), do: :force + def nx_conntrack_flags_to_atom(_), do: throw(:bad_enum) + def nx_nat_flags_to_int(:src), do: 0x1 + def nx_nat_flags_to_int(:dst), do: 0x2 + def nx_nat_flags_to_int(:persistent), do: 0x4 + def nx_nat_flags_to_int(:protocol_hash), do: 0x8 + def nx_nat_flags_to_int(:protocol_random), do: 0x10 + def nx_nat_flags_to_int(_), do: throw(:bad_enum) + def nx_nat_flags_to_atom(0x1), do: :src + def nx_nat_flags_to_atom(0x2), do: :dst + def nx_nat_flags_to_atom(0x4), do: :persistent + def nx_nat_flags_to_atom(0x8), do: :protocol_hash + def nx_nat_flags_to_atom(0x10), do: :protocol_random + def nx_nat_flags_to_atom(_), do: throw(:bad_enum) + def nx_nat_range_to_int(:ipv4_min), do: 0x1 + def nx_nat_range_to_int(:ipv4_max), do: 0x2 + def nx_nat_range_to_int(:ipv6_min), do: 0x4 + def nx_nat_range_to_int(:ipv6_max), do: 0x8 + def nx_nat_range_to_int(:proto_min), do: 0x10 + def nx_nat_range_to_int(:proto_max), do: 0x20 + def nx_nat_range_to_int(_), do: throw(:bad_enum) + def nx_nat_range_to_atom(0x1), do: :ipv4_min + def nx_nat_range_to_atom(0x2), do: :ipv4_max + def nx_nat_range_to_atom(0x4), do: :ipv6_min + def nx_nat_range_to_atom(0x8), do: :ipv6_max + def nx_nat_range_to_atom(0x10), do: :proto_min + def nx_nat_range_to_atom(0x20), do: :proto_max + def nx_nat_range_to_atom(_), do: throw(:bad_enum) + def nx_action_controller2_prop_type_to_int(:max_len), do: 0x0 + def nx_action_controller2_prop_type_to_int(:controller_id), do: 0x1 + def nx_action_controller2_prop_type_to_int(:reason), do: 0x2 + def nx_action_controller2_prop_type_to_int(:userdata), do: 0x3 + def nx_action_controller2_prop_type_to_int(:pause), do: 0x4 + def nx_action_controller2_prop_type_to_int(_), do: throw(:bad_enum) + def nx_action_controller2_prop_type_to_atom(0x0), do: :max_len + def nx_action_controller2_prop_type_to_atom(0x1), do: :controller_id + def nx_action_controller2_prop_type_to_atom(0x2), do: :reason + def nx_action_controller2_prop_type_to_atom(0x3), do: :userdata + def nx_action_controller2_prop_type_to_atom(0x4), do: :pause + def nx_action_controller2_prop_type_to_atom(_), do: throw(:bad_enum) + def nx_action_sample_direction_to_int(:default), do: 0x0 + def nx_action_sample_direction_to_int(:ingress), do: 0x1 + def nx_action_sample_direction_to_int(:egress), do: 0x2 + def nx_action_sample_direction_to_int(_), do: throw(:bad_enum) + def nx_action_sample_direction_to_atom(0x0), do: :default + def nx_action_sample_direction_to_atom(0x1), do: :ingress + def nx_action_sample_direction_to_atom(0x2), do: :egress + def nx_action_sample_direction_to_atom(_), do: throw(:bad_enum) + def nx_flow_spec_type_to_int(Openflow.Action.NxFlowSpecMatch), do: 0x0 + def nx_flow_spec_type_to_int(Openflow.Action.NxFlowSpecLoad), do: 0x1 + def nx_flow_spec_type_to_int(Openflow.Action.NxFlowSpecOutput), do: 0x2 + def nx_flow_spec_type_to_int(_), do: throw(:bad_enum) + def nx_flow_spec_type_to_atom(0x0), do: Openflow.Action.NxFlowSpecMatch + def nx_flow_spec_type_to_atom(0x1), do: Openflow.Action.NxFlowSpecLoad + def nx_flow_spec_type_to_atom(0x2), do: Openflow.Action.NxFlowSpecOutput + def nx_flow_spec_type_to_atom(_), do: throw(:bad_enum) + def instruction_type_to_int(Openflow.Instruction.GotoTable), do: 0x1 + def instruction_type_to_int(Openflow.Instruction.WriteMetadata), do: 0x2 + def instruction_type_to_int(Openflow.Instruction.WriteActions), do: 0x3 + def instruction_type_to_int(Openflow.Instruction.ApplyActions), do: 0x4 + def instruction_type_to_int(Openflow.Instruction.ClearActions), do: 0x5 + def instruction_type_to_int(Openflow.Instruction.Meter), do: 0x6 + def instruction_type_to_int(Openflow.Instruction.Experimenter), do: 0xFFFF + def instruction_type_to_int(_), do: throw(:bad_enum) + def instruction_type_to_atom(0x1), do: Openflow.Instruction.GotoTable + def instruction_type_to_atom(0x2), do: Openflow.Instruction.WriteMetadata + def instruction_type_to_atom(0x3), do: Openflow.Instruction.WriteActions + def instruction_type_to_atom(0x4), do: Openflow.Instruction.ApplyActions + def instruction_type_to_atom(0x5), do: Openflow.Instruction.ClearActions + def instruction_type_to_atom(0x6), do: Openflow.Instruction.Meter + def instruction_type_to_atom(0xFFFF), do: Openflow.Instruction.Experimenter + def instruction_type_to_atom(_), do: throw(:bad_enum) + def controller_role_to_int(:nochange), do: 0x0 + def controller_role_to_int(:equal), do: 0x1 + def controller_role_to_int(:master), do: 0x2 + def controller_role_to_int(:slave), do: 0x3 + def controller_role_to_int(_), do: throw(:bad_enum) + def controller_role_to_atom(0x0), do: :nochange + def controller_role_to_atom(0x1), do: :equal + def controller_role_to_atom(0x2), do: :master + def controller_role_to_atom(0x3), do: :slave + def controller_role_to_atom(_), do: throw(:bad_enum) + def nx_role_to_int(:other), do: 0x0 + def nx_role_to_int(:master), do: 0x1 + def nx_role_to_int(:slave), do: 0x2 + def nx_role_to_int(_), do: throw(:bad_enum) + def nx_role_to_atom(0x0), do: :other + def nx_role_to_atom(0x1), do: :master + def nx_role_to_atom(0x2), do: :slave + def nx_role_to_atom(_), do: throw(:bad_enum) + def packet_in_format_to_int(:standard), do: 0x0 + def packet_in_format_to_int(:nxt_packet_in), do: 0x1 + def packet_in_format_to_int(:nxt_packet_in2), do: 0x2 + def packet_in_format_to_int(_), do: throw(:bad_enum) + def packet_in_format_to_atom(0x0), do: :standard + def packet_in_format_to_atom(0x1), do: :nxt_packet_in + def packet_in_format_to_atom(0x2), do: :nxt_packet_in2 + def packet_in_format_to_atom(_), do: throw(:bad_enum) + def flow_format_to_int(:openflow10), do: 0x0 + def flow_format_to_int(:nxm), do: 0x1 + def flow_format_to_int(_), do: throw(:bad_enum) + def flow_format_to_atom(0x0), do: :openflow10 + def flow_format_to_atom(0x1), do: :nxm + def flow_format_to_atom(_), do: throw(:bad_enum) + def packet_in2_prop_type_to_int(:packet), do: 0x0 + def packet_in2_prop_type_to_int(:full_len), do: 0x1 + def packet_in2_prop_type_to_int(:buffer_id), do: 0x2 + def packet_in2_prop_type_to_int(:table_id), do: 0x3 + def packet_in2_prop_type_to_int(:cookie), do: 0x4 + def packet_in2_prop_type_to_int(:reason), do: 0x5 + def packet_in2_prop_type_to_int(:metadata), do: 0x6 + def packet_in2_prop_type_to_int(:userdata), do: 0x7 + def packet_in2_prop_type_to_int(:continuation), do: 0x8 + def packet_in2_prop_type_to_int(_), do: throw(:bad_enum) + def packet_in2_prop_type_to_atom(0x0), do: :packet + def packet_in2_prop_type_to_atom(0x1), do: :full_len + def packet_in2_prop_type_to_atom(0x2), do: :buffer_id + def packet_in2_prop_type_to_atom(0x3), do: :table_id + def packet_in2_prop_type_to_atom(0x4), do: :cookie + def packet_in2_prop_type_to_atom(0x5), do: :reason + def packet_in2_prop_type_to_atom(0x6), do: :metadata + def packet_in2_prop_type_to_atom(0x7), do: :userdata + def packet_in2_prop_type_to_atom(0x8), do: :continuation + def packet_in2_prop_type_to_atom(_), do: throw(:bad_enum) + def continuation_prop_type_to_int(:bridge), do: 0x8000 + def continuation_prop_type_to_int(:stack), do: 0x8001 + def continuation_prop_type_to_int(:mirrors), do: 0x8002 + def continuation_prop_type_to_int(:conntracked), do: 0x8003 + def continuation_prop_type_to_int(:table_id), do: 0x8004 + def continuation_prop_type_to_int(:cookie), do: 0x8005 + def continuation_prop_type_to_int(:actions), do: 0x8006 + def continuation_prop_type_to_int(:action_set), do: 0x8007 + def continuation_prop_type_to_int(_), do: throw(:bad_enum) + def continuation_prop_type_to_atom(0x8000), do: :bridge + def continuation_prop_type_to_atom(0x8001), do: :stack + def continuation_prop_type_to_atom(0x8002), do: :mirrors + def continuation_prop_type_to_atom(0x8003), do: :conntracked + def continuation_prop_type_to_atom(0x8004), do: :table_id + def continuation_prop_type_to_atom(0x8005), do: :cookie + def continuation_prop_type_to_atom(0x8006), do: :actions + def continuation_prop_type_to_atom(0x8007), do: :action_set + def continuation_prop_type_to_atom(_), do: throw(:bad_enum) + def flow_monitor_flag_to_int(:initial), do: 0x1 + def flow_monitor_flag_to_int(:add), do: 0x2 + def flow_monitor_flag_to_int(:delete), do: 0x4 + def flow_monitor_flag_to_int(:modify), do: 0x8 + def flow_monitor_flag_to_int(:actions), do: 0x10 + def flow_monitor_flag_to_int(:own), do: 0x20 + def flow_monitor_flag_to_int(_), do: throw(:bad_enum) + def flow_monitor_flag_to_atom(0x1), do: :initial + def flow_monitor_flag_to_atom(0x2), do: :add + def flow_monitor_flag_to_atom(0x4), do: :delete + def flow_monitor_flag_to_atom(0x8), do: :modify + def flow_monitor_flag_to_atom(0x10), do: :actions + def flow_monitor_flag_to_atom(0x20), do: :own + def flow_monitor_flag_to_atom(_), do: throw(:bad_enum) + def flow_update_event_to_int(:added), do: 0x0 + def flow_update_event_to_int(:deleted), do: 0x1 + def flow_update_event_to_int(:modified), do: 0x2 + def flow_update_event_to_int(:abbrev), do: 0x3 + def flow_update_event_to_int(_), do: throw(:bad_enum) + def flow_update_event_to_atom(0x0), do: :added + def flow_update_event_to_atom(0x1), do: :deleted + def flow_update_event_to_atom(0x2), do: :modified + def flow_update_event_to_atom(0x3), do: :abbrev + def flow_update_event_to_atom(_), do: throw(:bad_enum) + def tlv_table_mod_command_to_int(:add), do: 0x0 + def tlv_table_mod_command_to_int(:delete), do: 0x1 + def tlv_table_mod_command_to_int(:clear), do: 0x2 + def tlv_table_mod_command_to_int(_), do: throw(:bad_enum) + def tlv_table_mod_command_to_atom(0x0), do: :add + def tlv_table_mod_command_to_atom(0x1), do: :delete + def tlv_table_mod_command_to_atom(0x2), do: :clear + def tlv_table_mod_command_to_atom(_), do: throw(:bad_enum) + def table_feature_prop_type_to_int(:instructions), do: 0x0 + def table_feature_prop_type_to_int(:instructions_miss), do: 0x1 + def table_feature_prop_type_to_int(:next_tables), do: 0x2 + def table_feature_prop_type_to_int(:next_tables_miss), do: 0x3 + def table_feature_prop_type_to_int(:write_actions), do: 0x4 + def table_feature_prop_type_to_int(:write_actions_miss), do: 0x5 + def table_feature_prop_type_to_int(:apply_actions), do: 0x6 + def table_feature_prop_type_to_int(:apply_actions_miss), do: 0x7 + def table_feature_prop_type_to_int(:match), do: 0x8 + def table_feature_prop_type_to_int(:wildcards), do: 0xA + def table_feature_prop_type_to_int(:write_setfield), do: 0xC + def table_feature_prop_type_to_int(:write_setfield_miss), do: 0xD + def table_feature_prop_type_to_int(:apply_setfield), do: 0xE + def table_feature_prop_type_to_int(:apply_setfield_miss), do: 0xF + def table_feature_prop_type_to_int(:experimenter), do: 0xFFFE + def table_feature_prop_type_to_int(:experimenter_miss), do: 0xFFFF + def table_feature_prop_type_to_int(_), do: throw(:bad_enum) + def table_feature_prop_type_to_atom(0x0), do: :instructions + def table_feature_prop_type_to_atom(0x1), do: :instructions_miss + def table_feature_prop_type_to_atom(0x2), do: :next_tables + def table_feature_prop_type_to_atom(0x3), do: :next_tables_miss + def table_feature_prop_type_to_atom(0x4), do: :write_actions + def table_feature_prop_type_to_atom(0x5), do: :write_actions_miss + def table_feature_prop_type_to_atom(0x6), do: :apply_actions + def table_feature_prop_type_to_atom(0x7), do: :apply_actions_miss + def table_feature_prop_type_to_atom(0x8), do: :match + def table_feature_prop_type_to_atom(0xA), do: :wildcards + def table_feature_prop_type_to_atom(0xC), do: :write_setfield + def table_feature_prop_type_to_atom(0xD), do: :write_setfield_miss + def table_feature_prop_type_to_atom(0xE), do: :apply_setfield + def table_feature_prop_type_to_atom(0xF), do: :apply_setfield_miss + def table_feature_prop_type_to_atom(0xFFFE), do: :experimenter + def table_feature_prop_type_to_atom(0xFFFF), do: :experimenter_miss + def table_feature_prop_type_to_atom(_), do: throw(:bad_enum) + + def int_to_flags(int, :openflow_codec) do + Openflow.Utils.int_to_flags([], int, enum_of(:openflow_codec)) + end + + def int_to_flags(int, :experimenter_id) do + Openflow.Utils.int_to_flags([], int, enum_of(:experimenter_id)) + end + + def int_to_flags(int, :nicira_ext_message) do + Openflow.Utils.int_to_flags([], int, enum_of(:nicira_ext_message)) + end + + def int_to_flags(int, :onf_ext_message) do + Openflow.Utils.int_to_flags([], int, enum_of(:onf_ext_message)) + end + + def int_to_flags(int, :multipart_request_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:multipart_request_flags)) + end + + def int_to_flags(int, :multipart_reply_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:multipart_reply_flags)) + end + + def int_to_flags(int, :multipart_request_codec) do + Openflow.Utils.int_to_flags([], int, enum_of(:multipart_request_codec)) + end + + def int_to_flags(int, :multipart_reply_codec) do + Openflow.Utils.int_to_flags([], int, enum_of(:multipart_reply_codec)) + end + + def int_to_flags(int, :nicira_ext_stats) do + Openflow.Utils.int_to_flags([], int, enum_of(:nicira_ext_stats)) + end + + def int_to_flags(int, :hello_elem) do + Openflow.Utils.int_to_flags([], int, enum_of(:hello_elem)) + end + + def int_to_flags(int, :error_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:error_type)) + end + + def int_to_flags(int, :hello_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:hello_failed)) + end + + def int_to_flags(int, :bad_request) do + Openflow.Utils.int_to_flags([], int, enum_of(:bad_request)) + end + + def int_to_flags(int, :bad_action) do + Openflow.Utils.int_to_flags([], int, enum_of(:bad_action)) + end + + def int_to_flags(int, :bad_instruction) do + Openflow.Utils.int_to_flags([], int, enum_of(:bad_instruction)) + end + + def int_to_flags(int, :bad_match) do + Openflow.Utils.int_to_flags([], int, enum_of(:bad_match)) + end + + def int_to_flags(int, :flow_mod_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:flow_mod_failed)) + end + + def int_to_flags(int, :group_mod_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:group_mod_failed)) + end + + def int_to_flags(int, :port_mod_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:port_mod_failed)) + end + + def int_to_flags(int, :table_mod_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:table_mod_failed)) + end + + def int_to_flags(int, :queue_op_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:queue_op_failed)) + end + + def int_to_flags(int, :switch_config_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:switch_config_failed)) + end + + def int_to_flags(int, :role_request_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:role_request_failed)) + end + + def int_to_flags(int, :meter_mod_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:meter_mod_failed)) + end + + def int_to_flags(int, :table_features_failed) do + Openflow.Utils.int_to_flags([], int, enum_of(:table_features_failed)) + end + + def int_to_flags(int, :switch_capabilities) do + Openflow.Utils.int_to_flags([], int, enum_of(:switch_capabilities)) + end + + def int_to_flags(int, :config_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:config_flags)) + end + + def int_to_flags(int, :controller_max_len) do + Openflow.Utils.int_to_flags([], int, enum_of(:controller_max_len)) + end + + def int_to_flags(int, :experimenter_oxm_vendors) do + Openflow.Utils.int_to_flags([], int, enum_of(:experimenter_oxm_vendors)) + end + + def int_to_flags(int, :match_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:match_type)) + end + + def int_to_flags(int, :oxm_class) do + Openflow.Utils.int_to_flags([], int, enum_of(:oxm_class)) + end + + def int_to_flags(int, :nxm_0) do + Openflow.Utils.int_to_flags([], int, enum_of(:nxm_0)) + end + + def int_to_flags(int, :nxm_1) do + Openflow.Utils.int_to_flags([], int, enum_of(:nxm_1)) + end + + def int_to_flags(int, :openflow_basic) do + Openflow.Utils.int_to_flags([], int, enum_of(:openflow_basic)) + end + + def int_to_flags(int, :vlan_id) do + Openflow.Utils.int_to_flags([], int, enum_of(:vlan_id)) + end + + def int_to_flags(int, :ipv6exthdr_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:ipv6exthdr_flags)) + end + + def int_to_flags(int, :tcp_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:tcp_flags)) + end + + def int_to_flags(int, :ct_state_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:ct_state_flags)) + end + + def int_to_flags(int, :packet_register) do + Openflow.Utils.int_to_flags([], int, enum_of(:packet_register)) + end + + def int_to_flags(int, :nicira_ext_match) do + Openflow.Utils.int_to_flags([], int, enum_of(:nicira_ext_match)) + end + + def int_to_flags(int, :hp_ext_match) do + Openflow.Utils.int_to_flags([], int, enum_of(:hp_ext_match)) + end + + def int_to_flags(int, :hp_custom_match_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:hp_custom_match_type)) + end + + def int_to_flags(int, :onf_ext_match) do + Openflow.Utils.int_to_flags([], int, enum_of(:onf_ext_match)) + end + + def int_to_flags(int, :buffer_id) do + Openflow.Utils.int_to_flags([], int, enum_of(:buffer_id)) + end + + def int_to_flags(int, :port_config) do + Openflow.Utils.int_to_flags([], int, enum_of(:port_config)) + end + + def int_to_flags(int, :port_state) do + Openflow.Utils.int_to_flags([], int, enum_of(:port_state)) + end + + def int_to_flags(int, :port_features) do + Openflow.Utils.int_to_flags([], int, enum_of(:port_features)) + end + + def int_to_flags(int, :openflow10_port_no) do + Openflow.Utils.int_to_flags([], int, enum_of(:openflow10_port_no)) + end + + def int_to_flags(int, :openflow13_port_no) do + Openflow.Utils.int_to_flags([], int, enum_of(:openflow13_port_no)) + end + + def int_to_flags(int, :packet_in_reason) do + Openflow.Utils.int_to_flags([], int, enum_of(:packet_in_reason)) + end + + def int_to_flags(int, :flow_mod_command) do + Openflow.Utils.int_to_flags([], int, enum_of(:flow_mod_command)) + end + + def int_to_flags(int, :flow_mod_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:flow_mod_flags)) + end + + def int_to_flags(int, :flow_removed_reason) do + Openflow.Utils.int_to_flags([], int, enum_of(:flow_removed_reason)) + end + + def int_to_flags(int, :port_reason) do + Openflow.Utils.int_to_flags([], int, enum_of(:port_reason)) + end + + def int_to_flags(int, :group_mod_command) do + Openflow.Utils.int_to_flags([], int, enum_of(:group_mod_command)) + end + + def int_to_flags(int, :group_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:group_type)) + end + + def int_to_flags(int, :group_id) do + Openflow.Utils.int_to_flags([], int, enum_of(:group_id)) + end + + def int_to_flags(int, :group_capabilities) do + Openflow.Utils.int_to_flags([], int, enum_of(:group_capabilities)) + end + + def int_to_flags(int, :table_id) do + Openflow.Utils.int_to_flags([], int, enum_of(:table_id)) + end + + def int_to_flags(int, :queue_id) do + Openflow.Utils.int_to_flags([], int, enum_of(:queue_id)) + end + + def int_to_flags(int, :meter_mod_command) do + Openflow.Utils.int_to_flags([], int, enum_of(:meter_mod_command)) + end + + def int_to_flags(int, :meter_id) do + Openflow.Utils.int_to_flags([], int, enum_of(:meter_id)) + end + + def int_to_flags(int, :meter_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:meter_flags)) + end + + def int_to_flags(int, :meter_band_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:meter_band_type)) + end + + def int_to_flags(int, :table_config) do + Openflow.Utils.int_to_flags([], int, enum_of(:table_config)) + end + + def int_to_flags(int, :action_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:action_type)) + end + + def int_to_flags(int, :action_vendor) do + Openflow.Utils.int_to_flags([], int, enum_of(:action_vendor)) + end + + def int_to_flags(int, :onf_ext_action) do + Openflow.Utils.int_to_flags([], int, enum_of(:onf_ext_action)) + end + + def int_to_flags(int, :nicira_ext_action) do + Openflow.Utils.int_to_flags([], int, enum_of(:nicira_ext_action)) + end + + def int_to_flags(int, :nx_mp_algorithm) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_mp_algorithm)) + end + + def int_to_flags(int, :nx_hash_fields) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_hash_fields)) + end + + def int_to_flags(int, :nx_bd_algorithm) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_bd_algorithm)) + end + + def int_to_flags(int, :nx_learn_flag) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_learn_flag)) + end + + def int_to_flags(int, :nx_conntrack_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_conntrack_flags)) + end + + def int_to_flags(int, :nx_nat_flags) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_nat_flags)) + end + + def int_to_flags(int, :nx_nat_range) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_nat_range)) + end + + def int_to_flags(int, :nx_action_controller2_prop_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_action_controller2_prop_type)) + end + + def int_to_flags(int, :nx_action_sample_direction) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_action_sample_direction)) + end + + def int_to_flags(int, :nx_flow_spec_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_flow_spec_type)) + end + + def int_to_flags(int, :instruction_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:instruction_type)) + end + + def int_to_flags(int, :controller_role) do + Openflow.Utils.int_to_flags([], int, enum_of(:controller_role)) + end + + def int_to_flags(int, :nx_role) do + Openflow.Utils.int_to_flags([], int, enum_of(:nx_role)) + end + + def int_to_flags(int, :packet_in_format) do + Openflow.Utils.int_to_flags([], int, enum_of(:packet_in_format)) + end + + def int_to_flags(int, :flow_format) do + Openflow.Utils.int_to_flags([], int, enum_of(:flow_format)) + end + + def int_to_flags(int, :packet_in2_prop_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:packet_in2_prop_type)) + end + + def int_to_flags(int, :continuation_prop_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:continuation_prop_type)) + end + + def int_to_flags(int, :flow_monitor_flag) do + Openflow.Utils.int_to_flags([], int, enum_of(:flow_monitor_flag)) + end + + def int_to_flags(int, :flow_update_event) do + Openflow.Utils.int_to_flags([], int, enum_of(:flow_update_event)) + end + + def int_to_flags(int, :tlv_table_mod_command) do + Openflow.Utils.int_to_flags([], int, enum_of(:tlv_table_mod_command)) + end + + def int_to_flags(int, :table_feature_prop_type) do + Openflow.Utils.int_to_flags([], int, enum_of(:table_feature_prop_type)) + end + + def flags_to_int(flags, :openflow_codec) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:openflow_codec)) + end + + def flags_to_int(flags, :experimenter_id) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:experimenter_id)) + end + + def flags_to_int(flags, :nicira_ext_message) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nicira_ext_message)) + end + + def flags_to_int(flags, :onf_ext_message) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:onf_ext_message)) + end + + def flags_to_int(flags, :multipart_request_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:multipart_request_flags)) + end + + def flags_to_int(flags, :multipart_reply_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:multipart_reply_flags)) + end + + def flags_to_int(flags, :multipart_request_codec) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:multipart_request_codec)) + end + + def flags_to_int(flags, :multipart_reply_codec) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:multipart_reply_codec)) + end + + def flags_to_int(flags, :nicira_ext_stats) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nicira_ext_stats)) + end + + def flags_to_int(flags, :hello_elem) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:hello_elem)) + end + + def flags_to_int(flags, :error_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:error_type)) + end + + def flags_to_int(flags, :hello_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:hello_failed)) + end + + def flags_to_int(flags, :bad_request) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:bad_request)) + end + + def flags_to_int(flags, :bad_action) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:bad_action)) + end + + def flags_to_int(flags, :bad_instruction) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:bad_instruction)) + end + + def flags_to_int(flags, :bad_match) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:bad_match)) + end + + def flags_to_int(flags, :flow_mod_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_mod_failed)) + end + + def flags_to_int(flags, :group_mod_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:group_mod_failed)) + end + + def flags_to_int(flags, :port_mod_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:port_mod_failed)) + end + + def flags_to_int(flags, :table_mod_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:table_mod_failed)) + end + + def flags_to_int(flags, :queue_op_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:queue_op_failed)) + end + + def flags_to_int(flags, :switch_config_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:switch_config_failed)) + end + + def flags_to_int(flags, :role_request_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:role_request_failed)) + end + + def flags_to_int(flags, :meter_mod_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:meter_mod_failed)) + end + + def flags_to_int(flags, :table_features_failed) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:table_features_failed)) + end + + def flags_to_int(flags, :switch_capabilities) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:switch_capabilities)) + end + + def flags_to_int(flags, :config_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:config_flags)) + end + + def flags_to_int(flags, :controller_max_len) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:controller_max_len)) + end + + def flags_to_int(flags, :experimenter_oxm_vendors) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:experimenter_oxm_vendors)) + end + + def flags_to_int(flags, :match_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:match_type)) + end + + def flags_to_int(flags, :oxm_class) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:oxm_class)) + end + + def flags_to_int(flags, :nxm_0) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nxm_0)) + end + + def flags_to_int(flags, :nxm_1) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nxm_1)) + end + + def flags_to_int(flags, :openflow_basic) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:openflow_basic)) + end + + def flags_to_int(flags, :vlan_id) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:vlan_id)) + end + + def flags_to_int(flags, :ipv6exthdr_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:ipv6exthdr_flags)) + end + + def flags_to_int(flags, :tcp_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:tcp_flags)) + end + + def flags_to_int(flags, :ct_state_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:ct_state_flags)) + end + + def flags_to_int(flags, :packet_register) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:packet_register)) + end + + def flags_to_int(flags, :nicira_ext_match) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nicira_ext_match)) + end + + def flags_to_int(flags, :hp_ext_match) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:hp_ext_match)) + end + + def flags_to_int(flags, :hp_custom_match_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:hp_custom_match_type)) + end + + def flags_to_int(flags, :onf_ext_match) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:onf_ext_match)) + end + + def flags_to_int(flags, :buffer_id) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:buffer_id)) + end + + def flags_to_int(flags, :port_config) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:port_config)) + end + + def flags_to_int(flags, :port_state) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:port_state)) + end + + def flags_to_int(flags, :port_features) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:port_features)) + end + + def flags_to_int(flags, :openflow10_port_no) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:openflow10_port_no)) + end + + def flags_to_int(flags, :openflow13_port_no) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:openflow13_port_no)) + end + + def flags_to_int(flags, :packet_in_reason) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:packet_in_reason)) + end + + def flags_to_int(flags, :flow_mod_command) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_mod_command)) + end + + def flags_to_int(flags, :flow_mod_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_mod_flags)) + end + + def flags_to_int(flags, :flow_removed_reason) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_removed_reason)) + end + + def flags_to_int(flags, :port_reason) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:port_reason)) + end + + def flags_to_int(flags, :group_mod_command) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:group_mod_command)) + end + + def flags_to_int(flags, :group_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:group_type)) + end + + def flags_to_int(flags, :group_id) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:group_id)) + end + + def flags_to_int(flags, :group_capabilities) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:group_capabilities)) + end + + def flags_to_int(flags, :table_id) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:table_id)) + end + + def flags_to_int(flags, :queue_id) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:queue_id)) + end + + def flags_to_int(flags, :meter_mod_command) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:meter_mod_command)) + end + + def flags_to_int(flags, :meter_id) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:meter_id)) + end + + def flags_to_int(flags, :meter_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:meter_flags)) + end + + def flags_to_int(flags, :meter_band_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:meter_band_type)) + end + + def flags_to_int(flags, :table_config) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:table_config)) + end + + def flags_to_int(flags, :action_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:action_type)) + end + + def flags_to_int(flags, :action_vendor) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:action_vendor)) + end + + def flags_to_int(flags, :onf_ext_action) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:onf_ext_action)) + end + + def flags_to_int(flags, :nicira_ext_action) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nicira_ext_action)) + end + + def flags_to_int(flags, :nx_mp_algorithm) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_mp_algorithm)) + end + + def flags_to_int(flags, :nx_hash_fields) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_hash_fields)) + end + + def flags_to_int(flags, :nx_bd_algorithm) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_bd_algorithm)) + end + + def flags_to_int(flags, :nx_learn_flag) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_learn_flag)) + end + + def flags_to_int(flags, :nx_conntrack_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_conntrack_flags)) + end + + def flags_to_int(flags, :nx_nat_flags) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_nat_flags)) + end + + def flags_to_int(flags, :nx_nat_range) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_nat_range)) + end + + def flags_to_int(flags, :nx_action_controller2_prop_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_action_controller2_prop_type)) + end + + def flags_to_int(flags, :nx_action_sample_direction) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_action_sample_direction)) + end + + def flags_to_int(flags, :nx_flow_spec_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_flow_spec_type)) + end + + def flags_to_int(flags, :instruction_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:instruction_type)) + end + + def flags_to_int(flags, :controller_role) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:controller_role)) + end + + def flags_to_int(flags, :nx_role) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:nx_role)) + end + + def flags_to_int(flags, :packet_in_format) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:packet_in_format)) + end + + def flags_to_int(flags, :flow_format) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_format)) + end + + def flags_to_int(flags, :packet_in2_prop_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:packet_in2_prop_type)) + end + + def flags_to_int(flags, :continuation_prop_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:continuation_prop_type)) + end + + def flags_to_int(flags, :flow_monitor_flag) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_monitor_flag)) + end + + def flags_to_int(flags, :flow_update_event) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:flow_update_event)) + end + + def flags_to_int(flags, :tlv_table_mod_command) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:tlv_table_mod_command)) + end + + def flags_to_int(flags, :table_feature_prop_type) do + Openflow.Utils.flags_to_int(0, flags, enum_of(:table_feature_prop_type)) + end + + defp enum_of(:openflow_codec), + do: [ {Openflow.Hello, 0}, {Openflow.ErrorMsg, 1}, {Openflow.Echo.Request, 2}, @@ -31,22 +12558,13 @@ defmodule Openflow.Enums do {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. */ + ] + + defp enum_of(:experimenter_id), do: [nicira_ext_message: 8992, onf_ext_message: 1_330_529_792] + + defp enum_of(:nicira_ext_message), + do: [ {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}, @@ -58,18 +12576,16 @@ defmodule Openflow.Enums do {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: [ + ] + + defp enum_of(:onf_ext_message), + do: [{Openflow.OnfBundleControl, 2300}, {Openflow.OnfBundleAddMessage, 2301}] + + defp enum_of(:multipart_request_flags), do: [more: 1] + defp enum_of(:multipart_reply_flags), do: [more: 1] + + defp enum_of(:multipart_request_codec), + do: [ {Openflow.Multipart.Desc.Request, 0}, {Openflow.Multipart.Flow.Request, 1}, {Openflow.Multipart.Aggregate.Request, 2}, @@ -84,9 +12600,11 @@ defmodule Openflow.Enums do {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.Experimenter.Request, 65535} + ] + + defp enum_of(:multipart_reply_codec), + do: [ {Openflow.Multipart.Desc.Reply, 0}, {Openflow.Multipart.Flow.Reply, 1}, {Openflow.Multipart.Aggregate.Reply, 2}, @@ -101,19 +12619,22 @@ defmodule Openflow.Enums do {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.Experimenter.Reply, 65535} + ] + + defp enum_of(:nicira_ext_stats), + do: [ {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: [ + ] + + defp enum_of(:hello_elem), do: [versionbitmap: 1] + + defp enum_of(:error_type), + do: [ hello_failed: 0, bad_request: 1, bad_action: 2, @@ -128,13 +12649,13 @@ defmodule Openflow.Enums do role_request_failed: 11, meter_mod_failed: 12, table_features_failed: 13, - experimenter: 0xFFFF - ], - hello_failed: [ - inconpatible: 0, - eperm: 1 - ], - bad_request: [ + experimenter: 65535 + ] + + defp enum_of(:hello_failed), do: [inconpatible: 0, eperm: 1] + + defp enum_of(:bad_request), + do: [ bad_version: 0, bad_type: 1, bad_multipart: 2, @@ -149,8 +12670,10 @@ defmodule Openflow.Enums do bad_port: 11, bad_packet: 12, multipart_buffer_overflow: 13 - ], - bad_action: [ + ] + + defp enum_of(:bad_action), + do: [ bad_type: 0, bad_len: 1, bad_experimeter: 2, @@ -167,8 +12690,10 @@ defmodule Openflow.Enums do bad_set_type: 13, bad_set_len: 14, bad_set_argument: 15 - ], - bad_instruction: [ + ] + + defp enum_of(:bad_instruction), + do: [ unknown_instruction: 0, unsupported_instruction: 1, bad_table_id: 2, @@ -178,8 +12703,10 @@ defmodule Openflow.Enums do bad_exp_type: 6, bad_len: 7, eperm: 8 - ], - bad_match: [ + ] + + defp enum_of(:bad_match), + do: [ bad_type: 0, bad_len: 1, bad_tag: 2, @@ -192,8 +12719,10 @@ defmodule Openflow.Enums do bad_prereq: 9, dup_field: 10, eperm: 11 - ], - flow_mod_failed: [ + ] + + defp enum_of(:flow_mod_failed), + do: [ unknown: 0, table_full: 1, bad_table_id: 2, @@ -202,8 +12731,10 @@ defmodule Openflow.Enums do bad_timeout: 5, bad_command: 6, bad_flags: 7 - ], - group_mod_failed: [ + ] + + defp enum_of(:group_mod_failed), + do: [ group_exists: 0, invalid_group: 1, weight_unsupported: 2, @@ -219,35 +12750,18 @@ defmodule Openflow.Enums do 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: [ + ] + + defp enum_of(:port_mod_failed), + do: [bad_port: 0, bad_hw_addr: 1, bad_config: 2, bad_advertise: 3, eperm: 4] + + defp enum_of(:table_mod_failed), do: [bad_table: 0, bad_config: 1, eperm: 2] + defp enum_of(:queue_op_failed), do: [bad_port: 0, bad_queue: 1, eperm: 2] + defp enum_of(:switch_config_failed), do: [bad_flags: 0, bad_len: 1, eperm: 2] + defp enum_of(:role_request_failed), do: [stale: 0, unsup: 1, bad_role: 2] + + defp enum_of(:meter_mod_failed), + do: [ unknown: 0, meter_exists: 1, invalid_meter: 2, @@ -260,50 +12774,49 @@ defmodule Openflow.Enums do bad_band_value: 9, out_of_meters: 10, out_of_bands: 11 - ], - table_features_failed: [ + ] + + defp enum_of(:table_features_failed), + do: [ 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, - hp_ext_match: 0x00002428, - 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: [ + ] + + defp enum_of(:switch_capabilities), + do: [ + flow_stats: 1, + table_stats: 2, + port_stats: 4, + group_stats: 8, + ip_reasm: 32, + queue_stats: 64, + arp_match_ip: 128, + port_blocked: 256 + ] + + defp enum_of(:config_flags), do: [drop: 1, reasm: 2] + defp enum_of(:controller_max_len), do: [max: 65509, no_buffer: 65535] + + defp enum_of(:experimenter_oxm_vendors), + do: [nicira_ext_match: 8992, hp_ext_match: 9256, onf_ext_match: 1_330_529_792] + + defp enum_of(:match_type), do: [standard: 0, oxm: 1] + + defp enum_of(:oxm_class), + do: [ + nxm_0: 0, + nxm_1: 1, + openflow_basic: 32768, + packet_register: 32769, + experimenter: 65535 + ] + + defp enum_of(:nxm_0), + do: [ nx_in_port: 0, nx_eth_dst: 1, nx_eth_src: 2, @@ -323,8 +12836,10 @@ defmodule Openflow.Enums do nx_arp_spa: 16, nx_arp_tpa: 17, nx_tcp_flags: 34 - ], - nxm_1: [ + ] + + defp enum_of(:nxm_1), + do: [ reg0: 0, reg1: 1, reg2: 2, @@ -450,8 +12965,10 @@ defmodule Openflow.Enums do ct_ipv6_dst: 123, ct_tp_src: 124, ct_tp_dst: 125 - ], - openflow_basic: [ + ] + + defp enum_of(:openflow_basic), + do: [ in_port: 0, in_phy_port: 1, metadata: 2, @@ -492,8 +13009,6 @@ defmodule Openflow.Enums do pbb_isid: 37, tunnel_id: 38, ipv6_exthdr: 39, - - # Lagopus extended match fields pbb_uca: 41, packet_type: 42, gre_flags: 43, @@ -521,62 +13036,34 @@ defmodule Openflow.Enums do 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: [ - # Beginning of a new connection. - new: 1 <<< 0, - # Part of an existing connection. - est: 1 <<< 1, - # Related to an established connection. - rel: 1 <<< 2, - # Flow is in the reply direction. - rep: 1 <<< 3, - # Could not track connection. - inv: 1 <<< 4, - # Conntrack has occurred. - trk: 1 <<< 5, - # Packet's source address/port was mangled by NAT. - snat: 1 <<< 6, - # Packet's destination address/port was mangled by NAT. - dnat: 1 <<< 7 - ], - packet_register: [ - xreg0: 0, - xreg1: 1, - xreg2: 2, - xreg3: 3, - xreg4: 4, - xreg5: 5, - xreg6: 6, - xreg7: 7 - ], - nicira_ext_match: [ + ] + + defp enum_of(:vlan_id), do: [present: 4096, none: 0] + + defp enum_of(:ipv6exthdr_flags), + do: [ + nonext: 1, + esp: 2, + auth: 4, + dest: 8, + frag: 16, + router: 32, + hop: 64, + unrep: 128, + unseq: 256 + ] + + defp enum_of(:tcp_flags), + do: [fin: 1, syn: 2, rst: 4, psh: 8, ack: 16, urg: 32, ece: 64, cwr: 128, ns: 256] + + defp enum_of(:ct_state_flags), + do: [new: 1, est: 2, rel: 4, rep: 8, inv: 16, trk: 32, snat: 64, dnat: 128] + + defp enum_of(:packet_register), + do: [xreg0: 0, xreg1: 1, xreg2: 2, xreg3: 3, xreg4: 4, xreg5: 5, xreg6: 6, xreg7: 7] + + defp enum_of(:nicira_ext_match), + do: [ nsh_flags: 1, nsh_mdtype: 2, nsh_np: 3, @@ -586,8 +13073,10 @@ defmodule Openflow.Enums do nsh_c2: 7, nsh_c3: 8, nsh_c4: 9 - ], - hp_ext_match: [ + ] + + defp enum_of(:hp_ext_match), + do: [ hp_udp_src_port_range: 0, hp_udp_dst_port_range: 1, hp_tcp_src_port_range: 2, @@ -597,171 +13086,126 @@ defmodule Openflow.Enums do hp_custom_2: 6, hp_custom_3: 7, hp_custom_4: 8 - ], - hp_custom_match_type: [ - l2_start: 1, - l3_start: 2, - l4_start: 3 - ], - 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: [ + ] + + defp enum_of(:hp_custom_match_type), do: [l2_start: 1, l3_start: 2, l4_start: 3] + defp enum_of(:onf_ext_match), do: [onf_tcp_flags: 42, onf_actset_output: 43, onf_pbb_uca: 2560] + defp enum_of(:buffer_id), do: [no_buffer: 4_294_967_295] + defp enum_of(:port_config), do: [port_down: 1, no_receive: 4, no_forward: 32, no_packet_in: 64] + defp enum_of(:port_state), do: [link_down: 1, blocked: 2, live: 4] + + defp enum_of(:port_features), + do: [ + "10mb_hd": 1, + "10mb_fd": 2, + "100mb_hd": 4, + "100mb_fd": 8, + "1gb_hd": 16, + "1gb_fd": 32, + "10gb_fd": 64, + "40gb_fd": 128, + "100gb_fd": 256, + "1tb_fd": 512, + other: 1024, + copper: 2048, + fiber: 4096, + autoneg: 8192, + pause: 16384, + pause_asym: 32768 + ] + + defp enum_of(:openflow10_port_no), + do: [ + max: 65280, + in_port: 65528, + table: 65529, + normal: 65530, + flood: 65531, + all: 65532, + controller: 65533, + local: 65534, + none: 65535 + ] + + defp enum_of(:openflow13_port_no), + do: [ + max: 4_294_967_040, + in_port: 4_294_967_288, + table: 4_294_967_289, + normal: 4_294_967_290, + flood: 4_294_967_291, + all: 4_294_967_292, + controller: 4_294_967_293, + local: 4_294_967_294, + any: 4_294_967_295 + ] + + defp enum_of(:packet_in_reason), + do: [no_match: 0, action: 1, invalid_ttl: 2, action_set: 3, group: 4, packet_out: 5] + + defp enum_of(:flow_mod_command), + do: [add: 0, modify: 1, modify_strict: 2, delete: 3, delete_strict: 4] + + defp enum_of(:flow_mod_flags), + do: [ + send_flow_rem: 1, + check_overlap: 2, + reset_counts: 4, + no_packet_counts: 8, + no_byte_counts: 16 + ] + + defp enum_of(:flow_removed_reason), + do: [ 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: [ + ] + + defp enum_of(:port_reason), do: [add: 0, delete: 1, modify: 2] + defp enum_of(:group_mod_command), do: [add: 0, modify: 1, delete: 2] + defp enum_of(:group_type), do: [all: 0, select: 1, indirect: 2, fast_failover: 3] + defp enum_of(:group_id), do: [max: 4_294_967_040, all: 4_294_967_292, any: 4_294_967_295] + + defp enum_of(:group_capabilities), + do: [select_weight: 1, select_liveness: 2, chaining: 4, chaining_checks: 8] + + defp enum_of(:table_id), do: [max: 254, all: 255] + defp enum_of(:queue_id), do: [all: 4_294_967_295] + defp enum_of(:meter_mod_command), do: [add: 0, modify: 1, delete: 2] + + defp enum_of(:meter_id), + do: [ + max: 4_294_901_760, + slowpath: 4_294_967_293, + controller: 4_294_967_294, + all: 4_294_967_295 + ] + + defp enum_of(:meter_flags), do: [kbps: 1, pktps: 2, burst: 4, stats: 8] + + defp enum_of(:meter_band_type), + do: [ {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.MeterBand.Experimenter, 65535} + ] + + defp enum_of(:table_config), + do: [ + table_miss_controller: 0, + table_miss_continue: 1, + table_miss_drop: 2, + table_miss_mask: 3, + eviction: 4, + vacancy_events: 8 + ] + + defp enum_of(:action_type), + do: [ {Openflow.Action.Output, 0}, {Openflow.Action.CopyTtlOut, 11}, {Openflow.Action.CopyTtlIn, 12}, @@ -782,16 +13226,14 @@ defmodule Openflow.Enums do {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.Experimenter, 65535} + ] + + defp enum_of(:action_vendor), do: [nicira_ext_action: 8992, onf_ext_action: 1_330_529_792] + defp enum_of(:onf_ext_action), do: [{Openflow.Action.OnfCopyField, 3200}] + + defp enum_of(:nicira_ext_action), + do: [ {Openflow.Action.NxResubmit, 1}, {Openflow.Action.NxSetTunnel, 2}, {Openflow.Action.NxSetQueue, 4}, @@ -837,97 +13279,69 @@ defmodule Openflow.Enums do {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: [ + {Openflow.Action.NxDebugRecirc, 255} + ] + + defp enum_of(:nx_mp_algorithm), + do: [modulo_n: 0, hash_threshold: 1, highest_random_weight: 2, iterative_hash: 3] + + defp enum_of(:nx_hash_fields), + do: [ 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: [ + ] + + defp enum_of(:nx_bd_algorithm), do: [active_backup: 0, highest_random_weight: 1] + defp enum_of(:nx_learn_flag), do: [send_flow_rem: 1, delete_learned: 2, write_result: 4] + defp enum_of(:nx_conntrack_flags), do: [commit: 1, force: 2] + + defp enum_of(:nx_nat_flags), + do: [src: 1, dst: 2, persistent: 4, protocol_hash: 8, protocol_random: 16] + + defp enum_of(:nx_nat_range), + do: [ + ipv4_min: 1, + ipv4_max: 2, + ipv6_min: 4, + ipv6_max: 8, + proto_min: 16, + proto_max: 32 + ] + + defp enum_of(:nx_action_controller2_prop_type), + do: [max_len: 0, controller_id: 1, reason: 2, userdata: 3, pause: 4] + + defp enum_of(:nx_action_sample_direction), do: [default: 0, ingress: 1, egress: 2] + + defp enum_of(:nx_flow_spec_type), + do: [ {Openflow.Action.NxFlowSpecMatch, 0}, {Openflow.Action.NxFlowSpecLoad, 1}, {Openflow.Action.NxFlowSpecOutput, 2} - ], - instruction_type: [ + ] + + defp enum_of(:instruction_type), + do: [ {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: [ + {Openflow.Instruction.Experimenter, 65535} + ] + + defp enum_of(:controller_role), do: [nochange: 0, equal: 1, master: 2, slave: 3] + defp enum_of(:nx_role), do: [other: 0, master: 1, slave: 2] + defp enum_of(:packet_in_format), do: [standard: 0, nxt_packet_in: 1, nxt_packet_in2: 2] + defp enum_of(:flow_format), do: [openflow10: 0, nxm: 1] + + defp enum_of(:packet_in2_prop_type), + do: [ packet: 0, full_len: 1, buffer_id: 2, @@ -937,37 +13351,28 @@ defmodule Openflow.Enums do 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 - ], - table_feature_prop_type: [ + ] + + defp enum_of(:continuation_prop_type), + do: [ + bridge: 32768, + stack: 32769, + mirrors: 32770, + conntracked: 32771, + table_id: 32772, + cookie: 32773, + actions: 32774, + action_set: 32775 + ] + + defp enum_of(:flow_monitor_flag), + do: [initial: 1, add: 2, delete: 4, modify: 8, actions: 16, own: 32] + + defp enum_of(:flow_update_event), do: [added: 0, deleted: 1, modified: 2, abbrev: 3] + defp enum_of(:tlv_table_mod_command), do: [add: 0, delete: 1, clear: 2] + + defp enum_of(:table_feature_prop_type), + do: [ instructions: 0, instructions_miss: 1, next_tables: 2, @@ -982,64 +13387,7 @@ defmodule Openflow.Enums do write_setfield_miss: 13, apply_setfield: 14, apply_setfield_miss: 15, - experimenter: 0xFFFE, - experimenter_miss: 0xFFFF + experimenter: 65534, + experimenter_miss: 65535 ] - ] - - for {enum_name, enum_def} <- @enums do - to_int_fn_name = :"#{enum_name}_to_int" - to_atom_fn_name = :"#{enum_name}_to_atom" - - for {key, _value} <- enum_def do - def to_int(unquote(key), unquote(enum_name)) do - unquote(to_int_fn_name)(unquote(key)) - catch - _class, _reason -> unquote(key) - end - end - - for {_key, value} <- enum_def do - def to_atom(unquote(value), unquote(enum_name)) do - unquote(to_atom_fn_name)(unquote(value)) - catch - _class, _reason -> unquote(value) - end - end - - def to_int(_int, unquote(enum_name)) do - throw(:bad_enum) - end - - def to_atom(_, unquote(enum_name)) do - throw(:bad_enum) - end - end - - for {enum_name, enum_def} <- @enums do - to_int_fn_name = :"#{enum_name}_to_int" - to_atom_fn_name = :"#{enum_name}_to_atom" - - for {key, value} <- enum_def do - def unquote(to_int_fn_name)(unquote(key)), do: unquote(value) - end - def unquote(to_int_fn_name)(_), do: throw(:bad_enum) - - for {key, value} <- enum_def do - def unquote(to_atom_fn_name)(unquote(value)), do: unquote(key) - end - def unquote(to_atom_fn_name)(_), do: throw(:bad_enum) - end - - for {enum_name, enum_def} <- @enums do - def int_to_flags(int, unquote(enum_name)) do - Openflow.Utils.int_to_flags([], int, enum_of(unquote(enum_name))) - end - - def flags_to_int(flags, unquote(enum_name)) do - Openflow.Utils.flags_to_int(0, flags, enum_of(unquote(enum_name))) - end - - defp enum_of(unquote(enum_name)), do: unquote(enum_def) - end end diff --git a/lib/openflow/table_mod.ex b/lib/openflow/table_mod.ex index 1f6adb4..f0e148e 100644 --- a/lib/openflow/table_mod.ex +++ b/lib/openflow/table_mod.ex @@ -18,6 +18,7 @@ defmodule Openflow.TableMod do table_id: options[:table_id] || 0 } end + def new(table_id) when is_integer(table_id) or is_atom(table_id) do %TableMod{table_id: table_id} end diff --git a/lib/tres/message_helper.ex b/lib/tres/message_helper.ex index 8a6dc4a..b9b1655 100644 --- a/lib/tres/message_helper.ex +++ b/lib/tres/message_helper.ex @@ -82,11 +82,13 @@ defmodule Tres.MessageHelper do end defp send_group_mod_delete(datapath_id, options \\ []) do - group_mod = Openflow.GroupMod.new( - xid: options[:xid] || 0, - command: :delete, - group_id: options[:group_id] || :all - ) + group_mod = + Openflow.GroupMod.new( + xid: options[:xid] || 0, + command: :delete, + group_id: options[:group_id] || :all + ) + send_message(group_mod, datapath_id) end diff --git a/lib/tres/secure_channel.ex b/lib/tres/secure_channel.ex index 1f41636..033809b 100644 --- a/lib/tres/secure_channel.ex +++ b/lib/tres/secure_channel.ex @@ -500,6 +500,7 @@ defmodule Tres.SecureChannel do XACT_KV.insert(state_data.xact_kv_ref, xid, message) send_message(messages, state_data) end + defp xactional_send_message(%{xid: xid} = message, state_data) do _ = State.set_transaction_id(state_data.xid, xid) diff --git a/mix.exs b/mix.exs index 3f60fca..f100d96 100644 --- a/mix.exs +++ b/mix.exs @@ -7,9 +7,10 @@ defmodule Tres.Mixfile do version: "0.1.0", elixir: "~> 1.5", start_permanent: Mix.env() == :prod, + escript: [main_module: Openflow.EnumGen, name: :enum_gen, path: "bin/enum_gen"], compilers: [:erlang] ++ Mix.compilers(), deps: deps(), - aliases: [test: "test --no-start"] + aliases: [test: "test --no-start", compile: ["escript.build", "format"]] ] end

    3 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