From c8e264b60e405e60b666cca62d5c96c20a9cf3bd Mon Sep 17 00:00:00 2001 From: vivek Date: Thu, 25 Feb 2016 19:30:53 +0000 Subject: [PATCH] Quagga: Implement VRF change semantics for an interface Implement VRF change semantics for an interface to be invoked when an interface is moved from one VRF (e.g., the Default) to another. This includes the message definition as well as updating, deleting or adding the interface from clients, depending on their interest in the VRFs (old and new). Also handle replay of the addresses on the interface upon VRF change, if required. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Reviewed-by: Don Slice Ticket: CM-9527 Reviewed By: CCR-4174 Testing Done: Manual tests of various scenarios --- lib/log.c | 3 +- lib/zclient.c | 31 +++++++ lib/zclient.h | 3 + lib/zebra.h | 3 +- zebra/interface.c | 185 ++++++++++++++++++++++++++------------ zebra/interface.h | 1 + zebra/redistribute.c | 68 ++++++++++++++ zebra/redistribute.h | 2 + zebra/redistribute_null.c | 6 ++ zebra/zserv.c | 76 ++++++++++++---- zebra/zserv.h | 4 + 11 files changed, 304 insertions(+), 78 deletions(-) diff --git a/lib/log.c b/lib/log.c index ad610050f..fefc0db52 100644 --- a/lib/log.c +++ b/lib/log.c @@ -874,7 +874,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_REDISTRIBUTE_IPV4_ADD), DESC_ENTRY (ZEBRA_REDISTRIBUTE_IPV4_DEL), DESC_ENTRY (ZEBRA_REDISTRIBUTE_IPV6_ADD), - DESC_ENTRY (ZEBRA_REDISTRIBUTE_IPV6_DEL) + DESC_ENTRY (ZEBRA_REDISTRIBUTE_IPV6_DEL), + DESC_ENTRY (ZEBRA_INTERFACE_VRF_UPDATE) }; #undef DESC_ENTRY diff --git a/lib/zclient.c b/lib/zclient.c index 82f0c26e5..655eda817 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1211,6 +1211,33 @@ zebra_interface_nbr_address_read (int type, struct stream *s, vrf_id_t vrf_id) return ifc; } +struct interface * +zebra_interface_vrf_update_read (struct stream *s, vrf_id_t vrf_id, + vrf_id_t *new_vrf_id) +{ + unsigned int ifindex; + struct interface *ifp; + vrf_id_t new_id = VRF_DEFAULT; + + /* Get interface index. */ + ifindex = stream_getl (s); + + /* Lookup interface. */ + ifp = if_lookup_by_index_vrf (ifindex, vrf_id); + if (ifp == NULL) + { + zlog_warn ("INTERFACE_VRF_UPDATE: Cannot find IF %u in VRF %d", + ifindex, vrf_id); + return NULL; + } + + /* Fetch new VRF Id. */ + new_id = stream_getw (s); + + *new_vrf_id = new_id; + return ifp; +} + /* Zebra client message read function. */ static int zclient_read (struct thread *thread) @@ -1357,6 +1384,10 @@ zclient_read (struct thread *thread) if (zclient->interface_down) (*zclient->interface_down) (command, zclient, length, vrf_id); break; + case ZEBRA_INTERFACE_VRF_UPDATE: + if (zclient->interface_vrf_update) + (*zclient->interface_vrf_update) (command, zclient, length, vrf_id); + break; case ZEBRA_IPV4_ROUTE_ADD: if (zclient->ipv4_route_add) (*zclient->ipv4_route_add) (command, zclient, length, vrf_id); diff --git a/lib/zclient.h b/lib/zclient.h index e58870828..49f73b3e9 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -98,6 +98,7 @@ struct zclient int (*interface_bfd_dest_update) (int, struct zclient *, uint16_t, vrf_id_t); int (*interface_nbr_address_add) (int, struct zclient *, uint16_t, vrf_id_t); int (*interface_nbr_address_delete) (int, struct zclient *, uint16_t, vrf_id_t); + int (*interface_vrf_update) (int, struct zclient *, uint16_t, vrf_id_t); int (*ipv4_route_add) (int, struct zclient *, uint16_t, vrf_id_t); int (*ipv4_route_delete) (int, struct zclient *, uint16_t, vrf_id_t); int (*ipv6_route_add) (int, struct zclient *, uint16_t, vrf_id_t); @@ -200,6 +201,8 @@ extern struct interface *zebra_interface_add_read (struct stream *, vrf_id_t); extern struct interface *zebra_interface_state_read (struct stream *s, vrf_id_t); extern struct connected *zebra_interface_address_read (int, struct stream *, vrf_id_t); extern struct nbr_connected *zebra_interface_nbr_address_read (int, struct stream *, vrf_id_t); +extern struct interface * zebra_interface_vrf_update_read (struct stream *s, vrf_id_t vrf_id, + vrf_id_t *new_vrf_id); extern void zebra_interface_if_set_value (struct stream *, struct interface *); extern void zebra_router_id_update_read (struct stream *s, struct prefix *rid); extern int zapi_ipv4_route (u_char, struct zclient *, struct prefix_ipv4 *, diff --git a/lib/zebra.h b/lib/zebra.h index 3d38a4c5c..2945f1519 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -445,7 +445,8 @@ struct in_pktinfo #define ZEBRA_VRF_UNREGISTER 42 #define ZEBRA_VRF_ADD 43 #define ZEBRA_VRF_DELETE 44 -#define ZEBRA_MESSAGE_MAX 45 +#define ZEBRA_INTERFACE_VRF_UPDATE 45 +#define ZEBRA_MESSAGE_MAX 46 /* Marker value used in new Zserv, in the byte location corresponding * the command value in the old zserv header. To allow old and new diff --git a/zebra/interface.c b/zebra/interface.c index 7b1937991..5a47f60fa 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -54,6 +54,8 @@ const char *rtadv_pref_strs[] = { "medium", "high", "INVALID", "low", 0 }; #endif /* HAVE_RTADV */ +static void if_down_del_nbr_connected (struct interface *ifp); + struct zebra_ns *dzns; /* Called when new interface is added. */ @@ -482,32 +484,64 @@ if_add_update (struct interface *ifp) } } -/* Handle an interface delete event */ -void -if_delete_update (struct interface *ifp) +/* Install connected routes corresponding to an interface. */ +static void +if_install_connected (struct interface *ifp) { + struct listnode *node; + struct listnode *next; struct connected *ifc; struct prefix *p; - struct route_node *rn; - struct zebra_if *zebra_if; - zebra_if = ifp->info; + if (ifp->connected) + { + for (ALL_LIST_ELEMENTS (ifp->connected, node, next, ifc)) + { + p = ifc->address; - if (if_is_up(ifp)) + if (p->family == AF_INET) + connected_up_ipv4 (ifp, ifc); + else if (p->family == AF_INET6) + connected_up_ipv6 (ifp, ifc); + } + } +} + +/* Uninstall connected routes corresponding to an interface. */ +static void +if_uninstall_connected (struct interface *ifp) +{ + struct listnode *node; + struct listnode *next; + struct connected *ifc; + struct prefix *p; + + if (ifp->connected) { - zlog_err ("interface %s vrf %u index %d is still up while being deleted.", - ifp->name, ifp->vrf_id, ifp->ifindex); - return; + for (ALL_LIST_ELEMENTS (ifp->connected, node, next, ifc)) + { + p = ifc->address; + + if (p->family == AF_INET) + connected_down_ipv4 (ifp, ifc); + else if (p->family == AF_INET6) + connected_down_ipv6 (ifp, ifc); + } } +} - /* Mark interface as inactive */ - UNSET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE); - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("interface %s vrf %u index %d is now inactive.", - ifp->name, ifp->vrf_id, ifp->ifindex); +/* Uninstall and delete connected routes corresponding to an interface. */ +/* TODO - Check why IPv4 handling here is different from install or if_down */ +static void +if_delete_connected (struct interface *ifp) +{ + struct connected *ifc; + struct prefix *p; + struct route_node *rn; + struct zebra_if *zebra_if; + + zebra_if = ifp->info; - /* Delete connected routes from the kernel. */ if (ifp->connected) { struct listnode *node; @@ -571,7 +605,6 @@ if_delete_update (struct interface *ifp) rn->info = NULL; route_unlock_node (rn); } -#ifdef HAVE_IPV6 else if (p->family == AF_INET6) { connected_down_ipv6 (ifp, ifc); @@ -589,13 +622,36 @@ if_delete_update (struct interface *ifp) connected_free (ifc); } } -#endif /* HAVE_IPV6 */ else { last = node; } } } +} + +/* Handle an interface delete event */ +void +if_delete_update (struct interface *ifp) +{ + if (if_is_up(ifp)) + { + zlog_err ("interface %s vrf %u index %d is still up while being deleted.", + ifp->name, ifp->vrf_id, ifp->ifindex); + return; + } + + /* Mark interface as inactive */ + UNSET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE); + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("interface %s vrf %u index %d is now inactive.", + ifp->name, ifp->vrf_id, ifp->ifindex); + + /* Delete connected routes from the kernel. */ + if_delete_connected (ifp); + + /* Send out notification on interface delete. */ zebra_interface_delete_update (ifp); if_unlink_per_ns(ifp); @@ -608,6 +664,55 @@ if_delete_update (struct interface *ifp) ifp->ifindex = IFINDEX_INTERNAL; } +/* VRF change for an interface */ +void +if_handle_vrf_change (struct interface *ifp, vrf_id_t vrf_id) +{ + vrf_id_t old_vrf_id; + + old_vrf_id = ifp->vrf_id; + + /* Uninstall connected routes. */ + if_uninstall_connected (ifp); + + /* Delete any IPv4 neighbors created to implement RFC 5549 */ + if_nbr_ipv6ll_to_ipv4ll_neigh_del_all (ifp); + + /* Delete all neighbor addresses learnt through IPv6 RA */ + if_down_del_nbr_connected (ifp); + + /* Suppress RAs on this interface, if enabled. */ + ipv6_nd_suppress_ra_set (ifp, RA_SUPPRESS); + + /* Send out notification on interface VRF change. */ + /* This is to issue an UPDATE or a DELETE, as appropriate. */ + zebra_interface_vrf_update_del (ifp, vrf_id); + + /* update VRF */ + if_update_vrf (ifp, ifp->name, strlen (ifp->name), vrf_id); + + /* Send out notification on interface VRF change. */ + /* This is to issue an ADD, if needed. */ + zebra_interface_vrf_update_add (ifp, old_vrf_id); + + /* Install connected routes (in new VRF). */ + if_install_connected (ifp); + + /* Enable RAs on this interface, if IPv6 addresses are present. */ + if (ipv6_address_configured(ifp)) + ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); + + /* Due to connected route change, schedule RIB processing for both old + * and new VRF. + */ + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug ("%u: IF %s VRF change, scheduling RIB processing", + ifp->vrf_id, ifp->name); + rib_update (old_vrf_id, RIB_UPDATE_IF_CHANGE); + rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE); +} + + /* Handle VRF addition */ void vrf_add_update (struct vrf *vrfp) @@ -728,11 +833,6 @@ if_down_del_nbr_connected (struct interface *ifp) void if_up (struct interface *ifp) { - struct listnode *node; - struct listnode *next; - struct connected *ifc; - struct prefix *p; - /* Notify the protocol daemons. */ if (ifp->ptm_enable && (ifp->ptm_status == ZEBRA_PTM_STATUS_DOWN)) { zlog_warn("%s: interface %s hasn't passed ptm check\n", __func__, @@ -744,20 +844,7 @@ if_up (struct interface *ifp) if_nbr_ipv6ll_to_ipv4ll_neigh_add_all (ifp); /* Install connected routes to the kernel. */ - if (ifp->connected) - { - for (ALL_LIST_ELEMENTS (ifp->connected, node, next, ifc)) - { - p = ifc->address; - - if (p->family == AF_INET) - connected_up_ipv4 (ifp, ifc); -#ifdef HAVE_IPV6 - else if (p->family == AF_INET6) - connected_up_ipv6 (ifp, ifc); -#endif /* HAVE_IPV6 */ - } - } + if_install_connected (ifp); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug ("%u: IF %s up, scheduling RIB processing", @@ -770,29 +857,11 @@ if_up (struct interface *ifp) void if_down (struct interface *ifp) { - struct listnode *node; - struct listnode *next; - struct connected *ifc; - struct prefix *p; - /* Notify to the protocol daemons. */ zebra_interface_down_update (ifp); - /* Delete connected routes from the kernel. */ - if (ifp->connected) - { - for (ALL_LIST_ELEMENTS (ifp->connected, node, next, ifc)) - { - p = ifc->address; - - if (p->family == AF_INET) - connected_down_ipv4 (ifp, ifc); -#ifdef HAVE_IPV6 - else if (p->family == AF_INET6) - connected_down_ipv6 (ifp, ifc); -#endif /* HAVE_IPV6 */ - } - } + /* Uninstall connected routes from the kernel. */ + if_uninstall_connected (ifp); if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug ("%u: IF %s down, scheduling RIB processing", diff --git a/zebra/interface.h b/zebra/interface.h index 3d8a82d45..ed5150776 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -225,6 +225,7 @@ extern void if_flags_update (struct interface *, uint64_t); extern int if_subnet_add (struct interface *, struct connected *); extern int if_subnet_delete (struct interface *, struct connected *); extern int ipv6_address_configured (struct interface *ifp); +extern void if_handle_vrf_change (struct interface *ifp, vrf_id_t vrf_id); extern void vrf_delete_update (struct vrf *vrfp); extern void vrf_add_update (struct vrf *vrfp); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 552524dbc..993028519 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -558,6 +558,74 @@ zebra_interface_address_delete_update (struct interface *ifp, } } +/* Interface VRF change. May need to delete from clients not interested in + * the new VRF. Note that this function is invoked *prior* to the VRF change. + */ +void +zebra_interface_vrf_update_del (struct interface *ifp, vrf_id_t new_vrf_id) +{ + struct listnode *node, *nnode; + struct zserv *client; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug ("MESSAGE: ZEBRA_INTERFACE_VRF_UPDATE/DEL %s VRF Id %u -> %u", + ifp->name, ifp->vrf_id, new_vrf_id); + + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + { + /* Skip clients not interested in both VRFs. */ + if (!vrf_bitmap_check (client->ifinfo, ifp->vrf_id) && + !vrf_bitmap_check (client->ifinfo, new_vrf_id)) + continue; + + if (!vrf_bitmap_check (client->ifinfo, new_vrf_id)) + { + /* Need to delete if the client is not interested in the new VRF. */ + zsend_interface_update (ZEBRA_INTERFACE_DOWN, client, ifp); + client->ifdel_cnt++; + zsend_interface_delete (client, ifp); + } + else if (vrf_bitmap_check (client->ifinfo, ifp->vrf_id)) + { + /* Client is interested in both VRFs, inform about the change. */ + zsend_interface_vrf_update (client, ifp, new_vrf_id); + } + } +} + +/* Interface VRF change. This function is invoked *post* VRF change and sends an + * add to clients who are interested in the new VRF but not in the old VRF. + */ +void +zebra_interface_vrf_update_add (struct interface *ifp, vrf_id_t old_vrf_id) +{ + struct listnode *node, *nnode; + struct zserv *client; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug ("MESSAGE: ZEBRA_INTERFACE_VRF_UPDATE/ADD %s VRF Id %u -> %u", + ifp->name, old_vrf_id, ifp->vrf_id); + + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + { + /* Skip clients interested in both VRFs - they would've got an Update. */ + if (vrf_bitmap_check (client->ifinfo, ifp->vrf_id) && + vrf_bitmap_check (client->ifinfo, old_vrf_id)) + continue; + + /* Skip clients not interested in the new VRF - they would've + * got a Delete. + */ + if (!vrf_bitmap_check (client->ifinfo, ifp->vrf_id)) + continue; + + /* Need to add if the client is interested in the new VRF. */ + client->ifadd_cnt++; + zsend_interface_add (client, ifp); + zsend_interface_addresses (client, ifp); + } +} + int zebra_add_import_table_entry (struct route_node *rn, struct rib *rib) { diff --git a/zebra/redistribute.h b/zebra/redistribute.h index d56e3d0b9..bc3093403 100644 --- a/zebra/redistribute.h +++ b/zebra/redistribute.h @@ -52,6 +52,8 @@ extern void zebra_interface_address_add_update (struct interface *, struct connected *); extern void zebra_interface_address_delete_update (struct interface *, struct connected *c); +extern void zebra_interface_vrf_update_del (struct interface *, vrf_id_t new_vrf_id); +extern void zebra_interface_vrf_update_add (struct interface *, vrf_id_t old_vrf_id); extern int zebra_import_table (afi_t afi, u_int32_t table_id, u_int32_t metric, int add); diff --git a/zebra/redistribute_null.c b/zebra/redistribute_null.c index 44295be98..598eafc35 100644 --- a/zebra/redistribute_null.c +++ b/zebra/redistribute_null.c @@ -63,6 +63,12 @@ void zebra_interface_address_delete_update (struct interface *a, { return; } #endif +void zebra_interface_vrf_update_del (struct interface *a, vrf_id_t new_vrf_id) +{ return; } + +void zebra_interface_vrf_update_add (struct interface *a, vrf_id_t old_vrf_id) +{ return; } + int zebra_import_table (afi_t afi, u_int32_t table_id, u_int32_t metric, int add) { return 0; } diff --git a/zebra/zserv.c b/zebra/zserv.c index 3f9eceae1..6603437f0 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -357,7 +357,7 @@ zsend_interface_nbr_address (int cmd, struct zserv *client, struct prefix *p; /* Check this client need interface information. */ - if (! client->ifinfo) + if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id)) return 0; s = client->obuf; @@ -433,6 +433,61 @@ zebra_interface_nbr_address_delete_update (struct interface *ifp, zsend_interface_nbr_address (ZEBRA_INTERFACE_NBR_ADDRESS_DELETE, client, ifp, ifc); } +/* Send addresses on interface to client */ +int +zsend_interface_addresses (struct zserv *client, struct interface *ifp) +{ + struct listnode *cnode, *cnnode; + struct connected *c; + struct nbr_connected *nc; + + /* Send interface addresses. */ + for (ALL_LIST_ELEMENTS (ifp->connected, cnode, cnnode, c)) + { + if (!CHECK_FLAG (c->conf, ZEBRA_IFC_REAL)) + continue; + + if (zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_ADD, client, + ifp, c) < 0) + return -1; + } + + /* Send interface neighbors. */ + for (ALL_LIST_ELEMENTS (ifp->nbr_connected, cnode, cnnode, nc)) + { + if (zsend_interface_nbr_address (ZEBRA_INTERFACE_NBR_ADDRESS_ADD, + client, ifp, nc) < 0) + return -1; + } + + return 0; +} + +/* Notify client about interface moving from one VRF to another. + * Whether client is interested in old and new VRF is checked by caller. + */ +int +zsend_interface_vrf_update (struct zserv *client, struct interface *ifp, + vrf_id_t vrf_id) +{ + struct stream *s; + + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_INTERFACE_VRF_UPDATE, ifp->vrf_id); + + /* Fill in the ifIndex of the interface and its new VRF (id) */ + stream_putl (s, ifp->ifindex); + stream_putw (s, vrf_id); + + /* Write packet size. */ + stream_putw_at (s, 0, stream_get_endp (s)); + + client->if_vrfchg_cnt++; + return zebra_server_send_message(client); +} + /* Add new nbr connected IPv6 address if none exists already, or replace the existing one if an ifc entry is found on the interface. */ void @@ -1030,10 +1085,7 @@ static int zread_interface_add (struct zserv *client, u_short length, vrf_id_t vrf_id) { struct listnode *ifnode, *ifnnode; - struct listnode *cnode, *cnnode; struct interface *ifp; - struct connected *c; - struct nbr_connected *nc; /* Interface information is needed. */ vrf_bitmap_set (client->ifinfo, vrf_id); @@ -1047,20 +1099,8 @@ zread_interface_add (struct zserv *client, u_short length, vrf_id_t vrf_id) if (zsend_interface_add (client, ifp) < 0) return -1; - for (ALL_LIST_ELEMENTS (ifp->connected, cnode, cnnode, c)) - { - if (CHECK_FLAG (c->conf, ZEBRA_IFC_REAL) && - (zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_ADD, client, - ifp, c) < 0)) - return -1; - } - for (ALL_LIST_ELEMENTS (ifp->nbr_connected, cnode, cnnode, nc)) - { - if (zsend_interface_nbr_address (ZEBRA_INTERFACE_NBR_ADDRESS_ADD, client, - ifp, nc) < 0) - return -1; - } - + if (zsend_interface_addresses (client, ifp) < 0) + return -1; } return 0; } diff --git a/zebra/zserv.h b/zebra/zserv.h index 3016428ca..f3e7a3b88 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -102,6 +102,7 @@ struct zserv u_int32_t bfd_peer_replay_cnt; u_int32_t vrfadd_cnt; u_int32_t vrfdel_cnt; + u_int32_t if_vrfchg_cnt; time_t connect_time; time_t last_read_time; @@ -148,6 +149,7 @@ extern int zsend_vrf_delete (struct zserv *, struct vrf *); extern int zsend_interface_add (struct zserv *, struct interface *); extern int zsend_interface_delete (struct zserv *, struct interface *); +extern int zsend_interface_addresses (struct zserv *, struct interface *); extern int zsend_interface_address (int, struct zserv *, struct interface *, struct connected *); extern void nbr_connected_replacement_add_ipv6 (struct interface *, @@ -158,6 +160,8 @@ extern int zsend_redistribute_route (int, struct zserv *, struct prefix *, struct rib *); extern int zsend_router_id_update (struct zserv *, struct prefix *, vrf_id_t); +extern int zsend_interface_vrf_update (struct zserv *, struct interface *, + vrf_id_t); extern pid_t pid; -- 2.39.5