/* * Copyright (c) 2021-2023 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 #include #include #include #include #include #include #include #include "strategy_dpo_ctx.h" #include "strategy_dpo_manager.h" #include "strategy.h" #include "faces/face.h" #include "error.h" #include "strategies/dpo_mw.h" #include "infra.h" #include "udp_tunnels/udp_tunnel.h" #include "mapme.h" #include "pg.h" #define FIB_SOURCE_HICN 0x04 // Right after the FIB_SOURCE_INTERFACE priority fib_source_t hicn_fib_src; fib_node_type_t hicn_fib_node_type; ip4_address_t localhost4 = { 0 }; ip6_address_t localhost6 = { 0 }; int hicn_route_get_dpo (const fib_prefix_t *prefix, const dpo_id_t **hicn_dpo, u32 *fib_index) { // fib_prefix_t fib_pfx; const dpo_id_t *load_balance_dpo_id; const dpo_id_t *former_dpo_id; int found = 0, ret = HICN_ERROR_ROUTE_NOT_FOUND; fib_node_index_t fib_entry_index; /* Check if the route already exist in the fib */ /* * ASSUMPTION: we use table 0 which is the default table and it is * already existing and locked */ *fib_index = fib_table_find_or_create_and_lock ( prefix->fp_proto, HICN_FIB_TABLE, hicn_fib_src); fib_entry_index = fib_table_lookup_exact_match (*fib_index, prefix); if (fib_entry_index != FIB_NODE_INDEX_INVALID) { /* Route already existing. We need to update the dpo. */ load_balance_dpo_id = fib_entry_contribute_ip_forwarding (fib_entry_index); /* The dpo is not a load balance dpo as expected */ if (load_balance_dpo_id->dpoi_type != DPO_LOAD_BALANCE) ret = HICN_ERROR_ROUTE_NO_LD; else { /* former_dpo_id is a load_balance dpo */ load_balance_t *lb = load_balance_get (load_balance_dpo_id->dpoi_index); /* FIB entry exists but there is no hicn dpo. */ ret = HICN_ERROR_ROUTE_DPO_NO_HICN; for (int i = 0; i < lb->lb_n_buckets && !found; i++) { former_dpo_id = load_balance_get_bucket_i (lb, i); if (dpo_is_hicn (former_dpo_id)) { *hicn_dpo = former_dpo_id; ret = HICN_ERROR_NONE; found = 1; } } } } /* * Remove the lock from the table. We keep one lock per route, not * per dpo */ fib_table_unlock (*fib_index, prefix->fp_proto, hicn_fib_src); return ret; } int hicn_route_set_strategy (fib_prefix_t *prefix, u8 strategy_id) { const dpo_id_t *hicn_dpo_id; int ret; hicn_dpo_ctx_t *hicn_dpo_ctx; u32 fib_index; ret = hicn_route_get_dpo (prefix, &hicn_dpo_id, &fib_index); if (ret == HICN_ERROR_NONE) { hicn_dpo_ctx = hicn_strategy_dpo_ctx_get (hicn_dpo_id->dpoi_index); const hicn_dpo_vft_t *dpo_vft = hicn_dpo_get_vft_from_id (strategy_id); if (hicn_dpo_ctx == NULL || dpo_vft == NULL) return HICN_ERROR_STRATEGY_NOT_FOUND; dpo_vft->hicn_dpo_update_type (hicn_dpo_ctx); } // Remember to remove the lock from the table when removing the entry return ret; } static int ip_nh_add_del_helper (fib_protocol_t fib_proto, const fib_prefix_t *rpfx, ip46_address_t *nh, u32 sw_if, u32 udp_encap_id, dpo_proto_t proto, u8 is_add) { fib_route_path_t *rpaths = NULL, rpath; u32 fib_index = fib_table_find (fib_proto, 0); clib_memset (&rpath, 0, sizeof (rpath)); if (nh) { rpath.frp_addr = *nh; rpath.frp_sw_if_index = sw_if; } else if (udp_encap_id != ~0) { rpath.frp_udp_encap_id = udp_encap_id; rpath.frp_flags |= FIB_ROUTE_PATH_UDP_ENCAP; } rpath.frp_weight = 1; rpath.frp_proto = proto; vec_add1 (rpaths, rpath); if (is_add) fib_table_entry_path_add2 (fib_index, rpfx, FIB_SOURCE_API, FIB_ENTRY_FLAG_NONE, rpaths); else fib_table_entry_path_remove2 (fib_index, rpfx, FIB_SOURCE_API, rpaths); return 0; } int ip_nh_adj_add_del_helper (fib_protocol_t fib_proto, const fib_prefix_t *rpfx, ip46_address_t *nh, u32 sw_if, u8 is_add) { return ip_nh_add_del_helper ( fib_proto, rpfx, nh, sw_if, ~0, ip46_address_is_ip4 (nh) ? DPO_PROTO_IP4 : DPO_PROTO_IP6, is_add); } int ip_nh_udp_tunnel_add_del_helper (fib_protocol_t fib_proto, const fib_prefix_t *rpfx, u32 uei, dpo_proto_t proto, u8 is_add) { return ip_nh_add_del_helper (fib_proto, rpfx, NULL, ~0, uei, proto, is_add); } static ip46_address_t * get_address (ip46_address_t *nh, u32 sw_if, fib_protocol_t proto) { ip46_address_t *local_address = calloc (1, sizeof (ip46_address_t)); if (proto == FIB_PROTOCOL_IP4) { ip_interface_address_t *interface_address; ip4_address_t *addr = ip4_interface_address_matching_destination ( &ip4_main, &nh->ip4, sw_if, &interface_address); if (addr == NULL) addr = ip4_interface_first_address (&ip4_main, sw_if, &interface_address); if (addr != NULL) ip46_address_set_ip4 (local_address, addr); } else if (proto == FIB_PROTOCOL_IP6) { ip_interface_address_t *interface_address; ip6_interface_address_matching_destination (&ip6_main, &nh->ip6, sw_if, &interface_address); ip6_address_t *addr = NULL; if (interface_address != NULL) addr = (ip6_address_t *) ip_interface_address_get_address ( &ip6_main.lookup_main, interface_address); if (addr == NULL) addr = ip6_interface_first_address (&ip6_main, sw_if); if (addr != NULL) ip46_address_set_ip6 (local_address, addr); } return local_address; } static void sync_hicn_fib_entry (hicn_dpo_ctx_t *fib_entry, hicn_face_id_t **pvec_faces) { hicn_face_id_t *vec_faces = NULL; const dpo_id_t *dpo_loadbalance = fib_entry_contribute_ip_forwarding (fib_entry->fib_entry_index); const fib_entry_t *_fib_entry = fib_entry_get (fib_entry->fib_entry_index); const load_balance_t *lb0 = load_balance_get (dpo_loadbalance->dpoi_index); index_t hicn_fib_entry_index = hicn_strategy_dpo_ctx_get_index (fib_entry); dpo_id_t temp = DPO_INVALID; const dpo_id_t *former_dpo = &temp; int index = 0; #define ADD_FACE(nh) \ do \ { \ /* Careful, this adds a lock on the face if it exists */ \ hicn_face_add (dpo, nh, sw_if, &face_id); \ vec_validate (vec_faces, index); \ vec_faces[index] = face_id; \ (index)++; \ \ /* Face creation can realloc load_balance_t? Seem the fib_tracking does \ * so. */ \ dpo_loadbalance = \ fib_entry_contribute_ip_forwarding (fib_entry->fib_entry_index); \ lb0 = load_balance_get (dpo_loadbalance->dpoi_index); \ } \ while (0) for (int j = 0; j < lb0->lb_n_buckets; j++) { const dpo_id_t *dpo = load_balance_get_bucket_i (lb0, j); int dpo_comparison = dpo_cmp (former_dpo, dpo); former_dpo = dpo; /* * Loadbalancing in ip replicate the dpo in multiple buckets * in order to honor the assigned weights. */ if (dpo_comparison == 0) continue; u32 sw_if = ~0; ip46_address_t *nh = NULL; hicn_face_id_t face_id = HICN_FACE_NULL; if (dpo_is_adj (dpo)) { ip_adjacency_t *adj = adj_get (dpo->dpoi_index); sw_if = adj->rewrite_header.sw_if_index; nh = get_address (&(adj->sub_type.nbr.next_hop), sw_if, fib_entry->proto); ADD_FACE (nh); HICN_DEBUG ("Added new HICN face: %d because of route prefix %U", face_id, format_fib_prefix, &_fib_entry->fe_prefix); } else if (dpo->dpoi_type == dpo_type_udp_ip4 || dpo->dpoi_type == dpo_type_udp_ip6) { dpo_proto_t proto = dpo->dpoi_type == dpo_type_udp_ip4 ? DPO_PROTO_IP4 : DPO_PROTO_IP6; ip46_address_t _nh = { 0 }; nh = &_nh; switch (_fib_entry->fe_prefix.fp_proto) { case FIB_PROTOCOL_IP6: ip46_address_set_ip6 (nh, &localhost6); ADD_FACE (nh); break; case FIB_PROTOCOL_IP4: ip46_address_set_ip4 (nh, &localhost4); ADD_FACE (nh); break; default: continue; } HICN_DEBUG ("Added new UDP face: %d because of route prefix %U", face_id, format_ip_prefix, &_fib_entry->fe_prefix); udp_tunnel_add_existing (dpo->dpoi_index, proto); } else if (dpo_is_pgserver (dpo)) { hicnpg_server_t *pg_server = hicnpg_server_get (dpo->dpoi_index); ADD_FACE (&pg_server->hicn_locator); } } const hicn_dpo_vft_t *strategy_vft = hicn_dpo_get_vft (fib_entry->dpo_type); int i = 0; while (i < fib_entry->entry_count) { u32 idx_nh = vec_search (vec_faces, fib_entry->next_hops[i]); if (idx_nh == ~0) { strategy_vft->hicn_dpo_del_nh (fib_entry->next_hops[i], hicn_fib_entry_index); } else { vec_del1 (vec_faces, idx_nh); /* Remove the lock added by hicn_face_add */ hicn_face_unlock_with_id (fib_entry->next_hops[i]); i++; } } hicn_face_id_t *face_id; vec_foreach (face_id, vec_faces) { strategy_vft->hicn_dpo_add_update_nh (*face_id, hicn_fib_entry_index); /* Remove the lock added by hicn_face_add */ hicn_face_unlock_with_id (*face_id); } *pvec_faces = vec_faces; } static void enable_disable_data_receiving (fib_protocol_t proto, u32 sw_if, u8 is_enable) { if (proto == FIB_PROTOCOL_IP4 && sw_if != ~0) vnet_feature_enable_disable ("ip4-local", "hicn-data-input-ip4", sw_if, is_enable, 0, 0); else if (proto == FIB_PROTOCOL_IP6 && sw_if != ~0) vnet_feature_enable_disable ("ip6-local", "hicn-data-input-ip6", sw_if, is_enable, 0, 0); } walk_rc_t enable_data_receiving_new_fib_entry (vnet_main_t *vnm, vnet_sw_interface_t *si, void *ctx) { fib_protocol_t *proto = (fib_protocol_t *) ctx; enable_disable_data_receiving (*proto, si->sw_if_index, 1); return (WALK_CONTINUE); } walk_rc_t disable_data_receiving_rm_fib_entry (vnet_main_t *vnm, vnet_sw_interface_t *si, void *ctx) { fib_protocol_t *proto = (fib_protocol_t *) ctx; enable_disable_data_receiving (*proto, si->sw_if_index, 0); return (WALK_CONTINUE); } int hicn_route_enable (fib_prefix_t *prefix, hicn_face_id_t **pvec_faces) { int ret = HICN_ERROR_NONE; fib_node_index_t fib_entry_index; /* Check if the route already exist in the fib */ /* * ASSUMPTION: we use table 0 which is the default table and it is * already existing and locked */ u32 fib_index = fib_table_find (prefix->fp_proto, 0); fib_entry_index = fib_table_lookup_exact_match (fib_index, prefix); if (fib_entry_index == FIB_NODE_INDEX_INVALID) { fib_entry_index = fib_table_lookup (fib_index, prefix); fib_route_path_t *paths = fib_entry_encode (fib_entry_index); fib_table_entry_path_add2 (fib_index, prefix, FIB_SOURCE_CLI, FIB_ENTRY_FLAG_NONE, paths); } /* Check if the prefix is already enabled */ u32 fib_hicn_index = fib_table_find (prefix->fp_proto, HICN_FIB_TABLE); fib_node_index_t fib_hicn_entry_index = fib_table_lookup_exact_match (fib_hicn_index, prefix); if (fib_hicn_entry_index == FIB_NODE_INDEX_INVALID) { HICN_DEBUG ( "No route found for %U. Creating DPO and tracking fib prefix.", format_fib_prefix, prefix); dpo_id_t dpo = DPO_INVALID; index_t dpo_idx = ~0; default_dpo.hicn_dpo_create (prefix->fp_proto, 0, 0, &dpo_idx); HICN_DEBUG ("Created new DPO_MW_CTX_T: %d.", dpo_idx); /* the value we got when we registered */ /* * This should be taken from the name?!? the index of the * object */ dpo_set (&dpo, default_dpo.hicn_dpo_get_type (), (ip46_address_is_ip4 (&prefix->fp_addr) ? DPO_PROTO_IP4 : DPO_PROTO_IP6), dpo_idx); HICN_DEBUG ( "dpo_set called with parameters: type=%d, proto=%s, index=%d", default_dpo.hicn_dpo_get_type (), ip46_address_is_ip4 (&prefix->fp_addr) ? "DPO_PROTO_IP4" : "DPO_PROTO_IP6", dpo_idx); hicn_dpo_ctx_t *fib_entry = hicn_strategy_dpo_ctx_get (dpo_idx); fib_node_init (&fib_entry->fib_node, hicn_fib_node_type); fib_node_lock (&fib_entry->fib_node); fib_entry->fib_entry_index = fib_entry_track (fib_index, prefix, hicn_fib_node_type, dpo_idx, &fib_entry->fib_sibling); /* Here is where we create the "via" like route */ /* * For the moment we use the global one the prefix you want * to match Neale suggested -- FIB_SOURCE_HICN the client * that is adding them -- no easy explanation at this timeā€¦ */ CLIB_UNUSED (fib_node_index_t new_fib_node_index) = fib_table_entry_special_dpo_add ( fib_hicn_index, prefix, hicn_fib_src, (FIB_ENTRY_FLAG_EXCLUSIVE | FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT), &dpo); HICN_DEBUG ("Calling sync_hicn_fib_entry"); sync_hicn_fib_entry (fib_entry, pvec_faces); /* We added a route, therefore add one lock to the table */ fib_table_lock (fib_index, prefix->fp_proto, hicn_fib_src); /* Enable the feature to punt data packet every time we enable a new hicn * route For each enable there must be a disable to defenitely disable * the feature * * We cannot enable only the interfaces on which we send out interest * because Data packet might be coming on in different interfaces, as in * che case of mpls tunnels (packets are received from the physical nic, * not the mpls tunnel interface). */ vnet_main_t *vnm = vnet_get_main (); vnet_sw_interface_walk (vnm, enable_data_receiving_new_fib_entry, &(prefix->fp_proto)); dpo_unlock (&dpo); } else { HICN_DEBUG ("Found a route for %U. Updating DPO.", format_ip_prefix, &prefix); const dpo_id_t *load_balance_dpo_id; const dpo_id_t *strategy_dpo_id; /* Route already existing. We need to update the dpo. */ load_balance_dpo_id = fib_entry_contribute_ip_forwarding (fib_hicn_entry_index); /* The dpo is not a load balance dpo as expected */ if (load_balance_dpo_id->dpoi_type != DPO_LOAD_BALANCE) { ret = HICN_ERROR_ROUTE_NO_LD; HICN_ERROR ("DPO is not a load balance."); goto done; } else { load_balance_t *lb = load_balance_get (load_balance_dpo_id->dpoi_index); strategy_dpo_id = load_balance_get_bucket_i (lb, 0); if (!dpo_is_hicn (strategy_dpo_id)) { ret = HICN_ERROR_ROUTE_DPO_NO_HICN; HICN_ERROR ("DPO is not hicn."); goto done; } if (lb->lb_n_buckets > 1) { ret = HICN_ERROR_ROUTE_MLT_LD; HICN_ERROR ("Too many load balance buckets."); goto done; } hicn_dpo_ctx_t *hicn_fib_entry = hicn_strategy_dpo_ctx_get (strategy_dpo_id->dpoi_index); HICN_DEBUG ("Calling sync_hicn_fib_entry"); sync_hicn_fib_entry (hicn_fib_entry, pvec_faces); } } done: return ret; } int hicn_route_disable (fib_prefix_t *prefix) { int ret = HICN_ERROR_NONE; /* Check if the prefix is already enabled */ u32 fib_hicn_index = fib_table_find (prefix->fp_proto, HICN_FIB_TABLE); fib_node_index_t fib_hicn_entry_index = fib_table_lookup_exact_match (fib_hicn_index, prefix); if (fib_hicn_entry_index == FIB_NODE_INDEX_INVALID) { HICN_ERROR ("Route %U not found", format_fib_prefix, prefix); ret = HICN_ERROR_ROUTE_NOT_FOUND; } else { const dpo_id_t *load_balance_dpo_id; const dpo_id_t *strategy_dpo_id; hicn_dpo_ctx_t *hicn_fib_entry; /* Route already existing. We need to update the dpo. */ load_balance_dpo_id = fib_entry_contribute_ip_forwarding (fib_hicn_entry_index); /* The dpo is not a load balance dpo as expected */ if (load_balance_dpo_id->dpoi_type != DPO_LOAD_BALANCE) { HICN_ERROR ("DPO for route %U is not a load balance.", format_ip_prefix, prefix); ret = HICN_ERROR_ROUTE_NO_LD; goto done; } else { load_balance_t *lb = load_balance_get (load_balance_dpo_id->dpoi_index); strategy_dpo_id = load_balance_get_bucket_i (lb, 0); if (!dpo_is_hicn (strategy_dpo_id)) { HICN_ERROR ("ERROR: DPO for route %U is not a hicn.", format_ip_prefix, prefix); ret = HICN_ERROR_ROUTE_DPO_NO_HICN; goto done; } if (lb->lb_n_buckets > 1) { HICN_ERROR ("DPO for route %U contains multiple next hops.", format_ip_prefix, prefix); ret = HICN_ERROR_ROUTE_MLT_LD; goto done; } hicn_fib_entry = hicn_strategy_dpo_ctx_get (strategy_dpo_id->dpoi_index); HICN_DEBUG ("Found from hicn_strategy_dpo_ctx_get with index %d: %p", strategy_dpo_id->dpoi_index, hicn_fib_entry); if (PREDICT_FALSE (!hicn_fib_entry)) { HICN_ERROR ( "hicn_strategy_dpo_ctx_get for index %d returned NULL", strategy_dpo_id->dpoi_index); ret = HICN_ERROR_ROUTE_DPO_NO_HICN; goto done; } for (int i = 0; i < hicn_fib_entry->entry_count; i++) { hicn_strategy_dpo_ctx_del_nh (hicn_fib_entry->next_hops[i], hicn_fib_entry); } hicn_mapme_tfib_clear ((hicn_mapme_tfib_t *) hicn_fib_entry); } HICN_DEBUG ( "Calling fib_entry_untrack and fib_table_entry_special_remove " "for route %U.", format_fib_prefix, prefix); fib_entry_untrack (hicn_fib_entry->fib_entry_index, hicn_fib_entry->fib_sibling); fib_table_entry_special_remove (fib_hicn_index, prefix, hicn_fib_src); fib_node_unlock (&hicn_fib_entry->fib_node); /* Disable the feature to punt data packet every time we enable a new * hicn route */ vnet_main_t *vnm = vnet_get_main (); vnet_sw_interface_walk (vnm, disable_data_receiving_rm_fib_entry, &(prefix->fp_proto)); } done: return ret; } static fib_node_t * hicn_ctx_node_get (fib_node_index_t index) { hicn_dpo_ctx_t *hicn_ctx; hicn_ctx = hicn_strategy_dpo_ctx_get (index); return (&hicn_ctx->fib_node); } static void hicn_fib_last_lock_gone (fib_node_t *node) { } static hicn_dpo_ctx_t * hicn_ctx_from_fib_node (fib_node_t *node) { return ((hicn_dpo_ctx_t *) (((char *) node) - STRUCT_OFFSET_OF (hicn_dpo_ctx_t, fib_node))); } static fib_node_back_walk_rc_t hicn_fib_back_walk_notify (fib_node_t *node, fib_node_back_walk_ctx_t *ctx) { hicn_dpo_ctx_t *fib_entry = hicn_ctx_from_fib_node (node); hicn_face_id_t *vec_faces = NULL; HICN_DEBUG ("Calling sync_hicn_fib_entry from hicn_fib_back_walk_notify"); sync_hicn_fib_entry (fib_entry, &vec_faces); if (vec_faces != NULL) vec_free (vec_faces); return (FIB_NODE_BACK_WALK_CONTINUE); } static void hicn_fib_show_memory (void) { } static const fib_node_vft_t hicn_fib_vft = { .fnv_get = hicn_ctx_node_get, .fnv_last_lock = hicn_fib_last_lock_gone, .fnv_back_walk = hicn_fib_back_walk_notify, .fnv_mem_show = hicn_fib_show_memory, }; fib_table_walk_rc_t enable_data_on_existing_hicn (fib_node_index_t fei, void *ctx) { u32 sw_if = *(u32 *) ctx; const dpo_id_t *load_balance_dpo_id; const dpo_id_t *strategy_dpo_id; /* Route already existing. We need to update the dpo. */ load_balance_dpo_id = fib_entry_contribute_ip_forwarding (fei); /* The dpo is not a load balance dpo as expected */ if (load_balance_dpo_id->dpoi_type != DPO_LOAD_BALANCE) { goto done; } else { load_balance_t *lb = load_balance_get (load_balance_dpo_id->dpoi_index); strategy_dpo_id = load_balance_get_bucket_i (lb, 0); if (!dpo_is_hicn (strategy_dpo_id)) { goto done; } enable_disable_data_receiving ( (fib_protocol_t) strategy_dpo_id->dpoi_proto, sw_if, 1); } done: return (FIB_TABLE_WALK_CONTINUE); } static clib_error_t * set_table_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_add) { if (!is_add) return NULL; int rv = ip_table_bind (FIB_PROTOCOL_IP4, sw_if_index, HICN_FIB_TABLE); if (!rv) { rv = ip_table_bind (FIB_PROTOCOL_IP6, sw_if_index, HICN_FIB_TABLE); if (rv) { /* An error occurred. Bind the interface back to the default fib */ ip_table_bind (FIB_PROTOCOL_IP4, sw_if_index, 0); } } u32 fib_index = fib_table_find (FIB_PROTOCOL_IP4, HICN_FIB_TABLE); if (fib_index != ~0) { /* * Walk the ip4 and ip6 fib tables to discover existing hicn fib entries. * For each of them we need to enable the feature to punt data packets. */ fib_table_walk (fib_index, FIB_PROTOCOL_IP4, enable_data_on_existing_hicn, &sw_if_index); } fib_index = fib_table_find (FIB_PROTOCOL_IP6, HICN_FIB_TABLE); if (fib_index != ~0) { fib_table_walk (fib_index, FIB_PROTOCOL_IP6, enable_data_on_existing_hicn, &sw_if_index); } return rv ? clib_error_return (0, "unable to add hicn table to interface") : 0; } VNET_SW_INTERFACE_ADD_DEL_FUNCTION_PRIO (set_table_interface_add_del, VNET_ITF_FUNC_PRIORITY_HIGH); void hicn_route_init () { vnet_main_t *vnm = vnet_get_main (); vlib_main_t *vm = vlib_get_main (); hicn_fib_src = fib_source_allocate ("hicn", FIB_SOURCE_HICN, FIB_SOURCE_BH_API); hicn_fib_node_type = fib_node_register_new_type ("hicn_route_fib_node", &hicn_fib_vft); ip_table_create (FIB_PROTOCOL_IP4, HICN_FIB_TABLE, 1, (const u8 *) "hicn4"); ip_table_create (FIB_PROTOCOL_IP6, HICN_FIB_TABLE, 1, (const u8 *) "hicn6"); u32 sw_if_index; u8 mac_address[6]; u8 is_specified = 0; u32 user_instance = 0; vnet_create_loopback_interface (&sw_if_index, mac_address, is_specified, user_instance); localhost4.as_u8[0] = 127; localhost4.as_u8[3] = 1; u32 length4 = 32, length6 = 128, is_del = 0, flags = 0; localhost6.as_u8[15] = 1; ip4_add_del_interface_address (vm, sw_if_index, &localhost4, length4, is_del); ip6_add_del_interface_address (vm, sw_if_index, &localhost6, length6, is_del); flags |= VNET_SW_INTERFACE_FLAG_ADMIN_UP; vnet_sw_interface_set_flags (vnm, sw_if_index, flags); } /* * fd.io coding-style-patch-verification: ON * * Local Variables: eval: (c-set-style "gnu") End: */