/* * Copyright (c) 2016 Cisco and/or its affiliates. * 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. */ #include #include #include #include vxlan_gpe_ioam_main_t vxlan_gpe_ioam_main; int vxlan_gpe_ioam_set_rewrite (vxlan_gpe_tunnel_t * t, int has_trace_option, int has_pot_option, int has_ppc_option, u8 ipv6_set) { vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main; u32 size; vxlan_gpe_ioam_hdr_t *vxlan_gpe_ioam_hdr; u8 *current; u8 trace_data_size = 0; u8 pot_data_size = 0; if (has_trace_option == 0 && has_pot_option == 0) return -1; /* Work out how much space we need */ size = sizeof (vxlan_gpe_ioam_hdr_t); if (has_trace_option && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] != 0) { size += sizeof (vxlan_gpe_ioam_option_t); size += hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE]; } if (has_pot_option && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0) { size += sizeof (vxlan_gpe_ioam_option_t); size += hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT]; } t->rewrite_size = size; if (!ipv6_set) { vxlan4_gpe_rewrite (t, size, VXLAN_GPE_PROTOCOL_IOAM, hm->encap_v4_next_node); vxlan_gpe_ioam_hdr = (vxlan_gpe_ioam_hdr_t *) (t->rewrite + sizeof (ip4_vxlan_gpe_header_t)); } else { vxlan6_gpe_rewrite (t, size, VXLAN_GPE_PROTOCOL_IOAM, VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP); vxlan_gpe_ioam_hdr = (vxlan_gpe_ioam_hdr_t *) (t->rewrite + sizeof (ip6_vxlan_gpe_header_t)); } vxlan_gpe_ioam_hdr->type = VXLAN_GPE_PROTOCOL_IOAM; /* Length of the header in octets */ vxlan_gpe_ioam_hdr->length = size; vxlan_gpe_ioam_hdr->protocol = t->protocol; current = (u8 *) vxlan_gpe_ioam_hdr + sizeof (vxlan_gpe_ioam_hdr_t); if (has_trace_option && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] != 0) { if (0 != hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_TRACE] (current, &trace_data_size)) return -1; current += trace_data_size; } if (has_pot_option && hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0) { pot_data_size = hm->options_size[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT]; if (0 == hm->add_options[VXLAN_GPE_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] (current, &pot_data_size)) current += pot_data_size; } return 0; } int vxlan_gpe_ioam_clear_rewrite (vxlan_gpe_tunnel_t * t, int has_trace_option, int has_pot_option, int has_ppc_option, u8 ipv6_set) { t->rewrite_size = 0; if (!ipv6_set) { vxlan4_gpe_rewrite (t, 0, 0, VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP); } else { vxlan6_gpe_rewrite (t, 0, 0, VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP); } return 0; } clib_error_t * vxlan_gpe_ioam_clear (vxlan_gpe_tunnel_t * t, int has_trace_option, int has_pot_option, int has_ppc_option, u8 ipv6_set) { int rv; rv = vxlan_gpe_ioam_clear_rewrite (t, 0, 0, 0, 0); if (rv == 0) { return (0); } else { return clib_error_return_code (0, rv, 0, "vxlan_gpe_ioam_clear_rewrite returned %d", rv); } } clib_error_t * vxlan_gpe_ioam_set (vxlan_gpe_tunnel_t * t, int has_trace_option, int has_pot_option, int has_ppc_option, u8 ipv6_set) { int rv; rv = vxlan_gpe_ioam_set_rewrite (t, has_trace_option, has_pot_option, has_ppc_option, ipv6_set); if (rv == 0) { return (0); } else { return clib_error_return_code (0, rv, 0, "vxlan_gpe_ioam_set_rewrite returned %d", rv); } } static clib_error_t * vxlan_gpe_set_ioam_rewrite_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main; ip46_address_t local, remote; u8 local_set = 0; u8 remote_set = 0; u8 ipv4_set = 0; u8 ipv6_set = 0; u32 vni; u8 vni_set = 0; u8 disable = 0; clib_error_t *rv = 0; vxlan4_gpe_tunnel_key_t key4; vxlan6_gpe_tunnel_key_t key6; uword *p; vxlan_gpe_main_t *gm = &vxlan_gpe_main; vxlan_gpe_tunnel_t *t = 0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "local %U", unformat_ip4_address, &local.ip4)) { local_set = 1; ipv4_set = 1; } else if (unformat (input, "remote %U", unformat_ip4_address, &remote.ip4)) { remote_set = 1; ipv4_set = 1; } else if (unformat (input, "local %U", unformat_ip6_address, &local.ip6)) { local_set = 1; ipv6_set = 1; } else if (unformat (input, "remote %U", unformat_ip6_address, &remote.ip6)) { remote_set = 1; ipv6_set = 1; } else if (unformat (input, "vni %d", &vni)) vni_set = 1; else if (unformat (input, "disable")) disable = 1; else break; } if (local_set == 0) return clib_error_return (0, "tunnel local address not specified"); if (remote_set == 0) return clib_error_return (0, "tunnel remote address not specified"); if (ipv4_set && ipv6_set) return clib_error_return (0, "both IPv4 and IPv6 addresses specified"); if ((ipv4_set && memcmp (&local.ip4, &remote.ip4, sizeof (local.ip4)) == 0) || (ipv6_set && memcmp (&local.ip6, &remote.ip6, sizeof (local.ip6)) == 0)) return clib_error_return (0, "src and dst addresses are identical"); if (vni_set == 0) return clib_error_return (0, "vni not specified"); if (!ipv6_set) { key4.local = local.ip4.as_u32; key4.remote = remote.ip4.as_u32; key4.vni = clib_host_to_net_u32 (vni << 8); key4.pad = 0; p = hash_get_mem (gm->vxlan4_gpe_tunnel_by_key, &key4); } else { key6.local.as_u64[0] = local.ip6.as_u64[0]; key6.local.as_u64[1] = local.ip6.as_u64[1]; key6.remote.as_u64[0] = remote.ip6.as_u64[0]; key6.remote.as_u64[1] = remote.ip6.as_u64[1]; key6.vni = clib_host_to_net_u32 (vni << 8); p = hash_get_mem (gm->vxlan6_gpe_tunnel_by_key, &key6); } if (!p) return clib_error_return (0, "VxLAN Tunnel not found"); t = pool_elt_at_index (gm->tunnels, p[0]); if (!disable) { rv = vxlan_gpe_ioam_set (t, hm->has_trace_option, hm->has_pot_option, hm->has_ppc_option, ipv6_set); } else { rv = vxlan_gpe_ioam_clear (t, 0, 0, 0, 0); } return rv; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (vxlan_gpe_set_ioam_rewrite_cmd, static) = { .path = "set vxlan-gpe-ioam", .short_help = "set vxlan-gpe-ioam vxlan [disable]", .function = vxlan_gpe_set_ioam_rewrite_command_fn, }; /* *INDENT-ON* */ clib_error_t * vxlan_gpe_ioam_enable (int has_trace_option, int has_pot_option, int has_ppc_option) { vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main; hm->has_trace_option = has_trace_option; hm->has_pot_option = has_pot_option; hm->has_ppc_option = has_ppc_option; if (hm->has_trace_option) { vxlan_gpe_trace_profile_setup (); } return 0; } clib_error_t * vxlan_gpe_ioam_disable (int has_trace_option, int has_pot_option, int has_ppc_option) { vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main; hm->has_trace_option = has_trace_option; hm->has_pot_option = has_pot_option; hm->has_ppc_option = has_ppc_option; if (!hm->has_trace_option) { vxlan_gpe_trace_profile_cleanup (); } return 0; } void vxlan_gpe_set_next_override (uword next) { vxlan_gpe_ioam_main_t *hm = &vxlan_gpe_ioam_main; hm->decap_v4_next_override = next; return; } static clib_error_t * vxlan_gpe_set_ioam_flags_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { int has_trace_option = 0; int has_pot_option = 0; int has_ppc_option = 0; clib_error_t *rv = 0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "trace")) has_trace_option = 1; else if (unformat (input, "pot")) has_pot_option = 1; else if (unformat (input, "ppc encap")) has_ppc_option = PPC_ENCAP; else if (unformat (input, "ppc decap")) has_ppc_option = PPC_DECAP; else if (unformat (input, "ppc none")) has_ppc_option = PPC_NONE; else break; } rv = vxlan_gpe_ioam_enable (has_trace_option, has_pot_option, has_ppc_option); return rv; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (vxlan_gpe_set_ioam_flags_cmd, static) = { .path = "set vxlan-gpe-ioam rewrite", .short_help = "set vxlan-gpe-ioam [trace] [pot] [ppc ]", .function = vxlan_gpe_set_ioam_flags_command_fn,}; /* *INDENT-ON* */ clib_error_t * clear_vxlan_gpe_ioam_rewrite_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { return (vxlan_gpe_ioam_disable (0, 0, 0)); } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (vxlan_gpe_clear_ioam_flags_cmd, static) = { .path = "clear vxlan-gpe-ioam rewrite", .short_help = "clear vxlan-gpe-ioam rewrite", .function = clear_vxlan_gpe_ioam_rewrite_command_fn, }; /* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */