diff --git a/lib/log.c b/lib/log.c index ad610050f8..fefc0db526 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 82f0c26e59..655eda8176 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 e58870828b..49f73b3e9b 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 3d38a4c5cb..2945f15194 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 7b19379910..5a47f60fa0 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,9 +484,56 @@ 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; + + 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); + 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) + { + 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); + } + } +} + +/* 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; @@ -493,21 +542,6 @@ if_delete_update (struct interface *ifp) zebra_if = ifp->info; - 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 (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 3d8a82d454..ed51507763 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 552524dbc8..9930285199 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 d56e3d0b97..bc30934030 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 44295be98b..598eafc355 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 3f9eceae15..6603437f09 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 3016428caf..f3e7a3b883 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;