/* * 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 #include mpls_main_t mpls_main; u8 * format_mpls_gre_tx_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); mpls_gre_tx_trace_t * t = va_arg (*args, mpls_gre_tx_trace_t *); mpls_main_t * mm = &mpls_main; if (t->lookup_miss) s = format (s, "MPLS: lookup miss"); else { s = format (s, "MPLS: tunnel %d labels %U len %d src %U dst %U", t->tunnel_id, format_mpls_encap_index, mm, t->mpls_encap_index, clib_net_to_host_u16 (t->length), format_ip4_address, &t->src.as_u8, format_ip4_address, &t->dst.as_u8); } return s; } u8 * format_mpls_eth_tx_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); mpls_eth_tx_trace_t * t = va_arg (*args, mpls_eth_tx_trace_t *); mpls_main_t * mm = &mpls_main; if (t->lookup_miss) s = format (s, "MPLS: lookup miss"); else { s = format (s, "MPLS: tunnel %d labels %U len %d tx_sw_index %d dst %U", t->tunnel_id, format_mpls_encap_index, mm, t->mpls_encap_index, clib_net_to_host_u16 (t->length), t->tx_sw_if_index, format_ethernet_address, t->dst); } return s; } u8 * format_mpls_eth_header_with_length (u8 * s, va_list * args) { ethernet_header_t * h = va_arg (*args, ethernet_header_t *); mpls_unicast_header_t * m = (mpls_unicast_header_t *)(h+1); u32 max_header_bytes = va_arg (*args, u32); uword header_bytes; header_bytes = sizeof (h[0]); if (max_header_bytes != 0 && header_bytes > max_header_bytes) return format (s, "ethernet header truncated"); s = format (s, "ETHERNET-MPLS label %d", vnet_mpls_uc_get_label (clib_net_to_host_u32 (m->label_exp_s_ttl))); return s; } u8 * format_mpls_gre_header_with_length (u8 * s, va_list * args) { gre_header_t * h = va_arg (*args, gre_header_t *); mpls_unicast_header_t * m = (mpls_unicast_header_t *)(h+1); u32 max_header_bytes = va_arg (*args, u32); uword header_bytes; header_bytes = sizeof (h[0]); if (max_header_bytes != 0 && header_bytes > max_header_bytes) return format (s, "gre header truncated"); s = format (s, "GRE-MPLS label %d", vnet_mpls_uc_get_label (clib_net_to_host_u32 (m->label_exp_s_ttl))); return s; } u8 * format_mpls_gre_header (u8 * s, va_list * args) { gre_header_t * h = va_arg (*args, gre_header_t *); return format (s, "%U", format_mpls_gre_header_with_length, h, 0); } uword unformat_mpls_gre_header (unformat_input_t * input, va_list * args) { u8 ** result = va_arg (*args, u8 **); gre_header_t _g, * g = &_g; mpls_unicast_header_t _h, * h = &_h; u32 label, label_exp_s_ttl; if (! unformat (input, "MPLS %d", &label)) return 0; g->protocol = clib_host_to_net_u16 (GRE_PROTOCOL_mpls_unicast); 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 g_n_bytes = sizeof (g[0]); u32 h_n_bytes = sizeof (h[0]); vec_add2 (*result, p, g_n_bytes); memcpy (p, g, g_n_bytes); vec_add2 (*result, p, h_n_bytes); 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; } mpls_encap_t * mpls_encap_by_fib_and_dest (mpls_main_t * mm, u32 rx_fib, u32 dst_address) { uword * p; mpls_encap_t * e; u64 key; key = ((u64)rx_fib<<32) | ((u64) dst_address); p = hash_get (mm->mpls_encap_by_fib_and_dest, key); if (!p) return 0; e = pool_elt_at_index (mm->encaps, p[0]); return e; } int vnet_mpls_add_del_encap (ip4_address_t *dest, u32 fib_id, u32 *labels_host_byte_order, u32 policy_tunnel_index, int no_dst_hash, u32 * indexp, int is_add) { mpls_main_t * mm = &mpls_main; ip4_main_t * im = &ip4_main; mpls_encap_t * e; u32 label_net_byte_order, label_host_byte_order; u32 fib_index; u64 key; uword *p; int i; p = hash_get (im->fib_index_by_table_id, fib_id); if (! p) return VNET_API_ERROR_NO_SUCH_FIB; fib_index = p[0]; key = ((u64)fib_index<<32) | ((u64) dest->as_u32); if (is_add) { pool_get (mm->encaps, e); memset (e, 0, sizeof (*e)); for (i = 0; i < vec_len (labels_host_byte_order); i++) { mpls_unicast_header_t h; label_host_byte_order = labels_host_byte_order[i]; /* Reformat label into mpls_unicast_header_t */ label_host_byte_order <<= 12; if (i == vec_len(labels_host_byte_order) - 1) label_host_byte_order |= 1<<8; /* S=1 */ label_host_byte_order |= 0xff; /* TTL=FF */ label_net_byte_order = clib_host_to_net_u32 (label_host_byte_order); h.label_exp_s_ttl = label_net_byte_order; vec_add1 (e->labels, h); } if (no_dst_hash == 0) hash_set (mm->mpls_encap_by_fib_and_dest, key, e - mm->encaps); if (indexp) *indexp = e - mm->encaps; if (policy_tunnel_index != ~0) return vnet_mpls_policy_tunnel_add_rewrite (mm, e, policy_tunnel_index); } else { p = hash_get (mm->mpls_encap_by_fib_and_dest, key); if (!p) return VNET_API_ERROR_NO_SUCH_LABEL; e = pool_elt_at_index (mm->encaps, p[0]); vec_free (e->labels); vec_free (e->rewrite); pool_put(mm->encaps, e); if (no_dst_hash == 0) hash_unset (mm->mpls_encap_by_fib_and_dest, key); } return 0; } static clib_error_t * mpls_add_encap_command_fn (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { u32 fib_id; u32 *labels = 0; u32 this_label; ip4_address_t dest; u32 policy_tunnel_index = ~0; int no_dst_hash = 0; int rv; int fib_set = 0; int dest_set = 0; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "fib %d", &fib_id)) fib_set = 1; else if (unformat (input, "dest %U", unformat_ip4_address, &dest)) dest_set = 1; else if (unformat (input, "no-dst-hash")) no_dst_hash = 1; else if (unformat (input, "label %d", &this_label)) vec_add1 (labels, this_label); else if (unformat (input, "policy-tunnel %d", &policy_tunnel_index)) ; else break; } if (fib_set == 0) return clib_error_return (0, "fib-id missing"); if (dest_set == 0) return clib_error_return (0, "destination IP address missing"); if (vec_len (labels) == 0) return clib_error_return (0, "label stack missing"); rv = vnet_mpls_add_del_encap (&dest, fib_id, labels, policy_tunnel_index, no_dst_hash, 0 /* indexp */, 1 /* is_add */); vec_free (labels); switch (rv) { case 0: break; case VNET_API_ERROR_NO_SUCH_FIB: return clib_error_return (0, "fib id %d unknown", fib_id); default: return clib_error_return (0, "vnet_mpls_add_del_encap returned %d", rv); } return 0; } VLIB_CLI_COMMAND (mpls_add_encap_command, static) = { .path = "mpls encap add", .short_help = "mpls encap add label