+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,
+ .ip4_gtpu = {
+ .protocol = 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 */
+ if (t->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT)
+ return clib_error_return (0,
+ "currently only inner IPV4 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 <inerface-name> rx <tunnel-name> [del]",
+ .function = gtpu_offload_command_fn,
+};
+/* *INDENT-ON* */
+