diff options
Diffstat (limited to 'src/plugins/vrrp')
-rw-r--r-- | src/plugins/vrrp/node.c | 348 | ||||
-rw-r--r-- | src/plugins/vrrp/vrrp.c | 34 |
2 files changed, 381 insertions, 1 deletions
diff --git a/src/plugins/vrrp/node.c b/src/plugins/vrrp/node.c index 3355198efe2..1585a48a6ac 100644 --- a/src/plugins/vrrp/node.c +++ b/src/plugins/vrrp/node.c @@ -728,6 +728,354 @@ VLIB_REGISTER_NODE (vrrp6_input_node) = }, }; +typedef struct +{ + u32 sw_if_index; + u8 is_ipv6; + ip46_address_t src, dst; +} vrrp_accept_owner_trace_t; + +/* packet trace format function */ +static u8 * +format_vrrp_accept_owner_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 *); + vrrp_accept_owner_trace_t *t = va_arg (*args, vrrp_accept_owner_trace_t *); + int ip_ver = 4, ip_type = IP46_TYPE_IP4; + + if (t->is_ipv6) + { + ip_ver = 6; + ip_type = IP46_TYPE_IP6; + } + + s = format (s, "IPv%d sw_if_index %d %U -> %U", + ip_ver, t->sw_if_index, + format_ip46_address, &t->src, ip_type, + format_ip46_address, &t->dst, ip_type); + + return s; +} + +#define foreach_vrrp_accept_owner_error \ +_(RECEIVED, "VRRP owner accept packets received") \ +_(PROCESSED, "VRRP owner accept advertisements processed") + +typedef enum +{ +#define _(sym,str) VRRP_ACCEPT_OWNER_ERROR_##sym, + foreach_vrrp_accept_owner_error +#undef _ + VRRP_ACCEPT_OWNER_N_ERROR, +} vrrp_accept_owner_error_t; + +static char *vrrp_accept_owner_error_strings[] = { +#define _(sym,string) string, + foreach_vrrp_accept_owner_error +#undef _ +}; + +typedef enum +{ + VRRP_ACCEPT_OWNER_NEXT_PROCESS, + VRRP_ACCEPT_OWNER_N_NEXT, +} vrrp_accept_owner_next_t; + +static_always_inline void +vrrp_accept_owner_next_node (u32 sw_if_index, u8 vr_id, u8 is_ipv6, + u32 *next_index, u32 *error) +{ + vrrp_vr_t *vr = vrrp_vr_lookup (sw_if_index, vr_id, is_ipv6); + + if (vr && (vr->runtime.state == VRRP_VR_STATE_MASTER) && + (vr->config.flags & VRRP_VR_ACCEPT)) + { + *next_index = VRRP_ACCEPT_OWNER_NEXT_PROCESS; + *error = VRRP_ACCEPT_OWNER_ERROR_PROCESSED; + } +} + +static_always_inline uword +vrrp_accept_owner_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame, u8 is_ipv6) +{ + u32 n_left_from, *from, *to_next; + u32 next_index = node->cached_next_index; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + while (n_left_from > 0) + { + u32 n_left_to_next; + + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 2 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + u32 next0, next1; + u32 error0, error1; + vrrp_header_t *vrrp0, *vrrp1; + ip4_header_t *ip40, *ip41; + ip6_header_t *ip60, *ip61; + u32 sw_if_index0, sw_if_index1; + + bi0 = from[0]; + bi1 = from[1]; + + to_next[0] = bi0; + to_next[1] = bi1; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + /* most packets will follow feature arc */ + vnet_feature_next (&next0, b0); + vnet_feature_next (&next1, b1); + + error0 = error1 = VRRP_ACCEPT_OWNER_ERROR_RECEIVED; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; + + /* find VRRP advertisements which should be sent to VRRP node */ + if (is_ipv6) + { + ip60 = vlib_buffer_get_current (b0); + ip61 = vlib_buffer_get_current (b1); + + if (PREDICT_FALSE (ip60->protocol == IP_PROTOCOL_VRRP)) + { + vrrp0 = (vrrp_header_t *) (ip60 + 1); + vrrp_accept_owner_next_node (sw_if_index0, vrrp0->vr_id, + is_ipv6, &next0, &error0); + } + if (PREDICT_FALSE (ip61->protocol == IP_PROTOCOL_VRRP)) + { + vrrp1 = (vrrp_header_t *) (ip61 + 1); + vrrp_accept_owner_next_node (sw_if_index1, vrrp1->vr_id, + is_ipv6, &next1, &error1); + } + } + else + { + ip40 = vlib_buffer_get_current (b0); + ip41 = vlib_buffer_get_current (b1); + + if (PREDICT_FALSE (ip40->protocol == IP_PROTOCOL_VRRP)) + { + vrrp0 = (vrrp_header_t *) (ip40 + 1); + vrrp_accept_owner_next_node (sw_if_index0, vrrp0->vr_id, + is_ipv6, &next0, &error0); + } + if (PREDICT_FALSE (ip41->protocol == IP_PROTOCOL_VRRP)) + { + vrrp1 = (vrrp_header_t *) (ip41 + 1); + vrrp_accept_owner_next_node (sw_if_index1, vrrp1->vr_id, + is_ipv6, &next1, &error1); + } + } + + b0->error = node->errors[error0]; + b1->error = node->errors[error1]; + + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + vrrp_accept_owner_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + + t->sw_if_index = sw_if_index0; + t->is_ipv6 = is_ipv6; + if (is_ipv6) + { + ip6_address_copy (&t->src.ip6, &ip60->src_address); + ip6_address_copy (&t->dst.ip6, &ip60->dst_address); + } + else + { + t->src.ip4.as_u32 = ip40->src_address.as_u32; + t->dst.ip4.as_u32 = ip40->dst_address.as_u32; + } + } + + if (b1->flags & VLIB_BUFFER_IS_TRACED) + { + vrrp_accept_owner_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + + t->sw_if_index = sw_if_index1; + t->is_ipv6 = is_ipv6; + if (is_ipv6) + { + ip6_address_copy (&t->src.ip6, &ip61->src_address); + ip6_address_copy (&t->dst.ip6, &ip61->dst_address); + } + else + { + t->src.ip4.as_u32 = ip41->src_address.as_u32; + t->dst.ip4.as_u32 = ip41->dst_address.as_u32; + } + } + + from += 2; + n_left_from -= 2; + to_next += 2; + n_left_to_next -= 2; + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0; + u32 error0; + vrrp_header_t *vrrp0; + ip4_header_t *ip4; + ip6_header_t *ip6; + u32 sw_if_index0; + + bi0 = from[0]; + to_next[0] = bi0; + + b0 = vlib_get_buffer (vm, bi0); + + /* most packets will follow feature arc */ + vnet_feature_next (&next0, b0); + + error0 = VRRP_ACCEPT_OWNER_ERROR_RECEIVED; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + + /* find VRRP advertisements which should be sent to VRRP node */ + if (is_ipv6) + { + ip6 = vlib_buffer_get_current (b0); + + if (PREDICT_FALSE (ip6->protocol == IP_PROTOCOL_VRRP)) + { + vrrp0 = (vrrp_header_t *) (ip6 + 1); + vrrp_accept_owner_next_node (sw_if_index0, vrrp0->vr_id, + is_ipv6, &next0, &error0); + } + } + else + { + ip4 = vlib_buffer_get_current (b0); + + if (PREDICT_FALSE (ip4->protocol == IP_PROTOCOL_VRRP)) + { + vrrp0 = (vrrp_header_t *) (ip4 + 1); + vrrp_accept_owner_next_node (sw_if_index0, vrrp0->vr_id, + is_ipv6, &next0, &error0); + } + } + + b0->error = node->errors[error0]; + + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + vrrp_accept_owner_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + + t->sw_if_index = sw_if_index0; + t->is_ipv6 = is_ipv6; + if (is_ipv6) + { + ip6_address_copy (&t->src.ip6, &ip6->src_address); + ip6_address_copy (&t->dst.ip6, &ip6->dst_address); + } + else + { + t->src.ip4.as_u32 = ip4->src_address.as_u32; + t->dst.ip4.as_u32 = ip4->dst_address.as_u32; + } + } + + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + vlib_validate_buffer_enqueue_x1 (vm, node, next_index, + to_next, n_left_to_next, + bi0, next0); + } + + vlib_put_next_frame (vm, node, next_index, n_left_to_next); + } + + return frame->n_vectors; +} + +VLIB_NODE_FN (vrrp4_accept_owner_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return vrrp_accept_owner_input_inline (vm, node, frame, 0); +} + +VLIB_REGISTER_NODE (vrrp4_accept_owner_input_node) = +{ + .name = "vrrp4-accept-owner-input", + .vector_size = sizeof (u32), + .format_trace = format_vrrp_accept_owner_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(vrrp_accept_owner_error_strings), + .error_strings = vrrp_accept_owner_error_strings, + + .n_next_nodes = VRRP_ACCEPT_OWNER_N_NEXT, + + .next_nodes = { + [VRRP_ACCEPT_OWNER_NEXT_PROCESS] = "vrrp4-input", + }, +}; + +VNET_FEATURE_INIT (vrrp4_accept_owner_mc, static) = +{ + .arc_name = "ip4-multicast", + .node_name = "vrrp4-accept-owner-input", + .runs_before = VNET_FEATURES ("ip4-mfib-forward-lookup"), +}; + +VLIB_NODE_FN (vrrp6_accept_owner_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return vrrp_accept_owner_input_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (vrrp6_accept_owner_input_node) = +{ + .name = "vrrp6-accept-owner-input", + .vector_size = sizeof (u32), + .format_trace = format_vrrp_accept_owner_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(vrrp_accept_owner_error_strings), + .error_strings = vrrp_accept_owner_error_strings, + + .n_next_nodes = VRRP_ACCEPT_OWNER_N_NEXT, + + .next_nodes = { + [VRRP_ACCEPT_OWNER_NEXT_PROCESS] = "vrrp6-input", + }, +}; + +VNET_FEATURE_INIT (vrrp6_accept_owner_mc, static) = +{ + .arc_name = "ip4-multicast", + .node_name = "vrrp6-accept-owner-input", + .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"), +}; + static clib_error_t * vrrp_input_init (vlib_main_t *vm) { diff --git a/src/plugins/vrrp/vrrp.c b/src/plugins/vrrp/vrrp.c index 13cdba6f5e7..564cdb0551f 100644 --- a/src/plugins/vrrp/vrrp.c +++ b/src/plugins/vrrp/vrrp.c @@ -140,7 +140,10 @@ vrrp_vr_transition_intf (vrrp_vr_t * vr, vrrp_vr_state_t new_state) { vrrp_intf_t *intf; const char *arc_name = 0, *node_name = 0; + const char *mc_arc_name = 0, *mc_node_name = 0; u8 is_ipv6 = vrrp_vr_is_ipv6 (vr); + u32 *vr_index; + int n_master_accept = 0; /* only need to do something if entering or leaving master state */ if ((vr->runtime.state != VRRP_VR_STATE_MASTER) && @@ -151,14 +154,19 @@ vrrp_vr_transition_intf (vrrp_vr_t * vr, vrrp_vr_state_t new_state) { arc_name = "ip6-local"; node_name = "vrrp6-nd-input"; + mc_arc_name = "ip6-multicast"; + mc_node_name = "vrrp6-accept-owner-input"; } else { arc_name = "arp"; node_name = "vrrp4-arp-input"; + mc_arc_name = "ip4-multicast"; + mc_node_name = "vrrp4-accept-owner-input"; } intf = vrrp_intf_get (vr->config.sw_if_index); + if (new_state == VRRP_VR_STATE_MASTER) { intf->n_master_vrs[is_ipv6]++; @@ -175,6 +183,30 @@ vrrp_vr_transition_intf (vrrp_vr_t * vr, vrrp_vr_state_t new_state) if (intf->n_master_vrs[is_ipv6]) intf->n_master_vrs[is_ipv6]--; } + + /* accept mode enabled, count the other master VRs w/ accept mode on intf */ + if (vrrp_vr_accept_mode_enabled (vr)) + { + vec_foreach (vr_index, intf->vr_indices[is_ipv6]) + { + vrrp_vr_t *intf_vr = vrrp_vr_lookup_index (*vr_index); + + if (intf_vr == vr) + continue; + + if (vrrp_vr_accept_mode_enabled (intf_vr) && + (intf_vr->runtime.state == VRRP_VR_STATE_MASTER)) + n_master_accept++; + } + + /* If no others, enable or disable the feature based on new state */ + if (!n_master_accept) + vnet_feature_enable_disable (mc_arc_name, mc_node_name, + vr->config.sw_if_index, + (new_state == VRRP_VR_STATE_MASTER), + NULL, 0); + } + } /* If accept mode enabled, add/remove VR addresses from interface */ @@ -310,7 +342,7 @@ vrrp_vr_transition (vrrp_vr_t * vr, vrrp_vr_state_t new_state, void *data) /* add/delete virtual IP addrs if accept_mode is true */ vrrp_vr_transition_addrs (vr, new_state); - /* enable/disable arp/ND input features if necessary */ + /* enable/disable input features if necessary */ vrrp_vr_transition_intf (vr, new_state); /* add/delete virtual MAC address on NIC if necessary */ |