本文主要是介绍VPP添加接口IP地址,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
如下命令添加接口IPv4地址。
vpp# set interface ip address port6 192.168.1.11/24
配置IP地址由函数ip4_add_del_interface_address_internal处理,主要参数为接口索引,IPv4地址和掩码长度(address_length)。
clib_error_t *
ip4_add_del_interface_address (vlib_main_t * vm,u32 sw_if_index,ip4_address_t * address, u32 address_length, u32 is_del)
{ return ip4_add_del_interface_address_internal(vm, sw_if_index, address, address_length, is_del);
}
接口IPv4地址相关检查
以下函数配置接口sw_if_index的IPv4地址address,前缀长度为address_length。回环接口local0,以及未配置exact-match的VLAN子接口不支持配置IP地址。
static clib_error_t *
ip4_add_del_interface_address_internal (vlib_main_t * vm,u32 sw_if_index, ip4_address_t * address,u32 address_length, u32 is_del)
{vnet_main_t *vnm = vnet_get_main ();ip4_main_t *im = &ip4_main;ip_lookup_main_t *lm = &im->lookup_main;ip4_address_fib_t ip4_af, *addr_fib = 0;error = vnet_sw_interface_supports_addressing (vnm, sw_if_index);if (error) {vnm->api_errno = VNET_API_ERROR_UNSUPPORTED;return error;}
初始化ip4_address_fib_t结构ip4_af,
ip4_addr_fib_init (&ip4_af, address,vec_elt (im->fib_index_by_sw_if_index, sw_if_index));vec_add1 (addr_fib, ip4_af);
检查地址是否与接口所在FIB中其它接口的地址冲突。首先,遍历接口池pool(vnm->interface_main.sw_interface),找到与配置接口sw_if_index所在FIB相同的接口。
/* there is no support for adj-fib handling in the presence of overlapping* subnets on interfaces. Easy fix - disallow overlapping subnets, like most routers do.*/if (!is_del) {/* When adding an address check that it does not conflictwith an existing address on any interface in this table. */ip_interface_address_t *ia;vnet_sw_interface_t *sif;pool_foreach (sif, vnm->interface_main.sw_interfaces){if (im->fib_index_by_sw_if_index[sw_if_index] ==im->fib_index_by_sw_if_index[sif->sw_if_index]){
随后,遍历接口上的所有IPv4地址,检查接口上地址是否与将要配置的地址网段冲突,包括三种情况:第一,与已有地址相同网段;第二,包含已有地址;第三,已有地址包含要配置的地址网段。两次调用ip4_destination_matches_route函数实现网段冲突检查,分别用已有地址的掩码长度和配置地址的掩码长度做参数。
如果网段冲突,返回错误。但是,如果发生第一种情况,并且地址冲突的接口与参数中配置接口相同,在前缀不相等的情况下,允许配置,即允许在同一接口上配置同一网段的多个不同IPv4地址。
foreach_ip_interface_address (&im->lookup_main, ia, sif->sw_if_index, 0 /* honor unnumbered */ ,({ip4_address_t * x = ip_interface_address_get_address(&im->lookup_main, ia);if (ip4_destination_matches_route(im, address, x, ia->address_length) ||ip4_destination_matches_route (im, x, address, address_length)){/* an intf may have >1 addr from the same prefix */if ((sw_if_index == sif->sw_if_index) && (ia->address_length == address_length) && (x->as_u32 != address->as_u32))continue;if (ia->flags & IP_INTERFACE_ADDRESS_FLAG_STALE) continue;/* error if the length or intf was different */vnm->api_errno = VNET_API_ERROR_ADDRESS_IN_USE;error = clib_error_create("failed to add %U on %U which conflicts with %U for interface %U",format_ip4_address_and_length, address, address_length, format_vnet_sw_if_index_name, vnm, sw_if_index,format_ip4_address_and_length, x, ia->address_length, format_vnet_sw_if_index_name, vnm, sif->sw_if_index);goto done;}}));}}}
在结构ip_lookup_main_t的哈希成员address_to_if_address_index中查找是否已经存在配置地址对应的地址索引if_address_index,如果此地址索引存在,由地址池中取得接口地址结构ip_interface_address_t。
if_address_index = ip_interface_address_find (lm, addr_fib, address_length);
如果地址结构ia设置了过期标志,分两种情况处理。第一种,ia中的接口索引值与参数sw_if_index相等,认为是更新操作,去掉过期标志,处理结束。第二种,认为此地址前缀由一个接口移动到另外一个接口,删除旧接口上的地址(还是调用当前函数),在新接口上添加新地址,新地址的索引值填充到了参数if_address_index。
如果找到的地址索引if_address_index有效,并且没有过期标志,表明地址冲突,报错。如果if_address_index无效,直接调用地址添加函数ip_interface_address_add。
if (!is_del) { if (~0 != if_address_index){ ip_interface_address_t *ia = pool_elt_at_index (lm->if_address_pool, if_address_index);if (ia->flags & IP_INTERFACE_ADDRESS_FLAG_STALE) {if (ia->sw_if_index == sw_if_index) {/* re-adding an address during the replace action. consdier this the update. clear the flag and we're done */ia->flags &= ~IP_INTERFACE_ADDRESS_FLAG_STALE;goto done;} else {/* The prefix is moving from one interface to another. delete the stale and add the new */ip4_add_del_interface_address_internal (vm, ia->sw_if_index, address, address_length, 1);ia = NULL;error = ip_interface_address_add (lm, sw_if_index, addr_fib, address_length, &if_address_index);}} else {vnm->api_errno = VNET_API_ERROR_DUPLICATE_IF_ADDRESS;error = clib_error_create("Prefix %U already found on interface %U",lm->format_address_and_length, addr_fib, address_length, format_vnet_sw_if_index_name, vnm, ia->sw_if_index);}} elseerror = ip_interface_address_add (lm, sw_if_index, addr_fib, address_length, &if_address_index);}if (error) goto done;
使能接口的Feature。
ip4_sw_interface_enable_disable (sw_if_index, !is_del);
处理接口IP地址相关路由。在接口UP状态时,调用ip4_add_interface_routes增加接口路由。函数最后调用监听接口地址变化的回调函数。
ip4_mfib_interface_enable_disable (sw_if_index, !is_del);/* intf addr routes are added/deleted on admin up/down */if (vnet_sw_interface_is_admin_up (vnm, sw_if_index)) {if (!is_del)ip4_add_interface_routes (sw_if_index, im, ip4_af.fib_index, pool_elt_at_index(lm->if_address_pool, if_address_index));}ip4_add_del_interface_address_callback_t *cb;vec_foreach (cb, im->add_del_interface_address_callbacks)cb->function (im, cb->function_opaque, sw_if_index, address, address_length, if_address_index, is_del);
接口地址添加
对于IPv4前缀长度不能大于32,IPv6前缀长度不能大于128,前缀长度也不能为0,非法前缀长度,返回错误。
clib_error_t *
ip_interface_address_add (ip_lookup_main_t * lm, u32 sw_if_index,void *addr_fib, u32 address_length, u32 * result_if_address_index)
{vnet_main_t *vnm = vnet_get_main ();ip_interface_address_t *a, *prev;u32 pi, /* previous index */ ai, hi; /* head index *//* Verify given length. */if ((address_length == 0) || (lm->is_ip6 && address_length > 128) ||(!lm->is_ip6 && address_length > 32)) {vnm->api_errno = VNET_API_ERROR_ADDRESS_LENGTH_MISMATCH;return clib_error_create("%U wrong length for interface %U",lm->format_address_and_length, addr_fib, address_length, format_vnet_sw_if_index_name, vnm, sw_if_index);}
确保向量if_address_pool_index_by_sw_if_index的sw_if_index位置已经分配值,以便保存接口地址的索引。
vec_validate_init_empty (lm->if_address_pool_index_by_sw_if_index, sw_if_index, ~0);
之后,由if_address_pool地址池中分配ip_interface_address_t结构a,初始化a。
同一接口的所有ip_interface_address_t地址结构组成一个双向链表,第一个ip_interface_address_t结构的索引保存在lm->if_address_pool_index_by_sw_if_index[sw_if_index]中。新添加的地址放在链表的末尾。
最后,返回新分配的地址池索引result_if_address_index;
pool_get_zero (lm->if_address_pool, a);ai = a - lm->if_address_pool;hi = pi = lm->if_address_pool_index_by_sw_if_index[sw_if_index];prev = 0;while (pi != (u32) ~ 0) {prev = pool_elt_at_index (lm->if_address_pool, pi);pi = prev->next_this_sw_interface;}pi = prev ? prev - lm->if_address_pool : (u32) ~ 0;a->address_key = mhash_set (&lm->address_to_if_address_index, addr_fib, ai, /* old_value */ 0);a->address_length = address_length;a->sw_if_index = sw_if_index;a->flags = 0;a->prev_this_sw_interface = pi;a->next_this_sw_interface = ~0;if (prev) prev->next_this_sw_interface = ai;lm->if_address_pool_index_by_sw_if_index[sw_if_index] = (hi != ~0) ? hi : ai;*result_if_address_index = ai;
接口特性feature
记录接口im->ip_enabled_by_sw_if_index[sw_if_index]对应的数值,仅在首次使能的时候,开启ip4-unicast和ip4-multicast特性。仅在最后一次禁止时,关闭ip4-unicast和ip4-multicast特性。
void ip4_sw_interface_enable_disable (u32 sw_if_index, u32 is_enable)
{ip4_main_t *im = &ip4_main;vnet_main_t *vnm = vnet_get_main ();vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);vec_validate_init_empty (im->ip_enabled_by_sw_if_index, sw_if_index, 0);/* enable/disable only on the 1<->0 transition */if (is_enable) {if (1 != ++im->ip_enabled_by_sw_if_index[sw_if_index])return;} else {ASSERT (im->ip_enabled_by_sw_if_index[sw_if_index] > 0);if (0 != --im->ip_enabled_by_sw_if_index[sw_if_index])return;}
如果使能,增加接口l3_if_count计数,否则,递减l3_if_count计数。最后,调用向量enable_disable_interface_callbacks中的注册函数,例如arp_enable_disable_interface,其操作arp-reply特性。
vnet_feature_enable_disable ("ip4-unicast", "ip4-not-enabled", sw_if_index, !is_enable, 0, 0);vnet_feature_enable_disable ("ip4-multicast", "ip4-not-enabled", sw_if_index, !is_enable, 0, 0);if (is_enable)hi->l3_if_count++;else if (hi->l3_if_count)hi->l3_if_count--;{ip4_enable_disable_interface_callback_t *cb;vec_foreach (cb, im->enable_disable_interface_callbacks)cb->function (im, cb->function_opaque, sw_if_index, is_enable);}
参见如下接口features的变化。
vpp# show interface features port6
ip4-unicast:none configuredip4-multicast:none configuredvpp# show interface features port6
ip4-unicast:ip4-not-enabledip4-multicast:ip4-not-enabled
接口地址相关多播路由表项
增加Multicast FIB标志的特殊表项。
void ip4_mfib_interface_enable_disable (u32 sw_if_index, int is_enable)
{ const fib_route_path_t path = {.frp_proto = DPO_PROTO_IP4,.frp_addr = zero_addr,.frp_sw_if_index = sw_if_index,.frp_fib_index = ~0,.frp_weight = 1,.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT,};mfib_index = ip4_mfib_table_get_index_for_sw_if_index(sw_if_index);for (ii = 0; ii < ARRAY_LEN(ip4_specials); ii++) {if (is_enable) {mfib_table_entry_path_update(mfib_index, &ip4_specials[ii],MFIB_SOURCE_SPECIAL, MFIB_ENTRY_FLAG_NONE, &path);}}
如下两个特殊表项:(,224.0.0.1)/32和(,224.0.0.2)/32。
static const mfib_prefix_t ip4_specials[] =
{/* ALL prefixes are in network order */{/* (*,224.0.0.1)/32 - all hosts */.fp_grp_addr = {.ip4.data_u32 = 0x010000e0,},.fp_len = 32,.fp_proto = FIB_PROTOCOL_IP4,},{/* (*,224.0.0.2)/32 - all routers */.fp_grp_addr = {.ip4.data_u32 = 0x020000e0,},.fp_len = 32,.fp_proto = FIB_PROTOCOL_IP4,},
};
接口地址路由
根据IPv4地址,添加接口相关路由。
static void
ip4_add_interface_routes (u32 sw_if_index, ip4_main_t * im, u32 fib_index, ip_interface_address_t * a)
{ip_lookup_main_t *lm = &im->lookup_main;ip4_address_t *address = ip_interface_address_get_address (lm, a);fib_prefix_t pfx = {.fp_len = 32,.fp_proto = FIB_PROTOCOL_IP4,.fp_addr.ip4 = *address,};/* set special routes for the prefix if needed */ip4_add_interface_prefix_routes (im, sw_if_index, fib_index, a);
函数ip4_add_interface_prefix_routes首先添加的前缀路由如下,表项标志connected,attached:
vpp# show fib entry 38
38@9.9.9.0/24 fib:0 index:38 locks:2interface refs:1 entry-flags:connected,attached, src-flags:added,contributing,active, cover:-1path-list:[74] locks:2 uPRF-list:43 len:1 itfs:[2, ]path:[80] pl-index:74 ip4 weight=1 pref=0 attached: oper-flags:resolved, cfg-flags:glean,port7forwarding: unicast-ip4-chain[@0]: dpo-load-balance: [proto:ip4 index:40 buckets:1 uRPF:43 to:[0:0]][0] [@4]: ipv4-glean: [src:9.9.9.0/24] port7: mtu:9000 next:1 flags:[] ffffffffffff0060e0689ff00806Delegates:covered:[{entry:41}]Children:
之后是添加一个针对接口地址前缀的路由(9.9.9.0/32),掩码为32位。
vpp# show fib entry 39
39@9.9.9.0/32 fib:0 index:39 locks:2interface refs:1 entry-flags:drop,uRPF-exempt, src-flags:added,contributing,active, cover:-1path-list:[73] locks:2 flags:drop, uPRF-list:41 len:0 itfs:[]path:[79] pl-index:73 ip4 weight=1 pref=0 special: cfg-flags:drop,[@0]: dpo-drop ip4forwarding: unicast-ip4-chain[@0]: dpo-load-balance: [proto:ip4 index:41 buckets:1 uRPF:44 to:[0:0]][0] [@0]: dpo-drop ip4Delegates:Children:
之后再添加网段的广播地址路由(9.9.9.255/32)。
vpp# show fib entry 40
40@9.9.9.255/32 fib:0 index:40 locks:2interface refs:1 entry-flags:drop,uRPF-exempt, src-flags:added,contributing,active, cover:-1path-list:[75] locks:2 flags:drop, uPRF-list:45 len:0 itfs:[]path:[81] pl-index:75 ip4 weight=1 pref=0 special: cfg-flags:drop,[@0]: dpo-drop ip4forwarding: unicast-ip4-chain[@0]: dpo-load-balance: [proto:ip4 index:42 buckets:1 uRPF:46 to:[0:0]][0] [@0]: dpo-drop ip4Delegates:Children:
如果通过命令,在接口上配置了分类表功能,以下代码增加DPO_CLASSIFY类型的DPO。
if (sw_if_index < vec_len (lm->classify_table_index_by_sw_if_index)){u32 classify_table_index = lm->classify_table_index_by_sw_if_index[sw_if_index];if (classify_table_index != (u32) ~ 0) {dpo_id_t dpo = DPO_INVALID;dpo_set (&dpo, DPO_CLASSIFY, DPO_PROTO_IP4, classify_dpo_create (DPO_PROTO_IP4, classify_table_index));fib_table_entry_special_dpo_add (fib_index, &pfx, FIB_SOURCE_CLASSIFY, FIB_ENTRY_FLAG_NONE, &dpo);dpo_reset (&dpo);}}
最后增加接口ipv4地址对应的本地直连路由(FIB_ENTRY_FLAG_CONNECTED | FIB_ENTRY_FLAG_LOCAL)。
fib_table_entry_update_one_path (fib_index, &pfx, FIB_SOURCE_INTERFACE,(FIB_ENTRY_FLAG_CONNECTED | FIB_ENTRY_FLAG_LOCAL),DPO_PROTO_IP4, &pfx.fp_addr, sw_if_index,/* invalid FIB index */ ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
}
如下为接口地址9.9.9.1对应的本地直连(connected,local)路由。
vpp# show fib entry 41
41@9.9.9.1/32 fib:0 index:41 locks:2interface refs:1 entry-flags:connected,local, src-flags:added,contributing,active, cover:38path-list:[77] locks:2 flags:local, uPRF-list:48 len:0 itfs:[]path:[83] pl-index:77 ip4 weight=1 pref=0 receive: oper-flags:resolved, cfg-flags:local,[@0]: dpo-receive: 9.9.9.1 on port7forwarding: unicast-ip4-chain[@0]: dpo-load-balance: [proto:ip4 index:43 buckets:1 uRPF:48 to:[0:0]][0] [@14]: dpo-receive: 9.9.9.1 on port7Delegates:Children:
这篇关于VPP添加接口IP地址的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!