From: Renato Westphal Date: Fri, 4 Jan 2019 21:08:10 +0000 (-0200) Subject: ripd: add VRF support X-Git-Tag: frr-7.1~111^2~10 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=ae7b826a2338aa1cc61019fa74391da1c3ceb542;p=mirror_frr.git ripd: add VRF support * Turn the "instance" YANG presence-container into a YANG list keyed by the new "vrf" leaf. This is a backward incompatible change but this should be ok for now. * RIP VRF instances can be configured even when the corresponding VRF doesn't exist. And a RIP VRF instance isn't deleted when the corresponding VRF is deleted. For this to work, implement the rip_instance_enable() and rip_instance_disable() functions that are called to enable/disable RIP routing instances when necessary. A RIP routing instance can be enabled only when the corresponding VRF is enabled (this information comes from zebra and depends on the underlying VRF backend). Routing instances are stored in the new rip_instances rb-tree (global variable). * Add a vrf pointer to the rip structure instead of storing vrf_id only. This is much more convenient than using vrf_lookup_by_id() every time we need to get the vrf pointer from the VRF ID. The rip->vrf pointer is updated whenever the VRF enable/disable hooks are called. Signed-off-by: Renato Westphal --- diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c index 5bb81ef15..be24d04ff 100644 --- a/ripd/rip_cli.c +++ b/ripd/rip_cli.c @@ -39,31 +39,46 @@ */ DEFPY_NOSH (router_rip, router_rip_cmd, - "router rip", + "router rip [vrf NAME]", "Enable a routing process\n" - "Routing Information Protocol (RIP)\n") + "Routing Information Protocol (RIP)\n" + VRF_CMD_HELP_STR) { + char xpath[XPATH_MAXLEN]; int ret; - nb_cli_enqueue_change(vty, "/frr-ripd:ripd/instance", NB_OP_CREATE, - NULL); + /* Build RIP instance XPath. */ + if (!vrf) + vrf = VRF_DEFAULT_NAME; + snprintf(xpath, sizeof(xpath), "/frr-ripd:ripd/instance[vrf='%s']", + vrf); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); ret = nb_cli_apply_changes(vty, NULL); if (ret == CMD_SUCCESS) - VTY_PUSH_XPATH(RIP_NODE, "/frr-ripd:ripd/instance"); + VTY_PUSH_XPATH(RIP_NODE, xpath); return ret; } DEFPY (no_router_rip, no_router_rip_cmd, - "no router rip", + "no router rip [vrf NAME]", NO_STR "Enable a routing process\n" - "Routing Information Protocol (RIP)\n") + "Routing Information Protocol (RIP)\n" + VRF_CMD_HELP_STR) { - nb_cli_enqueue_change(vty, "/frr-ripd:ripd/instance", NB_OP_DELETE, - NULL); + char xpath[XPATH_MAXLEN]; + + /* Build RIP instance XPath. */ + if (!vrf) + vrf = VRF_DEFAULT_NAME; + snprintf(xpath, sizeof(xpath), "/frr-ripd:ripd/instance[vrf='%s']", + vrf); + + nb_cli_enqueue_change(vty, xpath, NB_OP_DELETE, NULL); return nb_cli_apply_changes(vty, NULL); } @@ -71,8 +86,15 @@ DEFPY (no_router_rip, void cli_show_router_rip(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { + const char *vrf_name; + + vrf_name = yang_dnode_get_string(dnode, "./vrf"); + vty_out(vty, "!\n"); - vty_out(vty, "router rip\n"); + vty_out(vty, "router rip"); + if (!strmatch(vrf_name, VRF_DEFAULT_NAME)) + vty_out(vty, " vrf %s", vrf_name); + vty_out(vty, "\n"); } /* diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index ca6dea1b3..86fb2952d 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -94,17 +94,12 @@ static int ipv4_multicast_leave(int sock, struct in_addr group, static void rip_interface_reset(struct rip_interface *); /* Allocate new RIP's interface configuration. */ -static struct rip_interface *rip_interface_new(struct interface *ifp) +static struct rip_interface *rip_interface_new(void) { - struct vrf *vrf; struct rip_interface *ri; ri = XCALLOC(MTYPE_RIP_INTERFACE, sizeof(struct rip_interface)); - vrf = vrf_lookup_by_id(ifp->vrf_id); - if (vrf) - ri->rip = vrf->info; - rip_interface_reset(ri); return ri; @@ -327,10 +322,9 @@ static int rip_if_ipv4_address_check(struct interface *ifp) /* Does this address belongs to me ? */ int if_check_address(struct rip *rip, struct in_addr addr) { - struct vrf *vrf = vrf_lookup_by_id(rip->vrf_id); struct interface *ifp; - FOR_ALL_INTERFACES (vrf, ifp) { + FOR_ALL_INTERFACES (rip->vrf, ifp) { struct listnode *cnode; struct connected *connected; @@ -365,13 +359,14 @@ int rip_interface_down(int command, struct zclient *zclient, if (ifp == NULL) return 0; + rip_interface_sync(ifp); rip_if_down(ifp); if (IS_RIP_DEBUG_ZEBRA) zlog_debug( - "interface %s index %d flags %llx metric %d mtu %d is down", - ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu); + "interface %s vrf %u index %d flags %llx metric %d mtu %d is down", + ifp->name, ifp->vrf_id, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); return 0; } @@ -391,9 +386,11 @@ int rip_interface_up(int command, struct zclient *zclient, zebra_size_t length, if (IS_RIP_DEBUG_ZEBRA) zlog_debug( - "interface %s index %d flags %#llx metric %d mtu %d is up", - ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu); + "interface %s vrf %u index %d flags %#llx metric %d mtu %d is up", + ifp->name, ifp->vrf_id, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); + + rip_interface_sync(ifp); /* Check if this interface is RIP enabled or not.*/ rip_enable_apply(ifp); @@ -414,12 +411,13 @@ int rip_interface_add(int command, struct zclient *zclient, zebra_size_t length, struct interface *ifp; ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); + rip_interface_sync(ifp); if (IS_RIP_DEBUG_ZEBRA) zlog_debug( - "interface add %s index %d flags %#llx metric %d mtu %d", - ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu); + "interface add %s vrf %u index %d flags %#llx metric %d mtu %d", + ifp->name, ifp->vrf_id, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); /* Check if this interface is RIP enabled or not.*/ rip_enable_apply(ifp); @@ -452,13 +450,15 @@ int rip_interface_delete(int command, struct zclient *zclient, if (ifp == NULL) return 0; + rip_interface_sync(ifp); if (if_is_up(ifp)) { rip_if_down(ifp); } - zlog_info("interface delete %s index %d flags %#llx metric %d mtu %d", - ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu); + zlog_info( + "interface delete %s vrf %u index %d flags %#llx metric %d mtu %d", + ifp->name, ifp->vrf_id, ifp->ifindex, + (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); /* To support pseudo interface do not free interface structure. */ /* if_delete(ifp); */ @@ -467,6 +467,28 @@ int rip_interface_delete(int command, struct zclient *zclient, return 0; } +/* VRF update for an interface. */ +int rip_interface_vrf_update(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + vrf_id_t new_vrf_id; + + ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, + &new_vrf_id); + if (!ifp) + return 0; + + if (IS_RIP_DEBUG_ZEBRA) + zlog_debug("interface %s VRF change vrf_id %u new vrf id %u", + ifp->name, vrf_id, new_vrf_id); + + if_update_to_new_vrf(ifp, new_vrf_id); + rip_interface_sync(ifp); + + return 0; +} + static void rip_interface_clean(struct rip_interface *ri) { ri->enable_network = 0; @@ -481,10 +503,9 @@ static void rip_interface_clean(struct rip_interface *ri) void rip_interfaces_clean(struct rip *rip) { - struct vrf *vrf = vrf_lookup_by_id(rip->vrf_id); struct interface *ifp; - FOR_ALL_INTERFACES (vrf, ifp) + FOR_ALL_INTERFACES (rip->vrf, ifp) rip_interface_clean(ifp->info); } @@ -972,11 +993,10 @@ void rip_enable_apply(struct interface *ifp) /* Apply network configuration to all interface. */ static void rip_enable_apply_all(struct rip *rip) { - struct vrf *vrf = vrf_lookup_by_id(rip->vrf_id); struct interface *ifp; /* Check each interface. */ - FOR_ALL_INTERFACES (vrf, ifp) + FOR_ALL_INTERFACES (rip->vrf, ifp) rip_enable_apply(ifp); } @@ -1090,10 +1110,9 @@ static void rip_passive_interface_apply(struct interface *ifp) static void rip_passive_interface_apply_all(struct rip *rip) { - struct vrf *vrf = vrf_lookup_by_id(rip->vrf_id); struct interface *ifp; - FOR_ALL_INTERFACES (vrf, ifp) + FOR_ALL_INTERFACES (rip->vrf, ifp) rip_passive_interface_apply(ifp); } @@ -1154,22 +1173,25 @@ void rip_passive_nondefault_clean(struct rip *rip) /* Write rip configuration of each interface. */ static int rip_interface_config_write(struct vty *vty) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - struct interface *ifp; + struct vrf *vrf; int write = 0; - FOR_ALL_INTERFACES (vrf, ifp) { - struct lyd_node *dnode; + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + struct interface *ifp; - dnode = yang_dnode_get( - running_config->dnode, - "/frr-interface:lib/interface[name='%s'][vrf='%s']", - ifp->name, vrf->name); - if (dnode == NULL) - continue; + FOR_ALL_INTERFACES (vrf, ifp) { + struct lyd_node *dnode; + + dnode = yang_dnode_get( + running_config->dnode, + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + ifp->name, vrf->name); + if (dnode == NULL) + continue; - write = 1; - nb_cli_show_dnode_cmds(vty, dnode, false); + write = 1; + nb_cli_show_dnode_cmds(vty, dnode, false); + } } return write; @@ -1206,10 +1228,26 @@ static struct cmd_node interface_node = { INTERFACE_NODE, "%s(config-if)# ", 1, }; +void rip_interface_sync(struct interface *ifp) +{ + struct vrf *vrf; + + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf) { + struct rip_interface *ri; + + ri = ifp->info; + if (ri) + ri->rip = vrf->info; + } +} + /* Called when interface structure allocated. */ static int rip_interface_new_hook(struct interface *ifp) { - ifp->info = rip_interface_new(ifp); + ifp->info = rip_interface_new(); + rip_interface_sync(ifp); + return 0; } diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h index 8723388e7..303be0315 100644 --- a/ripd/rip_interface.h +++ b/ripd/rip_interface.h @@ -20,6 +20,8 @@ #ifndef _QUAGGA_RIP_INTERFACE_H #define _QUAGGA_RIP_INTERFACE_H +#include "zclient.h" + extern int rip_interface_down(int, struct zclient *, zebra_size_t, vrf_id_t); extern int rip_interface_up(int, struct zclient *, zebra_size_t, vrf_id_t); extern int rip_interface_add(int, struct zclient *, zebra_size_t, vrf_id_t); @@ -28,5 +30,8 @@ extern int rip_interface_address_add(int, struct zclient *, zebra_size_t, vrf_id_t); extern int rip_interface_address_delete(int, struct zclient *, zebra_size_t, vrf_id_t); +extern int rip_interface_vrf_update(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id); +extern void rip_interface_sync(struct interface *ifp); #endif /* _QUAGGA_RIP_INTERFACE_H */ diff --git a/ripd/rip_main.c b/ripd/rip_main.c index 46babe2e0..8b6e3a620 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -46,7 +46,7 @@ static struct option longopts[] = {{"retain", no_argument, NULL, 'r'}, {0}}; /* ripd privileges */ -zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND}; +zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; struct zebra_privs_t ripd_privs = { #if defined(FRR_USER) @@ -59,7 +59,7 @@ struct zebra_privs_t ripd_privs = { .vty_group = VTY_GROUP, #endif .caps_p = _caps_p, - .cap_num_p = 2, + .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; /* Master of threads. */ @@ -79,18 +79,9 @@ static void sighup(void) /* SIGINT handler. */ static void sigint(void) { - struct vrf *vrf; - zlog_notice("Terminating on signal"); - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - struct rip *rip; - - rip = vrf->info; - if (rip) - rip_clean(rip); - } - + rip_vrf_terminate(); rip_zclient_stop(); frr_fini(); @@ -179,7 +170,7 @@ int main(int argc, char **argv) /* Library initialization. */ rip_error_init(); keychain_init(); - vrf_init(NULL, NULL, NULL, NULL, NULL); + rip_vrf_init(); /* RIP related initialization. */ rip_init(); diff --git a/ripd/rip_memory.c b/ripd/rip_memory.c index 185241074..7d703a86d 100644 --- a/ripd/rip_memory.c +++ b/ripd/rip_memory.c @@ -27,6 +27,7 @@ DEFINE_MGROUP(RIPD, "ripd") DEFINE_MTYPE(RIPD, RIP, "RIP structure") +DEFINE_MTYPE(RIPD, RIP_VRF_NAME, "RIP VRF name") DEFINE_MTYPE(RIPD, RIP_INFO, "RIP route info") DEFINE_MTYPE(RIPD, RIP_INTERFACE, "RIP interface") DEFINE_MTYPE(RIPD, RIP_INTERFACE_STRING, "RIP Interface String") diff --git a/ripd/rip_memory.h b/ripd/rip_memory.h index 29013ecec..1f9d8f500 100644 --- a/ripd/rip_memory.h +++ b/ripd/rip_memory.h @@ -26,6 +26,7 @@ DECLARE_MGROUP(RIPD) DECLARE_MTYPE(RIP) +DECLARE_MTYPE(RIP_VRF_NAME) DECLARE_MTYPE(RIP_INFO) DECLARE_MTYPE(RIP_INTERFACE) DECLARE_MTYPE(RIP_INTERFACE_STRING) diff --git a/ripd/rip_northbound.c b/ripd/rip_northbound.c index f5a75707e..8a8cbae1f 100644 --- a/ripd/rip_northbound.c +++ b/ripd/rip_northbound.c @@ -42,25 +42,42 @@ static int ripd_instance_create(enum nb_event event, { struct rip *rip; struct vrf *vrf; + const char *vrf_name; int socket; + vrf_name = yang_dnode_get_string(dnode, "./vrf"); + vrf = vrf_lookup_by_name(vrf_name); + + /* + * Try to create a RIP socket only if the VRF is enabled, otherwise + * create a disabled RIP instance and wait for the VRF to be enabled. + */ switch (event) { case NB_EV_VALIDATE: break; case NB_EV_PREPARE: - socket = rip_create_socket(); + if (!vrf || !vrf_is_enabled(vrf)) + break; + + socket = rip_create_socket(vrf); if (socket < 0) return NB_ERR_RESOURCE; resource->fd = socket; break; case NB_EV_ABORT: + if (!vrf || !vrf_is_enabled(vrf)) + break; + socket = resource->fd; close(socket); break; case NB_EV_APPLY: - vrf = vrf_lookup_by_id(VRF_DEFAULT); - socket = resource->fd; - rip = rip_create(vrf, socket); + if (vrf && vrf_is_enabled(vrf)) + socket = resource->fd; + else + socket = -1; + + rip = rip_create(vrf_name, vrf, socket); yang_dnode_set_entry(dnode, rip); break; } diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 6fbc170cb..d8b35cf97 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -47,7 +47,7 @@ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp, int count = 0; memset(&api, 0, sizeof(api)); - api.vrf_id = rip->vrf_id; + api.vrf_id = rip->vrf->vrf_id; api.type = ZEBRA_ROUTE_RIP; api.safi = SAFI_UNICAST; @@ -56,7 +56,7 @@ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp, if (count >= MULTIPATH_NUM) break; api_nh = &api.nexthops[count]; - api_nh->vrf_id = rip->vrf_id; + api_nh->vrf_id = rip->vrf->vrf_id; api_nh->gate = rinfo->nh.gate; api_nh->type = NEXTHOP_TYPE_IPV4; if (cmd == ZEBRA_ROUTE_ADD) @@ -153,14 +153,14 @@ static int rip_zebra_read_route(int command, struct zclient *zclient, void rip_redistribute_conf_update(struct rip *rip, int type) { zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, - 0, rip->vrf_id); + 0, rip->vrf->vrf_id); } void rip_redistribute_conf_delete(struct rip *rip, int type) { if (zclient->sock > 0) zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient, - AFI_IP, type, 0, rip->vrf_id); + AFI_IP, type, 0, rip->vrf->vrf_id); /* Remove the routes from RIP table. */ rip_redistribute_withdraw(rip, type); @@ -168,21 +168,23 @@ void rip_redistribute_conf_delete(struct rip *rip, int type) int rip_redistribute_check(struct rip *rip, int type) { - return vrf_bitmap_check(zclient->redist[AFI_IP][type], rip->vrf_id); + return vrf_bitmap_check(zclient->redist[AFI_IP][type], + rip->vrf->vrf_id); } void rip_redistribute_clean(struct rip *rip) { for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (!vrf_bitmap_check(zclient->redist[AFI_IP][i], rip->vrf_id)) + if (!vrf_bitmap_check(zclient->redist[AFI_IP][i], + rip->vrf->vrf_id)) continue; if (zclient->sock > 0) zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient, AFI_IP, i, 0, - rip->vrf_id); + rip->vrf->vrf_id); - vrf_bitmap_unset(zclient->redist[AFI_IP][i], rip->vrf_id); + vrf_bitmap_unset(zclient->redist[AFI_IP][i], rip->vrf->vrf_id); } } @@ -191,13 +193,37 @@ void rip_show_redistribute_config(struct vty *vty, struct rip *rip) for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) { if (i == zclient->redist_default || !vrf_bitmap_check(zclient->redist[AFI_IP][i], - rip->vrf_id)) + rip->vrf->vrf_id)) continue; vty_out(vty, " %s", zebra_route_string(i)); } } +void rip_zebra_vrf_register(struct vrf *vrf) +{ + if (vrf->vrf_id == VRF_DEFAULT) + return; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s: register VRF %s(%u) to zebra", __func__, + vrf->name, vrf->vrf_id); + + zclient_send_reg_requests(zclient, vrf->vrf_id); +} + +void rip_zebra_vrf_deregister(struct vrf *vrf) +{ + if (vrf->vrf_id == VRF_DEFAULT) + return; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s: deregister VRF %s(%u) from zebra.", __func__, + vrf->name, vrf->vrf_id); + + zclient_send_dereg_requests(zclient, vrf->vrf_id); +} + static void rip_zebra_connected(struct zclient *zclient) { zclient_send_reg_requests(zclient, VRF_DEFAULT); @@ -215,6 +241,7 @@ void rip_zclient_init(struct thread_master *master) zclient->interface_address_delete = rip_interface_address_delete; zclient->interface_up = rip_interface_up; zclient->interface_down = rip_interface_down; + zclient->interface_vrf_update = rip_interface_vrf_update; zclient->redistribute_route_add = rip_zebra_read_route; zclient->redistribute_route_del = rip_zebra_read_route; } diff --git a/ripd/ripd.c b/ripd/ripd.c index c6abfb557..a6a2a29de 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -46,6 +46,7 @@ #include "ripd/ripd.h" #include "ripd/rip_debug.h" #include "ripd/rip_errors.h" +#include "ripd/rip_interface.h" /* UDP receive buffer size */ #define RIP_UDP_RCV_BUF 41600 @@ -57,6 +58,8 @@ static int rip_triggered_update(struct thread *); static int rip_update_jitter(unsigned long); static void rip_distance_table_node_cleanup(struct route_table *table, struct route_node *node); +static void rip_instance_enable(struct rip *rip, struct vrf *vrf, int sock); +static void rip_instance_disable(struct rip *rip); static void rip_distribute_update(struct distribute_ctx *ctx, struct distribute *dist); @@ -73,6 +76,15 @@ static const struct message rip_msg[] = {{RIP_REQUEST, "REQUEST"}, {RIP_POLL_ENTRY, "POLL ENTRY"}, {0}}; +/* Generate rb-tree of RIP instances. */ +static inline int rip_instance_compare(const struct rip *a, const struct rip *b) +{ + return strcmp(a->vrf_name, b->vrf_name); +} +RB_GENERATE(rip_instance_head, rip, entry, rip_instance_compare) + +struct rip_instance_head rip_instances = RB_INITIALIZER(&rip_instances); + /* Utility function to set boradcast option to the socket. */ static int sockopt_broadcast(int sock) { @@ -372,7 +384,6 @@ static int rip_filter(int rip_distribute, struct prefix_ipv4 *p, /* Check nexthop address validity. */ static int rip_nexthop_check(struct rip *rip, struct in_addr *addr) { - struct vrf *vrf = vrf_lookup_by_id(rip->vrf_id); struct interface *ifp; struct listnode *cnode; struct connected *ifc; @@ -381,7 +392,7 @@ static int rip_nexthop_check(struct rip *rip, struct in_addr *addr) /* If nexthop address matches local configured address then it is invalid nexthop. */ - FOR_ALL_INTERFACES (vrf, ifp) { + FOR_ALL_INTERFACES (rip->vrf, ifp) { for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) { p = ifc->address; @@ -1114,7 +1125,8 @@ static void rip_response_process(struct rip_packet *packet, int size, whether the datagram is from a valid neighbor; the source of the datagram must be on a directly connected network (RFC2453 - Sec. 3.9.2) */ - if (if_lookup_address((void *)&from->sin_addr, AF_INET, rip->vrf_id) + if (if_lookup_address((void *)&from->sin_addr, AF_INET, + rip->vrf->vrf_id) == NULL) { zlog_info( "This datagram doesn't came from a valid neighbor: %s", @@ -1197,7 +1209,7 @@ static void rip_response_process(struct rip_packet *packet, int size, } if (!if_lookup_address((void *)&rte->nexthop, AF_INET, - rip->vrf_id)) { + rip->vrf->vrf_id)) { struct route_node *rn; struct rip_info *rinfo; @@ -1327,11 +1339,12 @@ static void rip_response_process(struct rip_packet *packet, int size, } /* Make socket for RIP protocol. */ -int rip_create_socket(void) +int rip_create_socket(struct vrf *vrf) { int ret; int sock; struct sockaddr_in addr; + const char *vrf_dev = NULL; memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; @@ -1343,11 +1356,17 @@ int rip_create_socket(void) addr.sin_port = htons(RIP_PORT_DEFAULT); /* Make datagram socket. */ - sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock < 0) { - flog_err_sys(EC_LIB_SOCKET, "Cannot create UDP socket: %s", - safe_strerror(errno)); - return -1; + if (vrf->vrf_id != VRF_DEFAULT) + vrf_dev = vrf->name; + frr_elevate_privs(&ripd_privs) { + sock = vrf_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, vrf->vrf_id, + vrf_dev); + if (sock < 0) { + flog_err_sys(EC_LIB_SOCKET, + "Cannot create UDP socket: %s", + safe_strerror(errno)); + return -1; + } } sockopt_broadcast(sock); @@ -1586,8 +1605,9 @@ void rip_redistribute_delete(struct rip *rip, int type, int sub_type, "infinity metric [delete]", inet_ntoa(p->prefix), p->prefixlen, - ifindex2ifname(ifindex, - rip->vrf_id)); + ifindex2ifname( + ifindex, + rip->vrf->vrf_id)); rip_event(rip, RIP_TRIGGERED_UPDATE, 0); } @@ -1708,33 +1728,37 @@ static int rip_read(struct thread *t) len = recvfrom(sock, (char *)&rip_buf.buf, sizeof(rip_buf.buf), 0, (struct sockaddr *)&from, &fromlen); if (len < 0) { - zlog_info("recvfrom failed: %s", safe_strerror(errno)); + zlog_info("recvfrom failed (VRF %s): %s", rip->vrf_name, + safe_strerror(errno)); return len; } /* Check is this packet comming from myself? */ if (if_check_address(rip, from.sin_addr)) { if (IS_RIP_DEBUG_PACKET) - zlog_debug("ignore packet comes from myself"); + zlog_debug("ignore packet comes from myself (VRF %s)", + rip->vrf_name); return -1; } /* Which interface is this packet comes from. */ - ifc = if_lookup_address((void *)&from.sin_addr, AF_INET, rip->vrf_id); + ifc = if_lookup_address((void *)&from.sin_addr, AF_INET, + rip->vrf->vrf_id); if (ifc) ifp = ifc->ifp; /* RIP packet received */ if (IS_RIP_DEBUG_EVENT) - zlog_debug("RECV packet from %s port %d on %s", + zlog_debug("RECV packet from %s port %d on %s (VRF %s)", inet_ntoa(from.sin_addr), ntohs(from.sin_port), - ifp ? ifp->name : "unknown"); + ifp ? ifp->name : "unknown", rip->vrf_name); /* If this packet come from unknown interface, ignore it. */ if (ifp == NULL) { zlog_info( - "rip_read: cannot find interface for packet from %s port %d", - inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + "rip_read: cannot find interface for packet from %s port %d (VRF %s)", + inet_ntoa(from.sin_addr), ntohs(from.sin_port), + rip->vrf_name); return -1; } @@ -1747,9 +1771,9 @@ static int rip_read(struct thread *t) if (ifc == NULL) { zlog_info( "rip_read: cannot find connected address for packet from %s " - "port %d on interface %s", + "port %d on interface %s (VRF %s)", inet_ntoa(from.sin_addr), ntohs(from.sin_port), - ifp->name); + ifp->name, rip->vrf_name); return -1; } @@ -2415,7 +2439,6 @@ static void rip_update_interface(struct connected *ifc, uint8_t version, /* Update send to all interface and neighbor. */ static void rip_update_process(struct rip *rip, int route_type) { - struct vrf *vrf = vrf_lookup_by_id(rip->vrf_id); struct listnode *ifnode, *ifnnode; struct connected *connected; struct interface *ifp; @@ -2425,7 +2448,7 @@ static void rip_update_process(struct rip *rip, int route_type) struct prefix *p; /* Send RIP update to each interface. */ - FOR_ALL_INTERFACES (vrf, ifp) { + FOR_ALL_INTERFACES (rip->vrf, ifp) { if (if_is_loopback(ifp)) continue; @@ -2478,7 +2501,7 @@ static void rip_update_process(struct rip *rip, int route_type) p = &rp->p; connected = if_lookup_address(&p->u.prefix4, AF_INET, - rip->vrf_id); + rip->vrf->vrf_id); if (!connected) { zlog_warn( "Neighbor %s doesn't have connected interface!", @@ -2622,7 +2645,7 @@ void rip_redistribute_withdraw(struct rip *rip, int type) p->prefixlen, ifindex2ifname( rinfo->nh.ifindex, - rip->vrf_id)); + rip->vrf->vrf_id)); } rip_event(rip, RIP_TRIGGERED_UPDATE, 0); @@ -2641,13 +2664,22 @@ struct rip *rip_lookup_by_vrf_id(vrf_id_t vrf_id) return vrf->info; } +struct rip *rip_lookup_by_vrf_name(const char *vrf_name) +{ + struct rip rip; + + rip.vrf_name = (char *)vrf_name; + + return RB_FIND(rip_instance_head, &rip_instances, &rip); +} + /* Create new RIP instance and set it to global variable. */ -struct rip *rip_create(struct vrf *vrf, int socket) +struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket) { struct rip *rip; - struct interface *ifp; rip = XCALLOC(MTYPE_RIP, sizeof(struct rip)); + rip->vrf_name = XSTRDUP(MTYPE_RIP_VRF_NAME, vrf_name); /* Set initial value. */ rip->ecmp = yang_get_default_bool("%s/allow-ecmp", RIP_INSTANCE); @@ -2685,31 +2717,23 @@ struct rip *rip_create(struct vrf *vrf, int socket) rip->offset_list_master->del = (void (*)(void *))offset_list_del; /* Distribute list install. */ - rip->distribute_ctx = - distribute_list_ctx_create(vrf_lookup_by_id(VRF_DEFAULT)); + rip->distribute_ctx = distribute_list_ctx_create(vrf); distribute_list_add_hook(rip->distribute_ctx, rip_distribute_update); distribute_list_delete_hook(rip->distribute_ctx, rip_distribute_update); /* Make output stream. */ rip->obuf = stream_new(1500); - /* Set socket. */ - rip->sock = socket; - - /* Create read and timer thread. */ - rip_event(rip, RIP_READ, rip->sock); - rip_event(rip, RIP_UPDATE_EVENT, 1); - - /* Link RIP instance to VRF. */ - rip->vrf_id = vrf->vrf_id; - vrf->info = rip; - FOR_ALL_INTERFACES (vrf, ifp) { - struct rip_interface *ri; - - ri = ifp->info; - ri->rip = rip; + /* Enable the routing instance if possible. */ + if (vrf && vrf_is_enabled(vrf)) + rip_instance_enable(rip, vrf, socket); + else { + rip->vrf = NULL; + rip->sock = -1; } + RB_INSERT(rip_instance_head, &rip_instances, rip); + return rip; } @@ -2988,20 +3012,34 @@ static const char *rip_route_type_print(int sub_type) DEFUN (show_ip_rip, show_ip_rip_cmd, - "show ip rip", + "show ip rip [vrf NAME]", SHOW_STR IP_STR - "Show RIP routes\n") + "Show RIP routes\n" + VRF_CMD_HELP_STR) { struct rip *rip; struct route_node *np; struct rip_info *rinfo = NULL; struct list *list = NULL; struct listnode *listnode = NULL; + const char *vrf_name; + int idx = 0; - rip = rip_lookup_by_vrf_id(VRF_DEFAULT); - if (!rip) + if (argv_find(argv, argc, "vrf", &idx)) + vrf_name = argv[idx + 1]->arg; + else + vrf_name = VRF_DEFAULT_NAME; + + rip = rip_lookup_by_vrf_name(vrf_name); + if (!rip) { + vty_out(vty, "%% RIP instance not found\n"); + return CMD_SUCCESS; + } + if (!rip->enabled) { + vty_out(vty, "%% RIP instance is disabled\n"); return CMD_SUCCESS; + } vty_out(vty, "Codes: R - RIP, C - connected, S - Static, O - OSPF, B - BGP\n" @@ -3093,23 +3131,36 @@ DEFUN (show_ip_rip, /* Vincent: formerly, it was show_ip_protocols_rip: "show ip protocols" */ DEFUN (show_ip_rip_status, show_ip_rip_status_cmd, - "show ip rip status", + "show ip rip [vrf NAME] status", SHOW_STR IP_STR "Show RIP routes\n" + VRF_CMD_HELP_STR "IP routing protocol process parameters and statistics\n") { struct rip *rip; - struct vrf *vrf; struct interface *ifp; struct rip_interface *ri; extern const struct message ri_version_msg[]; const char *send_version; const char *receive_version; + const char *vrf_name; + int idx = 0; - rip = rip_lookup_by_vrf_id(VRF_DEFAULT); - if (!rip) + if (argv_find(argv, argc, "vrf", &idx)) + vrf_name = argv[idx + 1]->arg; + else + vrf_name = VRF_DEFAULT_NAME; + + rip = rip_lookup_by_vrf_name(vrf_name); + if (!rip) { + vty_out(vty, "%% RIP instance not found\n"); return CMD_SUCCESS; + } + if (!rip->enabled) { + vty_out(vty, "%% RIP instance is disabled\n"); + return CMD_SUCCESS; + } vty_out(vty, "Routing Protocol is \"rip\"\n"); vty_out(vty, " Sending updates every %u seconds with +/-50%%,", @@ -3141,8 +3192,7 @@ DEFUN (show_ip_rip_status, vty_out(vty, " Interface Send Recv Key-chain\n"); - vrf = vrf_lookup_by_id(rip->vrf_id); - FOR_ALL_INTERFACES (vrf, ifp) { + FOR_ALL_INTERFACES (rip->vrf, ifp) { ri = ifp->info; if (!ri->running) @@ -3176,7 +3226,7 @@ DEFUN (show_ip_rip_status, { int found_passive = 0; - FOR_ALL_INTERFACES (vrf, ifp) { + FOR_ALL_INTERFACES (rip->vrf, ifp) { ri = ifp->info; if ((ri->enable_network || ri->enable_interface) @@ -3204,28 +3254,31 @@ DEFUN (show_ip_rip_status, /* RIP configuration write function. */ static int config_write_rip(struct vty *vty) { + struct rip *rip; int write = 0; - struct lyd_node *dnode; - dnode = yang_dnode_get(running_config->dnode, - "/frr-ripd:ripd/instance"); - if (dnode) { - struct rip *rip; + RB_FOREACH(rip, rip_instance_head, &rip_instances) { + char xpath[XPATH_MAXLEN]; + struct lyd_node *dnode; - write++; + snprintf(xpath, sizeof(xpath), + "/frr-ripd:ripd/instance[vrf='%s']", rip->vrf_name); + + dnode = yang_dnode_get(running_config->dnode, xpath); + assert(dnode); nb_cli_show_dnode_cmds(vty, dnode, false); - rip = rip_lookup_by_vrf_id(VRF_DEFAULT); - if (rip) { - /* Distribute configuration. */ - write += config_write_distribute(vty, - rip->distribute_ctx); + /* Distribute configuration. */ + config_write_distribute(vty, rip->distribute_ctx); - /* Interface routemap configuration */ - write += config_write_if_rmap(vty); - } + /* Interface routemap configuration */ + if (strmatch(rip->vrf_name, VRF_DEFAULT_NAME)) + config_write_if_rmap(vty); + + write = 1; } + return write; } @@ -3241,10 +3294,10 @@ static void rip_distribute_update(struct distribute_ctx *ctx, struct access_list *alist; struct prefix_list *plist; - if (!dist->ifname) + if (!ctx->vrf || !dist->ifname) return; - ifp = if_lookup_by_name(dist->ifname, VRF_DEFAULT); + ifp = if_lookup_by_name(dist->ifname, ctx->vrf->vrf_id); if (ifp == NULL) return; @@ -3323,46 +3376,8 @@ static void rip_distribute_update_all_wrapper(struct access_list *notused) /* Delete all added rip route. */ void rip_clean(struct rip *rip) { - struct vrf *vrf; - struct interface *ifp; - struct route_node *rp; - - /* Clear RIP routes */ - for (rp = route_top(rip->table); rp; rp = route_next(rp)) { - struct rip_info *rinfo; - struct list *list; - struct listnode *listnode; - - if ((list = rp->info) == NULL) - continue; - - rinfo = listgetdata(listhead(list)); - if (rip_route_rte(rinfo)) - rip_zebra_ipv4_delete(rip, rp); - - for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - RIP_TIMER_OFF(rinfo->t_timeout); - RIP_TIMER_OFF(rinfo->t_garbage_collect); - rip_info_free(rinfo); - } - list_delete(&list); - rp->info = NULL; - route_unlock_node(rp); - } - - /* Cancel RIP related timers. */ - RIP_TIMER_OFF(rip->t_update); - RIP_TIMER_OFF(rip->t_triggered_update); - RIP_TIMER_OFF(rip->t_triggered_interval); - - /* Cancel read thread. */ - THREAD_READ_OFF(rip->t_read); - - /* Close RIP socket. */ - if (rip->sock >= 0) { - close(rip->sock); - rip->sock = -1; - } + if (rip->enabled) + rip_instance_disable(rip); stream_free(rip->obuf); @@ -3385,16 +3400,8 @@ void rip_clean(struct rip *rip) route_table_finish(rip->distance_table); rip_redistribute_clean(rip); - vrf = vrf_lookup_by_id(rip->vrf_id); - vrf->info = NULL; - - FOR_ALL_INTERFACES (vrf, ifp) { - struct rip_interface *ri; - - ri = ifp->info; - ri->rip = NULL; - } - + RB_REMOVE(rip_instance_head, &rip_instances, rip); + XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name); XFREE(MTYPE_RIP, rip); } @@ -3461,6 +3468,168 @@ static void rip_routemap_update(const char *notused) rip_routemap_update_redistribute(rip); } +/* Link RIP instance to VRF. */ +static void rip_vrf_link(struct rip *rip, struct vrf *vrf) +{ + struct interface *ifp; + + rip->vrf = vrf; + rip->distribute_ctx->vrf = vrf; + vrf->info = rip; + + FOR_ALL_INTERFACES (vrf, ifp) + rip_interface_sync(ifp); +} + +/* Unlink RIP instance from VRF. */ +static void rip_vrf_unlink(struct rip *rip, struct vrf *vrf) +{ + struct interface *ifp; + + rip->vrf = NULL; + rip->distribute_ctx->vrf = NULL; + vrf->info = NULL; + + FOR_ALL_INTERFACES (vrf, ifp) + rip_interface_sync(ifp); +} + +static void rip_instance_enable(struct rip *rip, struct vrf *vrf, int sock) +{ + rip->sock = sock; + + rip_vrf_link(rip, vrf); + rip->enabled = true; + + /* Create read and timer thread. */ + rip_event(rip, RIP_READ, rip->sock); + rip_event(rip, RIP_UPDATE_EVENT, 1); + + rip_zebra_vrf_register(vrf); +} + +static void rip_instance_disable(struct rip *rip) +{ + struct vrf *vrf = rip->vrf; + struct route_node *rp; + + /* Clear RIP routes */ + for (rp = route_top(rip->table); rp; rp = route_next(rp)) { + struct rip_info *rinfo; + struct list *list; + struct listnode *listnode; + + if ((list = rp->info) == NULL) + continue; + + rinfo = listgetdata(listhead(list)); + if (rip_route_rte(rinfo)) + rip_zebra_ipv4_delete(rip, rp); + + for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { + RIP_TIMER_OFF(rinfo->t_timeout); + RIP_TIMER_OFF(rinfo->t_garbage_collect); + rip_info_free(rinfo); + } + list_delete(&list); + rp->info = NULL; + route_unlock_node(rp); + } + + /* Cancel RIP related timers. */ + RIP_TIMER_OFF(rip->t_update); + RIP_TIMER_OFF(rip->t_triggered_update); + RIP_TIMER_OFF(rip->t_triggered_interval); + + /* Cancel read thread. */ + THREAD_READ_OFF(rip->t_read); + + /* Close RIP socket. */ + close(rip->sock); + rip->sock = -1; + + /* Clear existing peers. */ + list_delete_all_node(rip->peer_list); + + rip_zebra_vrf_deregister(vrf); + + rip_vrf_unlink(rip, vrf); + rip->enabled = false; +} + +static int rip_vrf_new(struct vrf *vrf) +{ + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s: VRF created: %s(%u)", __func__, vrf->name, + vrf->vrf_id); + + return 0; +} + +static int rip_vrf_delete(struct vrf *vrf) +{ + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s: VRF deleted: %s(%u)", __func__, vrf->name, + vrf->vrf_id); + + return 0; +} + +static int rip_vrf_enable(struct vrf *vrf) +{ + struct rip *rip; + int socket; + + rip = rip_lookup_by_vrf_name(vrf->name); + if (!rip || rip->enabled) + return 0; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s: VRF %s(%u) enabled", __func__, vrf->name, + vrf->vrf_id); + + /* Activate the VRF RIP instance. */ + if (!rip->enabled) { + socket = rip_create_socket(vrf); + if (socket < 0) + return -1; + + rip_instance_enable(rip, vrf, socket); + } + + return 0; +} + +static int rip_vrf_disable(struct vrf *vrf) +{ + struct rip *rip; + + rip = rip_lookup_by_vrf_name(vrf->name); + if (!rip || !rip->enabled) + return 0; + + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s: VRF %s(%u) disabled", __func__, vrf->name, + vrf->vrf_id); + + /* Deactivate the VRF RIP instance. */ + if (rip->enabled) + rip_instance_disable(rip); + + return 0; +} + +void rip_vrf_init(void) +{ + vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete, + NULL); +} + +void rip_vrf_terminate(void) +{ + vrf_terminate(); +} + /* Allocate new rip structure and set default value. */ void rip_init(void) { diff --git a/ripd/ripd.h b/ripd/ripd.h index a18a74157..268ba74b9 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -99,8 +99,16 @@ /* RIP structure. */ struct rip { - /* VRF ID. */ - vrf_id_t vrf_id; + RB_ENTRY(rip) entry; + + /* VRF this routing instance is associated with. */ + char *vrf_name; + + /* VRF backpointer (might be NULL if the VRF doesn't exist). */ + struct vrf *vrf; + + /* Status of the routing instance. */ + bool enabled; /* RIP socket. */ int sock; @@ -182,6 +190,8 @@ struct rip { long queries; } counters; }; +RB_HEAD(rip_instance_head, rip); +RB_PROTOTYPE(rip_instance_head, rip, entry, rip_instance_compare) /* RIP routing table entry which belong to rip_packet. */ struct rte { @@ -416,11 +426,15 @@ extern int rip_passive_nondefault_unset(struct rip *rip, const char *ifname); extern void rip_passive_nondefault_clean(struct rip *rip); extern void rip_if_init(void); extern void rip_route_map_init(void); +extern void rip_zebra_vrf_register(struct vrf *vrf); +extern void rip_zebra_vrf_deregister(struct vrf *vrf); extern void rip_zclient_init(struct thread_master *); extern void rip_zclient_stop(void); extern int if_check_address(struct rip *rip, struct in_addr addr); extern struct rip *rip_lookup_by_vrf_id(vrf_id_t vrf_id); -extern struct rip *rip_create(struct vrf *vrf, int socket); +extern struct rip *rip_lookup_by_vrf_name(const char *vrf_name); +extern struct rip *rip_create(const char *vrf_name, struct vrf *vrf, + int socket); extern int rip_request_send(struct sockaddr_in *, struct interface *, uint8_t, struct connected *); @@ -436,7 +450,7 @@ extern int rip_enable_if_delete(struct rip *rip, const char *ifname); extern void rip_event(struct rip *rip, enum rip_event event, int sock); extern void rip_ecmp_disable(struct rip *rip); -extern int rip_create_socket(void); +extern int rip_create_socket(struct vrf *vrf); extern int rip_redistribute_check(struct rip *rip, int type); extern void rip_redistribute_conf_update(struct rip *rip, int type); @@ -495,6 +509,9 @@ extern int rip_offset_list_apply_out(struct prefix_ipv4 *, struct interface *, extern int offset_list_cmp(struct rip_offset_list *o1, struct rip_offset_list *o2); +extern void rip_vrf_init(void); +extern void rip_vrf_terminate(void); + /* YANG notifications */ extern void ripd_notif_send_auth_type_failure(const char *ifname); extern void ripd_notif_send_auth_failure(const char *ifname); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 6cf45789d..11be03363 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1519,8 +1519,8 @@ DEFUNSH(VTYSH_KEYS, key, key_cmd, "key (0-2147483647)", return CMD_SUCCESS; } -DEFUNSH(VTYSH_RIPD, router_rip, router_rip_cmd, "router rip", - ROUTER_STR "RIP\n") +DEFUNSH(VTYSH_RIPD, router_rip, router_rip_cmd, "router rip [vrf NAME]", + ROUTER_STR "RIP\n" VRF_CMD_HELP_STR) { vty->node = RIP_NODE; return CMD_SUCCESS; diff --git a/yang/example/ripd.json b/yang/example/ripd.json index 888c52b93..00040622e 100644 --- a/yang/example/ripd.json +++ b/yang/example/ripd.json @@ -20,25 +20,28 @@ ] }, "frr-ripd:ripd": { - "instance": { - "allow-ecmp": "true", - "distance": { - "source": [ + "instance": [ + { + "vrf": "default", + "allow-ecmp": "true", + "distance": { + "source": [ + { + "distance": "25", + "prefix": "172.16.1.0/24" + } + ] + }, + "redistribute": [ { - "prefix": "172.16.1.0/24", - "distance": "25" + "metric": "3", + "protocol": "ospf" } + ], + "static-route": [ + "10.0.1.0/24" ] - }, - "redistribute": [ - { - "protocol": "ospf", - "metric": "3" - } - ], - "static-route": [ - "10.0.1.0/24" - ] - } + } + ] } } diff --git a/yang/example/ripd.xml b/yang/example/ripd.xml index 756e382bd..2feddde2d 100644 --- a/yang/example/ripd.xml +++ b/yang/example/ripd.xml @@ -18,6 +18,7 @@ + default true 10.0.1.0/24 diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang index 8073fba77..ca2b76615 100644 --- a/yang/frr-ripd.yang +++ b/yang/frr-ripd.yang @@ -34,13 +34,18 @@ module frr-ripd { container ripd { /* - * Global configuration data + * Routing instance configuration. */ - container instance { - presence "Present if the RIP protocol is enabled."; + list instance { + key "vrf"; description "RIP routing instance."; + leaf vrf { + type string; + description + "VRF name."; + } leaf allow-ecmp { type boolean; default "false";