diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c index 5bb81ef157..be24d04ff3 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 ca6dea1b37..86fb2952dd 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; - write = 1; - nb_cli_show_dnode_cmds(vty, dnode, false); + 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); + } } 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 8723388e75..303be0315d 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 46babe2e0b..8b6e3a620a 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 1852410743..7d703a86db 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 29013ecec3..1f9d8f500f 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 f5a75707e8..8a8cbae1fa 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 6fbc170cb9..d8b35cf976 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 c6abfb557d..a6a2a29deb 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 a18a741579..268ba74b96 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 6cf45789dd..11be03363c 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 888c52b930..00040622e5 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 756e382bd6..2feddde2d8 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 8073fba77e..ca2b766159 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";