| /* |
| * mpls.c: mpls |
| * |
| * Copyright (c) 2012 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 <vnet/vnet.h> |
| #include <vnet/mpls/mpls.h> |
| #include <vnet/fib/ip4_fib.h> |
| #include <vnet/fib/mpls_fib.h> |
| |
| const static char* mpls_eos_bit_names[] = MPLS_EOS_BITS; |
| |
| mpls_main_t mpls_main; |
| |
| u8 * format_mpls_unicast_label (u8 * s, va_list * args) |
| { |
| mpls_label_t label = va_arg (*args, mpls_label_t); |
| |
| switch (label) { |
| case MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL: |
| s = format (s, "%s", MPLS_IETF_IPV4_EXPLICIT_NULL_STRING); |
| break; |
| case MPLS_IETF_ROUTER_ALERT_LABEL: |
| s = format (s, "%s", MPLS_IETF_ROUTER_ALERT_STRING); |
| break; |
| case MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL: |
| s = format (s, "%s", MPLS_IETF_IPV6_EXPLICIT_NULL_STRING); |
| break; |
| case MPLS_IETF_IMPLICIT_NULL_LABEL: |
| s = format (s, "%s", MPLS_IETF_IMPLICIT_NULL_STRING); |
| break; |
| case MPLS_IETF_ELI_LABEL: |
| s = format (s, "%s", MPLS_IETF_ELI_STRING); |
| break; |
| case MPLS_IETF_GAL_LABEL: |
| s = format (s, "%s", MPLS_IETF_GAL_STRING); |
| break; |
| case MPLS_LABEL_POP: |
| s = format (s, "pop"); |
| break; |
| default: |
| s = format (s, "%d", label); |
| break; |
| } |
| return s; |
| } |
| |
| uword unformat_mpls_unicast_label (unformat_input_t * input, va_list * args) |
| { |
| mpls_label_t *label = va_arg (*args, mpls_label_t*); |
| |
| if (unformat (input, MPLS_IETF_IPV4_EXPLICIT_NULL_STRING)) |
| *label = MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL; |
| else if (unformat (input, MPLS_IETF_IPV6_EXPLICIT_NULL_STRING)) |
| *label = MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL; |
| else if (unformat (input, MPLS_IETF_ROUTER_ALERT_STRING)) |
| *label = MPLS_IETF_ROUTER_ALERT_LABEL; |
| else if (unformat (input, MPLS_IETF_IMPLICIT_NULL_STRING)) |
| *label = MPLS_IETF_IMPLICIT_NULL_LABEL; |
| else if (unformat (input, MPLS_IETF_IPV4_EXPLICIT_NULL_BRIEF_STRING)) |
| *label = MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL; |
| else if (unformat (input, MPLS_IETF_IPV6_EXPLICIT_NULL_BRIEF_STRING)) |
| *label = MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL; |
| else if (unformat (input, MPLS_IETF_ROUTER_ALERT_BRIEF_STRING)) |
| *label = MPLS_IETF_ROUTER_ALERT_LABEL; |
| else if (unformat (input, MPLS_IETF_IMPLICIT_NULL_BRIEF_STRING)) |
| *label = MPLS_IETF_IMPLICIT_NULL_LABEL; |
| else if (unformat (input, "%d", label)) |
| ; |
| else |
| return (0); |
| |
| return (1); |
| } |
| |
| u8 * format_mpls_eos_bit (u8 * s, va_list * args) |
| { |
| mpls_eos_bit_t eb = va_arg (*args, mpls_eos_bit_t); |
| |
| ASSERT(eb <= MPLS_EOS); |
| |
| s = format(s, "%s", mpls_eos_bit_names[eb]); |
| |
| return (s); |
| } |
| |
| u8 * format_mpls_header (u8 * s, va_list * args) |
| { |
| mpls_unicast_header_t hdr = va_arg (*args, mpls_unicast_header_t); |
| |
| return (format(s, "[%U:%d:%d:%U]", |
| format_mpls_unicast_label, |
| vnet_mpls_uc_get_label(hdr.label_exp_s_ttl), |
| vnet_mpls_uc_get_ttl(hdr.label_exp_s_ttl), |
| vnet_mpls_uc_get_exp(hdr.label_exp_s_ttl), |
| format_mpls_eos_bit, |
| vnet_mpls_uc_get_s(hdr.label_exp_s_ttl))); |
| } |
| |
| uword |
| unformat_mpls_header (unformat_input_t * input, va_list * args) |
| { |
| u8 ** result = va_arg (*args, u8 **); |
| mpls_unicast_header_t _h, * h = &_h; |
| u32 label, label_exp_s_ttl; |
| |
| if (! unformat (input, "MPLS %d", &label)) |
| return 0; |
| |
| label_exp_s_ttl = (label<<12) | (1<<8) /* s-bit */ | 0xFF; |
| h->label_exp_s_ttl = clib_host_to_net_u32 (label_exp_s_ttl); |
| |
| /* Add gre, mpls headers to result. */ |
| { |
| void * p; |
| u32 h_n_bytes = sizeof (h[0]); |
| |
| vec_add2 (*result, p, h_n_bytes); |
| clib_memcpy (p, h, h_n_bytes); |
| } |
| |
| return 1; |
| } |
| |
| uword |
| unformat_mpls_label_net_byte_order (unformat_input_t * input, |
| va_list * args) |
| { |
| u32 * result = va_arg (*args, u32 *); |
| u32 label; |
| |
| if (!unformat (input, "MPLS: label %d", &label)) |
| return 0; |
| |
| label = (label<<12) | (1<<8) /* s-bit set */ | 0xFF /* ttl */; |
| |
| *result = clib_host_to_net_u32 (label); |
| return 1; |
| } |
| |
| u8 * format_mpls_unicast_header_host_byte_order (u8 * s, va_list * args) |
| { |
| mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *); |
| u32 label = h->label_exp_s_ttl; |
| |
| s = format (s, "label %d exp %d, s %d, ttl %d", |
| vnet_mpls_uc_get_label (label), |
| vnet_mpls_uc_get_exp (label), |
| vnet_mpls_uc_get_s (label), |
| vnet_mpls_uc_get_ttl (label)); |
| return s; |
| } |
| |
| u8 * format_mpls_unicast_header_net_byte_order (u8 * s, va_list * args) |
| { |
| mpls_unicast_header_t *h = va_arg(*args, mpls_unicast_header_t *); |
| mpls_unicast_header_t h_host; |
| |
| h_host.label_exp_s_ttl = clib_net_to_host_u32 (h->label_exp_s_ttl); |
| |
| return format (s, "%U", format_mpls_unicast_header_host_byte_order, |
| &h_host); |
| } |
| |
| typedef struct { |
| u32 fib_index; |
| u32 entry_index; |
| u32 dest; |
| u32 s_bit; |
| u32 label; |
| } show_mpls_fib_t; |
| |
| int |
| mpls_dest_cmp(void * a1, void * a2) |
| { |
| show_mpls_fib_t * r1 = a1; |
| show_mpls_fib_t * r2 = a2; |
| |
| return clib_net_to_host_u32(r1->dest) - clib_net_to_host_u32(r2->dest); |
| } |
| |
| int |
| mpls_fib_index_cmp(void * a1, void * a2) |
| { |
| show_mpls_fib_t * r1 = a1; |
| show_mpls_fib_t * r2 = a2; |
| |
| return r1->fib_index - r2->fib_index; |
| } |
| |
| int |
| mpls_label_cmp(void * a1, void * a2) |
| { |
| show_mpls_fib_t * r1 = a1; |
| show_mpls_fib_t * r2 = a2; |
| |
| return r1->label - r2->label; |
| } |
| |
| static clib_error_t * |
| vnet_mpls_local_label (vlib_main_t * vm, |
| unformat_input_t * input, |
| vlib_cli_command_t * cmd) |
| { |
| unformat_input_t _line_input, * line_input = &_line_input; |
| u32 table_id, is_del, is_ip, payload_proto; |
| fib_route_path_t *rpaths = NULL, rpath; |
| mpls_label_t local_label; |
| clib_error_t * error; |
| mpls_eos_bit_t eos; |
| fib_prefix_t pfx; |
| |
| error = NULL; |
| is_ip = 0; |
| table_id = 0; |
| eos = MPLS_EOS; |
| is_del = 0; |
| local_label = MPLS_LABEL_INVALID; |
| clib_memset(&pfx, 0, sizeof(pfx)); |
| payload_proto = DPO_PROTO_MPLS; |
| |
| /* Get a line of input. */ |
| if (! unformat_user (input, unformat_line_input, line_input)) |
| return 0; |
| |
| while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) |
| { |
| if (unformat (line_input, "table %d", &table_id)) |
| ; |
| else if (unformat (line_input, "del")) |
| is_del = 1; |
| else if (unformat (line_input, "add")) |
| is_del = 0; |
| else if (unformat (line_input, "eos")) |
| pfx.fp_eos = MPLS_EOS; |
| else if (unformat (line_input, "non-eos")) |
| pfx.fp_eos = MPLS_NON_EOS; |
| else if (unformat (line_input, "%U/%d", |
| unformat_ip4_address, |
| &pfx.fp_addr.ip4, |
| &pfx.fp_len)) |
| { |
| pfx.fp_proto = FIB_PROTOCOL_IP4; |
| is_ip = 1; |
| } |
| else if (unformat (line_input, "%U/%d", |
| unformat_ip6_address, |
| &pfx.fp_addr.ip6, |
| &pfx.fp_len)) |
| { |
| pfx.fp_proto = FIB_PROTOCOL_IP6; |
| is_ip = 1; |
| } |
| else if (unformat (line_input, "via %U", |
| unformat_fib_route_path, |
| &rpath, &payload_proto)) |
| { |
| pfx.fp_payload_proto = payload_proto; |
| vec_add1(rpaths, rpath); |
| } |
| else if (unformat (line_input, "%d", &local_label)) |
| ; |
| else |
| { |
| error = clib_error_return (0, "unknown input: %U", |
| format_unformat_error, line_input); |
| goto done; |
| } |
| |
| } |
| |
| if (MPLS_LABEL_INVALID == local_label) |
| { |
| error = clib_error_return (0, "local-label required: %U", |
| format_unformat_error, input); |
| goto done; |
| } |
| |
| |
| if (is_ip) |
| { |
| u32 fib_index = fib_table_find(pfx.fp_proto, table_id); |
| |
| if (FIB_NODE_INDEX_INVALID == fib_index) |
| { |
| error = clib_error_return (0, "%U table-id %d does not exist", |
| format_fib_protocol, pfx.fp_proto, table_id); |
| goto done; |
| } |
| |
| if (is_del) |
| { |
| fib_table_entry_local_label_remove(fib_index, &pfx, local_label); |
| } |
| else |
| { |
| fib_table_entry_local_label_add(fib_index, &pfx, local_label); |
| } |
| } |
| else |
| { |
| fib_node_index_t fib_index; |
| |
| if (NULL == rpaths) |
| { |
| error = clib_error_return(0 , "no paths"); |
| goto done; |
| } |
| |
| pfx.fp_proto = FIB_PROTOCOL_MPLS; |
| pfx.fp_len = 21; |
| pfx.fp_label = local_label; |
| pfx.fp_payload_proto = rpaths[0].frp_proto; |
| |
| fib_index = mpls_fib_index_from_table_id(table_id); |
| |
| if (FIB_NODE_INDEX_INVALID == fib_index) |
| { |
| error = clib_error_return (0, "MPLS table-id %d does not exist", |
| table_id); |
| goto done; |
| } |
| |
| if (is_del) |
| { |
| fib_table_entry_path_remove2(fib_index, |
| &pfx, |
| FIB_SOURCE_CLI, |
| rpaths); |
| } |
| else |
| { |
| fib_node_index_t lfe; |
| |
| lfe = fib_table_entry_path_add2(fib_index, |
| &pfx, |
| FIB_SOURCE_CLI, |
| FIB_ENTRY_FLAG_NONE, |
| rpaths); |
| |
| if (FIB_NODE_INDEX_INVALID == lfe) |
| { |
| error = clib_error_return (0, "Failed to create %U-%U in MPLS table-id %d", |
| format_mpls_unicast_label, local_label, |
| format_mpls_eos_bit, eos, |
| table_id); |
| goto done; |
| } |
| } |
| } |
| |
| done: |
| unformat_free (line_input); |
| |
| return error; |
| } |
| |
| VLIB_CLI_COMMAND (mpls_local_label_command, static) = { |
| .path = "mpls local-label", |
| .function = vnet_mpls_local_label, |
| .short_help = |
| "mpls local-label [add|del] <label-value> [eos|non-eos] via " |
| "[next-hop-address] [next-hop-interface] [next-hop-table <value>] [weight " |
| "<value>] [preference <value>] [udp-encap-id <value>] " |
| "[ip4-lookup-in-table <value>] [ip6-lookup-in-table <value>] " |
| "[mpls-lookup-in-table <value>] [resolve-via-host] [resolve-via-attached] " |
| "[rx-ip4|rx-ip6 <interface>] [out-labels <value value value>]", |
| }; |
| |
| clib_error_t * |
| vnet_mpls_table_cmd (vlib_main_t * vm, |
| unformat_input_t * main_input, |
| vlib_cli_command_t * cmdo) |
| { |
| unformat_input_t _line_input, *line_input = &_line_input; |
| clib_error_t *error = NULL; |
| u32 table_id, is_add; |
| u8 *name = NULL; |
| |
| is_add = 1; |
| table_id = ~0; |
| |
| /* Get a line of input. */ |
| if (!unformat_user (main_input, unformat_line_input, line_input)) |
| return 0; |
| |
| while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) |
| { |
| if (unformat (line_input, "%d", &table_id)) |
| ; |
| else if (unformat (line_input, "del")) |
| is_add = 0; |
| else if (unformat (line_input, "add")) |
| is_add = 1; |
| else if (unformat (line_input, "name %s", &name)) |
| ; |
| else |
| { |
| error = unformat_parse_error (line_input); |
| goto done; |
| } |
| } |
| |
| if (~0 == table_id) |
| { |
| error = clib_error_return (0, "No table id"); |
| goto done; |
| } |
| else |
| { |
| if (is_add) |
| { |
| mpls_table_create (table_id, 0, name); |
| } |
| else |
| { |
| mpls_table_delete (table_id, 0); |
| } |
| } |
| |
| done: |
| vec_free (name); |
| unformat_free (line_input); |
| return error; |
| } |
| |
| /*? |
| * This command is used to add or delete MPLS Tables. All |
| * Tables must be explicitly added before that can be used, |
| * Including the default table. |
| ?*/ |
| VLIB_CLI_COMMAND (mpls_table_command, static) = { |
| .path = "mpls table", |
| .short_help = "mpls table [add|del] <table-id>", |
| .function = vnet_mpls_table_cmd, |
| .is_mp_safe = 1, |
| }; |
| |
| static clib_error_t * |
| mpls_init (vlib_main_t * vm) |
| { |
| clib_error_t * error; |
| |
| if ((error = vlib_call_init_function (vm, ip_main_init))) |
| return error; |
| |
| return vlib_call_init_function (vm, mpls_input_init); |
| } |
| |
| VLIB_INIT_FUNCTION (mpls_init); |