X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Fgtpu%2Fgtpu.c;h=65c3053cd01380c63b8ae12e3796271c2546f90a;hb=34bfa50b6;hp=7612055ed7f8c54acdd4d1a226ef38cda8e78e02;hpb=e8964fefc7384bf5f5dbf8a9b75b9a3bb2baaeec;p=vpp.git diff --git a/src/plugins/gtpu/gtpu.c b/src/plugins/gtpu/gtpu.c index 7612055ed7f..65c3053cd01 100644 --- a/src/plugins/gtpu/gtpu.c +++ b/src/plugins/gtpu/gtpu.c @@ -24,13 +24,14 @@ #include #include #include +#include #include #include #include #include #include #include - +#include gtpu_main_t gtpu_main; @@ -55,8 +56,8 @@ u8 * format_gtpu_encap_trace (u8 * s, va_list * args) gtpu_encap_trace_t * t = va_arg (*args, gtpu_encap_trace_t *); - s = format (s, "GTPU encap to gtpu_tunnel%d teid %d", - t->tunnel_index, t->teid); + s = format (s, "GTPU encap to gtpu_tunnel%d tteid %d", + t->tunnel_index, t->tteid); return s; } @@ -86,12 +87,20 @@ format_gtpu_tunnel (u8 * s, va_list * args) { gtpu_tunnel_t *t = va_arg (*args, gtpu_tunnel_t *); gtpu_main_t *ngm = >pu_main; + ip4_main_t *im4 = &ip4_main; + ip6_main_t *im6 = &ip6_main; + u8 is_ipv6 = !ip46_address_is_ip4 (&t->dst); + + u32 encap_vrf_id = + is_ipv6 ? im6->fibs[t->encap_fib_index].ft_table_id : + im4->fibs[t->encap_fib_index].ft_table_id; - s = format (s, "[%d] src %U dst %U teid %d fib-idx %d sw-if-idx %d ", + s = format (s, "[%d] src %U dst %U teid %d tteid %d " + "encap-vrf-id %d sw-if-idx %d ", t - ngm->tunnels, format_ip46_address, &t->src, IP46_TYPE_ANY, format_ip46_address, &t->dst, IP46_TYPE_ANY, - t->teid, t->encap_fib_index, t->sw_if_index); + t->teid, t->tteid, encap_vrf_id, t->sw_if_index); s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index); s = format (s, "decap-next-%U ", format_decap_next, t->decap_next_index); @@ -217,7 +226,8 @@ const static fib_node_vft_t gtpu_vft = { #define foreach_copy_field \ -_(teid) \ +_(teid) \ +_(tteid) \ _(mcast_sw_if_index) \ _(encap_fib_index) \ _(decap_next_index) \ @@ -278,7 +288,7 @@ ip_udp_gtpu_rewrite (gtpu_tunnel_t * t, bool is_ip6) /* GTPU header */ gtpu->ver_flags = GTPU_V1_VER | GTPU_PT_GTP; gtpu->type = GTPU_TYPE_GTPU; - gtpu->teid = clib_host_to_net_u32 (t->teid); + gtpu->teid = clib_host_to_net_u32 (t->tteid); t->rewrite = r.rw; /* Now only support 8-byte gtpu header. TBD */ @@ -297,35 +307,6 @@ gtpu_decap_next_is_valid (gtpu_main_t * gtm, u32 is_ip6, u32 decap_next_index) return decap_next_index < r->n_next_nodes; } -static uword -vtep_addr_ref (ip46_address_t * ip) -{ - uword *vtep = ip46_address_is_ip4 (ip) ? - hash_get (gtpu_main.vtep4, ip->ip4.as_u32) : - hash_get_mem (gtpu_main.vtep6, &ip->ip6); - if (vtep) - return ++(*vtep); - ip46_address_is_ip4 (ip) ? - hash_set (gtpu_main.vtep4, ip->ip4.as_u32, 1) : - hash_set_mem_alloc (>pu_main.vtep6, &ip->ip6, 1); - return 1; -} - -static uword -vtep_addr_unref (ip46_address_t * ip) -{ - uword *vtep = ip46_address_is_ip4 (ip) ? - hash_get (gtpu_main.vtep4, ip->ip4.as_u32) : - hash_get_mem (gtpu_main.vtep6, &ip->ip6); - ASSERT (vtep); - if (--(*vtep) != 0) - return *vtep; - ip46_address_is_ip4 (ip) ? - hash_unset (gtpu_main.vtep4, ip->ip4.as_u32) : - hash_unset_mem_free (>pu_main.vtep6, &ip->ip6); - return 0; -} - typedef CLIB_PACKED (union { struct @@ -340,7 +321,7 @@ mcast_shared_get (ip46_address_t * ip) { ASSERT (ip46_address_is_multicast (ip)); uword *p = hash_get_mem (gtpu_main.mcast_shared, ip); - ASSERT (p); + ALWAYS_ASSERT (p); return (mcast_shared_t) { .as_u64 = *p}; @@ -368,8 +349,8 @@ mcast_shared_remove (ip46_address_t * dst) hash_unset_mem_free (>pu_main.mcast_shared, dst); } -int vnet_gtpu_add_del_tunnel - (vnet_gtpu_add_del_tunnel_args_t * a, u32 * sw_if_indexp) +int vnet_gtpu_add_mod_del_tunnel + (vnet_gtpu_add_mod_del_tunnel_args_t * a, u32 * sw_if_indexp) { gtpu_main_t *gtm = >pu_main; gtpu_tunnel_t *t = 0; @@ -379,7 +360,7 @@ int vnet_gtpu_add_del_tunnel u32 sw_if_index = ~0; gtpu4_tunnel_key_t key4; gtpu6_tunnel_key_t key6; - u32 is_ip6 = a->is_ip6; + bool is_ip6 = !ip46_address_is_ip4 (&a->dst); if (!is_ip6) { @@ -394,7 +375,7 @@ int vnet_gtpu_add_del_tunnel p = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6); } - if (a->is_add) + if (a->opn == GTPU_ADD_TUNNEL) { l2input_main_t *l2im = &l2input_main; @@ -416,8 +397,15 @@ int vnet_gtpu_add_del_tunnel foreach_copy_field; #undef _ + /* default to same as local rx teid */ + if (t->tteid == 0) + t->tteid = t->teid; + ip_udp_gtpu_rewrite (t, is_ip6); + /* clear the flow index */ + t->flow_index = ~0; + /* copy the key */ if (is_ip6) hash_set_mem_alloc (>m->gtpu6_tunnel_by_key, &key6, @@ -489,17 +477,17 @@ int vnet_gtpu_add_del_tunnel if (!ip46_address_is_multicast (&t->dst)) { /* Unicast tunnel - - * source the FIB entry for the tunnel's destination - * and become a child thereof. The tunnel will then get poked + * Track the FIB entry for the tunnel's destination. + * The tunnel will then get poked * when the forwarding for the entry updates, and the tunnel can * re-stack accordingly */ - vtep_addr_ref (&t->src); - t->fib_entry_index = fib_table_entry_special_add - (t->encap_fib_index, &tun_dst_pfx, FIB_SOURCE_RR, - FIB_ENTRY_FLAG_NONE); - t->sibling_index = fib_entry_child_add - (t->fib_entry_index, gtm->fib_node_type, t - gtm->tunnels); + vtep_addr_ref (>m->vtep_table, t->encap_fib_index, &t->src); + t->fib_entry_index = fib_entry_track (t->encap_fib_index, + &tun_dst_pfx, + gtm->fib_node_type, + t - gtm->tunnels, + &t->sibling_index); gtpu_tunnel_restack_dpo (t); } else @@ -511,7 +499,8 @@ int vnet_gtpu_add_del_tunnel */ fib_protocol_t fp = fib_ip_proto (is_ip6); - if (vtep_addr_ref (&t->dst) == 1) + if (vtep_addr_ref (>m->vtep_table, + t->encap_fib_index, &t->dst) == 1) { fib_node_index_t mfei; adj_index_t ai; @@ -520,8 +509,9 @@ int vnet_gtpu_add_del_tunnel .frp_addr = zero_addr, .frp_sw_if_index = 0xffffffff, .frp_fib_index = ~0, - .frp_weight = 0, + .frp_weight = 1, .frp_flags = FIB_ROUTE_PATH_LOCAL, + .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; const mfib_prefix_t mpfx = { .fp_proto = fp, @@ -535,17 +525,14 @@ int vnet_gtpu_add_del_tunnel * - the accepting interface is that from the API */ mfib_table_entry_path_update (t->encap_fib_index, - &mpfx, - MFIB_SOURCE_GTPU, - &path, MFIB_ITF_FLAG_FORWARD); + &mpfx, MFIB_SOURCE_GTPU, &path); path.frp_sw_if_index = a->mcast_sw_if_index; path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE; + path.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT; mfei = mfib_table_entry_path_update (t->encap_fib_index, &mpfx, - MFIB_SOURCE_GTPU, - &path, - MFIB_ITF_FLAG_ACCEPT); + MFIB_SOURCE_GTPU, &path); /* * Create the mcast adjacency to send traffic to the group @@ -578,13 +565,23 @@ int vnet_gtpu_add_del_tunnel } else { - /* deleting a tunnel: tunnel must exist */ + /* mod-tteid or deleting a tunnel: tunnel must exist */ if (!p) return VNET_API_ERROR_NO_SUCH_ENTRY; t = pool_elt_at_index (gtm->tunnels, p[0]); sw_if_index = t->sw_if_index; + if (a->opn == GTPU_UPD_TTEID) + { + if (a->tteid == 0) + return VNET_API_ERROR_INVALID_VALUE; + t->tteid = a->tteid; + ip_udp_gtpu_rewrite (t, is_ip6); + return 0; + } + + /* delete tunnel */ vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ ); vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index); si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN; @@ -603,11 +600,14 @@ int vnet_gtpu_add_del_tunnel if (!ip46_address_is_multicast (&t->dst)) { - vtep_addr_unref (&t->src); - fib_entry_child_remove (t->fib_entry_index, t->sibling_index); - fib_table_entry_delete_index (t->fib_entry_index, FIB_SOURCE_RR); + if (t->flow_index != ~0) + vnet_flow_del (vnm, t->flow_index); + + vtep_addr_unref (>m->vtep_table, t->encap_fib_index, &t->src); + fib_entry_untrack (t->fib_entry_index, t->sibling_index); } - else if (vtep_addr_unref (&t->dst) == 0) + else if (vtep_addr_unref (>m->vtep_table, + t->encap_fib_index, &t->dst) == 0) { mcast_shared_remove (&t->dst); } @@ -620,7 +620,7 @@ int vnet_gtpu_add_del_tunnel if (sw_if_indexp) *sw_if_indexp = sw_if_index; - if (a->is_add) + if (a->opn == GTPU_ADD_TUNNEL) { /* register udp ports */ if (!is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU, 1)) @@ -678,7 +678,7 @@ gtpu_add_del_tunnel_command_fn (vlib_main_t * vm, { unformat_input_t _line_input, *line_input = &_line_input; ip46_address_t src, dst; - u8 is_add = 1; + u8 opn = GTPU_ADD_TUNNEL; u8 src_set = 0; u8 dst_set = 0; u8 grp_set = 0; @@ -687,10 +687,10 @@ gtpu_add_del_tunnel_command_fn (vlib_main_t * vm, u32 encap_fib_index = 0; u32 mcast_sw_if_index = ~0; u32 decap_next_index = GTPU_INPUT_NEXT_L2_INPUT; - u32 teid = 0; + u32 teid = 0, tteid = 0; u32 tmp; int rv; - vnet_gtpu_add_del_tunnel_args_t _a, *a = &_a; + vnet_gtpu_add_mod_del_tunnel_args_t _a, *a = &_a; u32 tunnel_sw_if_index; clib_error_t *error = NULL; @@ -706,7 +706,7 @@ gtpu_add_del_tunnel_command_fn (vlib_main_t * vm, { if (unformat (line_input, "del")) { - is_add = 0; + opn = GTPU_DEL_TUNNEL; } else if (unformat (line_input, "src %U", unformat_ip4_address, &src.ip4)) @@ -763,6 +763,10 @@ gtpu_add_del_tunnel_command_fn (vlib_main_t * vm, ; else if (unformat (line_input, "teid %d", &teid)) ; + else if (unformat (line_input, "tteid %d", &tteid)) + ; + else if (unformat (line_input, "upd-tteid %d", &tteid)) + opn = GTPU_UPD_TTEID; else { error = clib_error_return (0, "parse error: '%U'", @@ -771,7 +775,13 @@ gtpu_add_del_tunnel_command_fn (vlib_main_t * vm, } } - if (src_set == 0) + if (teid == 0) + { + error = clib_error_return (0, "tunnel teid specified"); + goto done; + } + + if (src_set == 0 && opn == GTPU_ADD_TUNNEL) { error = clib_error_return (0, "tunnel src address not specified"); goto done; @@ -821,19 +831,18 @@ gtpu_add_del_tunnel_command_fn (vlib_main_t * vm, clib_memset (a, 0, sizeof (*a)); - a->is_add = is_add; - a->is_ip6 = ipv6_set; + a->opn = opn; #define _(x) a->x = x; foreach_copy_field; #undef _ - rv = vnet_gtpu_add_del_tunnel (a, &tunnel_sw_if_index); + rv = vnet_gtpu_add_mod_del_tunnel (a, &tunnel_sw_if_index); switch (rv) { case 0: - if (is_add) + if (opn == GTPU_ADD_TUNNEL) vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (), tunnel_sw_if_index); break; @@ -846,6 +855,10 @@ gtpu_add_del_tunnel_command_fn (vlib_main_t * vm, error = clib_error_return (0, "tunnel does not exist..."); goto done; + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "tx teid not specified..."); + goto done; + default: error = clib_error_return (0, "vnet_gtpu_add_del_tunnel returned %d", rv); @@ -861,30 +874,31 @@ done: /*? * Add or delete a GTPU Tunnel. * - * GTPU provides the features needed to allow L2 bridge domains (BDs) + * GTPU can be used to transport Ethernet packets as its PDU type to + * provides allow L2 network or bridge domains (BDs) * to span multiple servers. This is done by building an L2 overlay on * top of an L3 network underlay using GTPU tunnels. * - * This makes it possible for servers to be co-located in the same data - * center or be separated geographically as long as they are reachable - * through the underlay L3 network. - * - * You can refer to this kind of L2 overlay bridge domain as a GTPU - * (Virtual eXtensible VLAN) segment. + * GTPU can also be used to transport IP packetes as its PDU type to + * allow IP forwarding over underlay network, e.g. between RAN and UPF + * for mobility deplyments. * * @cliexpar * Example of how to create a GTPU Tunnel: - * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 encap-vrf-id 7} + * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 tteid 55 encap-vrf-id 7} * Example of how to delete a GTPU Tunnel: - * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 del} + * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 encap-vrf-id 7 del} + * Example of how to update tx TEID of a GTPU Tunnel: + * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 encap-vrf-id 7 upd-tteid 55} ?*/ /* *INDENT-OFF* */ VLIB_CLI_COMMAND (create_gtpu_tunnel_command, static) = { .path = "create gtpu tunnel", .short_help = - "create gtpu tunnel src " - " {dst |group } teid " - " [encap-vrf-id ] [decap-next [l2|ip4|ip6|node ]] [del]", + "create gtpu tunnel src " + " {dst |group }" + " teid [tteid ] [encap-vrf-id ]" + " [decap-next [l2|ip4|ip6|node ]] [del | upd-tteid ]", .function = gtpu_add_del_tunnel_command_fn, }; /* *INDENT-ON* */ @@ -916,7 +930,7 @@ show_gtpu_tunnel_command_fn (vlib_main_t * vm, * @cliexpar * Example of how to display the GTPU Tunnel entries: * @cliexstart{show gtpu tunnel} - * [0] src 10.0.3.1 dst 10.0.3.3 teid 13 encap_fib_index 0 sw_if_index 5 decap_next l2 + * [0] src 10.0.3.1 dst 10.0.3.3 teid 13 tx-teid 55 encap_fib_index 0 sw_if_index 5 decap_next l2 * @cliexend ?*/ /* *INDENT-OFF* */ @@ -1092,7 +1106,137 @@ set_ip6_gtpu_bypass (vlib_main_t * vm, VLIB_CLI_COMMAND (set_interface_ip6_gtpu_bypass_command, static) = { .path = "set interface ip6 gtpu-bypass", .function = set_ip6_gtpu_bypass, - .short_help = "set interface ip gtpu-bypass [del]", + .short_help = "set interface ip6 gtpu-bypass [del]", +}; +/* *INDENT-ON* */ + +int +vnet_gtpu_add_del_rx_flow (u32 hw_if_index, u32 t_index, int is_add) +{ + gtpu_main_t *gtm = >pu_main; + gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index); + vnet_main_t *vnm = vnet_get_main (); + if (is_add) + { + if (t->flow_index == ~0) + { + vnet_flow_t flow = { + .actions = + VNET_FLOW_ACTION_REDIRECT_TO_NODE | VNET_FLOW_ACTION_MARK | + VNET_FLOW_ACTION_BUFFER_ADVANCE, + .mark_flow_id = t_index + gtm->flow_id_start, + .redirect_node_index = gtpu4_flow_input_node.index, + .buffer_advance = sizeof (ethernet_header_t) + + sizeof (ip4_header_t) + sizeof (udp_header_t), + .type = VNET_FLOW_TYPE_IP4_GTPU, + .ip4_gtpu = { + .protocol.prot = IP_PROTOCOL_UDP, + .src_addr.addr = t->dst.ip4, + .src_addr.mask.as_u32 = ~0, + .dst_addr.addr = t->src.ip4, + .dst_addr.mask.as_u32 = ~0, + .teid = t->teid, + } + , + }; + vnet_flow_add (vnm, &flow, &t->flow_index); + } + + return vnet_flow_enable (vnm, t->flow_index, hw_if_index); + } + + /* flow index is removed when the tunnel is deleted */ + return vnet_flow_disable (vnm, t->flow_index, hw_if_index); +} + +u32 +vnet_gtpu_get_tunnel_index (u32 sw_if_index) +{ + gtpu_main_t *gtm = >pu_main; + + if (sw_if_index >= vec_len (gtm->tunnel_index_by_sw_if_index)) + return ~0; + return gtm->tunnel_index_by_sw_if_index[sw_if_index]; +} + +static clib_error_t * +gtpu_offload_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + vnet_main_t *vnm = vnet_get_main (); + u32 rx_sw_if_index = ~0; + u32 hw_if_index = ~0; + int is_add = 1; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "hw %U", unformat_vnet_hw_interface, vnm, + &hw_if_index)) + continue; + if (unformat (line_input, "rx %U", unformat_vnet_sw_interface, vnm, + &rx_sw_if_index)) + continue; + if (unformat (line_input, "del")) + { + is_add = 0; + continue; + } + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, line_input); + } + + if (rx_sw_if_index == ~0) + return clib_error_return (0, "missing rx interface"); + if (hw_if_index == ~0) + return clib_error_return (0, "missing hw interface"); + + u32 t_index = vnet_gtpu_get_tunnel_index (rx_sw_if_index);; + if (t_index == ~0) + return clib_error_return (0, "%U is not a gtpu tunnel", + format_vnet_sw_if_index_name, vnm, + rx_sw_if_index); + + gtpu_main_t *gtm = >pu_main; + gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index); + + /* first support ipv4 hw offload */ + if (!ip46_address_is_ip4 (&t->dst)) + return clib_error_return (0, "currently only IPV4 tunnels are supported"); + + /* inner protocol should be IPv4/IPv6 */ + if ((t->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT) && + (t->decap_next_index != GTPU_INPUT_NEXT_IP6_INPUT)) + return clib_error_return (0, + "currently only inner IPv4/IPv6 protocol is supported"); + + vnet_hw_interface_t *hw_if = vnet_get_hw_interface (vnm, hw_if_index); + ip4_main_t *im = &ip4_main; + u32 rx_fib_index = + vec_elt (im->fib_index_by_sw_if_index, hw_if->sw_if_index); + + if (t->encap_fib_index != rx_fib_index) + return clib_error_return (0, "interface/tunnel fib mismatch"); + + if (vnet_gtpu_add_del_rx_flow (hw_if_index, t_index, is_add)) + return clib_error_return (0, "error %s flow", + is_add ? "enabling" : "disabling"); + + return 0; +} + + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (gtpu_offload_command, static) = { + .path = "set flow-offload gtpu", + .short_help = + "set flow-offload gtpu hw rx [del]", + .function = gtpu_offload_command_fn, }; /* *INDENT-ON* */ @@ -1104,11 +1248,14 @@ gtpu_init (vlib_main_t * vm) gtm->vnet_main = vnet_get_main (); gtm->vlib_main = vm; + vnet_flow_get_range (gtm->vnet_main, "gtpu", 1024 * 1024, + >m->flow_id_start); + /* initialize the ip6 hash */ gtm->gtpu6_tunnel_by_key = hash_create_mem (0, sizeof (gtpu6_tunnel_key_t), sizeof (uword)); - gtm->vtep6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword)); + gtm->vtep_table = vtep_table_create (); gtm->mcast_shared = hash_create_mem (0, sizeof (ip46_address_t), sizeof (mcast_shared_t));