#include "ripngd/ripng_debug.h"
#include "ripngd/ripng_nexthop.h"
-/* RIPng structure which includes many parameters related to RIPng
- protocol. If ripng couldn't active or ripng doesn't configured,
- ripng->fd must be negative value. */
-struct ripng *ripng = NULL;
-
enum { ripng_all_route,
ripng_changed_route,
};
/* Prototypes. */
void ripng_output_process(struct interface *, struct sockaddr_in6 *, int);
-
+static void ripng_instance_enable(struct ripng *ripng, struct vrf *vrf,
+ int sock);
+static void ripng_instance_disable(struct ripng *ripng);
int ripng_triggered_update(struct thread *);
-
static void ripng_if_rmap_update(struct if_rmap_ctx *ctx,
struct if_rmap *if_rmap);
+/* Generate rb-tree of RIPng instances. */
+static inline int ripng_instance_compare(const struct ripng *a,
+ const struct ripng *b)
+{
+ return strcmp(a->vrf_name, b->vrf_name);
+}
+RB_GENERATE(ripng_instance_head, ripng, entry, ripng_instance_compare)
+
+struct ripng_instance_head ripng_instances = RB_INITIALIZER(&ripng_instances);
+
/* RIPng next hop specification. */
struct ripng_nexthop {
enum ripng_nexthop_type {
XFREE(MTYPE_RIPNG_ROUTE, rinfo);
}
+struct ripng *ripng_info_get_instance(const struct ripng_info *rinfo)
+{
+ return agg_get_table_info(agg_get_table(rinfo->rp));
+}
+
/* Create ripng socket. */
-int ripng_make_socket(void)
+int ripng_make_socket(struct vrf *vrf)
{
int ret;
int sock;
struct sockaddr_in6 ripaddr;
-
- sock = socket(AF_INET6, SOCK_DGRAM, 0);
- if (sock < 0) {
- flog_err_sys(EC_LIB_SOCKET, "Can't make ripng socket");
- return sock;
+ const char *vrf_dev = NULL;
+
+ /* Make datagram socket. */
+ if (vrf->vrf_id != VRF_DEFAULT)
+ vrf_dev = vrf->name;
+ frr_elevate_privs(&ripngd_privs)
+ {
+ sock = vrf_socket(AF_INET6, 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_reuseaddr(sock);
+ sockopt_reuseport(sock);
setsockopt_so_recvbuf(sock, 8096);
ret = setsockopt_ipv6_pktinfo(sock, 1);
if (ret < 0)
int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to,
struct interface *ifp)
{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
int ret;
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsgptr;
- char adata[256];
+ char adata[256] = {};
struct in6_pktinfo *pkt;
struct sockaddr_in6 addr;
return 0;
}
-static void ripng_timeout_update(struct ripng_info *rinfo);
+static void ripng_timeout_update(struct ripng *ripng, struct ripng_info *rinfo);
/* Add new route to the ECMP list.
* RETURN: the new entry added in the list, or NULL if it is not the first
* entry and ECMP is not allowed.
*/
-struct ripng_info *ripng_ecmp_add(struct ripng_info *rinfo_new)
+struct ripng_info *ripng_ecmp_add(struct ripng *ripng,
+ struct ripng_info *rinfo_new)
{
struct agg_node *rp = rinfo_new->rp;
struct ripng_info *rinfo = NULL;
listnode_add(list, rinfo);
if (ripng_route_rte(rinfo)) {
- ripng_timeout_update(rinfo);
- ripng_zebra_ipv6_add(rp);
+ ripng_timeout_update(ripng, rinfo);
+ ripng_zebra_ipv6_add(ripng, rp);
}
ripng_aggregate_increment(rp, rinfo);
SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
/* Signal the output process to trigger an update. */
- ripng_event(RIPNG_TRIGGERED_UPDATE, 0);
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
return rinfo;
}
/* Replace the ECMP list with the new route.
* RETURN: the new entry added in the list
*/
-struct ripng_info *ripng_ecmp_replace(struct ripng_info *rinfo_new)
+struct ripng_info *ripng_ecmp_replace(struct ripng *ripng,
+ struct ripng_info *rinfo_new)
{
struct agg_node *rp = rinfo_new->rp;
struct list *list = (struct list *)rp->info;
struct listnode *node = NULL, *nextnode = NULL;
if (list == NULL || listcount(list) == 0)
- return ripng_ecmp_add(rinfo_new);
+ return ripng_ecmp_add(ripng, rinfo_new);
/* Get the first entry */
rinfo = listgetdata(listhead(list));
/* Learnt route replaced by a local one. Delete it from zebra. */
if (ripng_route_rte(rinfo) && !ripng_route_rte(rinfo_new))
if (CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
- ripng_zebra_ipv6_delete(rp);
+ ripng_zebra_ipv6_delete(ripng, rp);
if (rinfo->metric != RIPNG_METRIC_INFINITY)
ripng_aggregate_decrement_list(rp, list);
memcpy(rinfo, rinfo_new, sizeof(struct ripng_info));
if (ripng_route_rte(rinfo)) {
- ripng_timeout_update(rinfo);
+ ripng_timeout_update(ripng, rinfo);
/* The ADD message implies an update. */
- ripng_zebra_ipv6_add(rp);
+ ripng_zebra_ipv6_add(ripng, rp);
}
ripng_aggregate_increment(rp, rinfo);
SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
/* Signal the output process to trigger an update. */
- ripng_event(RIPNG_TRIGGERED_UPDATE, 0);
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
return rinfo;
}
* the entry - the entry is the last one in the list; its metric is set
* to INFINITY, and the garbage collector is started for it
*/
-struct ripng_info *ripng_ecmp_delete(struct ripng_info *rinfo)
+struct ripng_info *ripng_ecmp_delete(struct ripng *ripng,
+ struct ripng_info *rinfo)
{
struct agg_node *rp = rinfo->rp;
struct list *list = (struct list *)rp->info;
if (ripng_route_rte(rinfo)
&& CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
/* The ADD message implies the update. */
- ripng_zebra_ipv6_add(rp);
+ ripng_zebra_ipv6_add(ripng, rp);
ripng_info_free(rinfo);
rinfo = NULL;
} else {
if (ripng_route_rte(rinfo)
&& CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB))
- ripng_zebra_ipv6_delete(rp);
+ ripng_zebra_ipv6_delete(ripng, rp);
}
/* Set the route change flag on the first entry. */
SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
/* Signal the output process to trigger an update. */
- ripng_event(RIPNG_TRIGGERED_UPDATE, 0);
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
return rinfo;
}
/* Timeout RIPng routes. */
static int ripng_timeout(struct thread *t)
{
- ripng_ecmp_delete((struct ripng_info *)THREAD_ARG(t));
+ struct ripng_info *rinfo = THREAD_ARG(t);
+ struct ripng *ripng = ripng_info_get_instance(rinfo);
+
+ ripng_ecmp_delete(ripng, rinfo);
+
return 0;
}
-static void ripng_timeout_update(struct ripng_info *rinfo)
+static void ripng_timeout_update(struct ripng *ripng, struct ripng_info *rinfo)
{
if (rinfo->metric != RIPNG_METRIC_INFINITY) {
RIPNG_TIMER_OFF(rinfo->t_timeout);
- RIPNG_TIMER_ON(rinfo->t_timeout, ripng_timeout,
- ripng->timeout_time);
+ thread_add_timer(master, ripng_timeout, rinfo,
+ ripng->timeout_time, &rinfo->t_timeout);
}
}
}
/* All interface filter check. */
- dist = distribute_lookup(ripng->distribute_ctx, NULL);
+ dist = distribute_lookup(ri->ripng->distribute_ctx, NULL);
if (dist) {
if (dist->list[distribute]) {
alist = access_list_lookup(AFI_IP6,
struct agg_node *rp;
struct ripng_info *rinfo = NULL, newinfo;
struct ripng_interface *ri;
+ struct ripng *ripng;
struct in6_addr *nexthop;
int same = 0;
struct list *list = NULL;
apply_mask_ipv6. */
apply_mask_ipv6(&p);
- /* Apply input filters. */
ri = ifp->info;
+ ripng = ri->ripng;
+ /* Apply input filters. */
ret = ripng_filter(RIPNG_FILTER_IN, &p, ri);
if (ret < 0)
return;
**/
/* Zebra ripngd can handle offset-list in. */
- ret = ripng_offset_list_apply_in(&p, ifp, &rte->metric);
+ ret = ripng_offset_list_apply_in(ripng, &p, ifp, &rte->metric);
/* If offset-list does not modify the metric use interface's
* one. */
infinity (there is no point in adding a route which
unusable). */
if (rte->metric != RIPNG_METRIC_INFINITY)
- ripng_ecmp_add(&newinfo);
+ ripng_ecmp_add(ripng, &newinfo);
else
agg_unlock_node(rp);
} else {
&& rinfo->t_timeout
&& (thread_timer_remain_second(rinfo->t_timeout)
< (ripng->timeout_time / 2))) {
- ripng_ecmp_replace(&newinfo);
+ ripng_ecmp_replace(ripng, &newinfo);
}
/* Next, compare the metrics. If the datagram is from the same
router as the existing route, and the new metric is different
|| rte->metric < rinfo->metric) {
if (listcount(list) == 1) {
if (newinfo.metric != RIPNG_METRIC_INFINITY)
- ripng_ecmp_replace(&newinfo);
+ ripng_ecmp_replace(ripng, &newinfo);
else
- ripng_ecmp_delete(rinfo);
+ ripng_ecmp_delete(ripng, rinfo);
} else {
if (newinfo.metric < rinfo->metric)
- ripng_ecmp_replace(&newinfo);
+ ripng_ecmp_replace(ripng, &newinfo);
else /* newinfo.metric > rinfo->metric */
- ripng_ecmp_delete(rinfo);
+ ripng_ecmp_delete(ripng, rinfo);
}
} else /* same & no change */
- ripng_timeout_update(rinfo);
+ ripng_timeout_update(ripng, rinfo);
/* Unlock tempolary lock of the route. */
agg_unlock_node(rp);
}
/* Add redistributed route to RIPng table. */
-void ripng_redistribute_add(int type, int sub_type, struct prefix_ipv6 *p,
- ifindex_t ifindex, struct in6_addr *nexthop,
- route_tag_t tag)
+void ripng_redistribute_add(struct ripng *ripng, int type, int sub_type,
+ struct prefix_ipv6 *p, ifindex_t ifindex,
+ struct in6_addr *nexthop, route_tag_t tag)
{
struct agg_node *rp;
struct ripng_info *rinfo = NULL, newinfo;
}
}
- ripng_ecmp_replace(&newinfo);
+ ripng_ecmp_replace(ripng, &newinfo);
agg_unlock_node(rp);
} else
- ripng_ecmp_add(&newinfo);
+ ripng_ecmp_add(ripng, &newinfo);
if (IS_RIPNG_DEBUG_EVENT) {
if (!nexthop)
zlog_debug(
"Redistribute new prefix %s/%d on the interface %s",
inet6_ntoa(p->prefix), p->prefixlen,
- ifindex2ifname(ifindex, VRF_DEFAULT));
+ ifindex2ifname(ifindex, ripng->vrf->vrf_id));
else
zlog_debug(
"Redistribute new prefix %s/%d with nexthop %s on the interface %s",
inet6_ntoa(p->prefix), p->prefixlen,
inet6_ntoa(*nexthop),
- ifindex2ifname(ifindex, VRF_DEFAULT));
+ ifindex2ifname(ifindex, ripng->vrf->vrf_id));
}
- ripng_event(RIPNG_TRIGGERED_UPDATE, 0);
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
}
/* Delete redistributed route to RIPng table. */
-void ripng_redistribute_delete(int type, int sub_type, struct prefix_ipv6 *p,
- ifindex_t ifindex)
+void ripng_redistribute_delete(struct ripng *ripng, int type, int sub_type,
+ struct prefix_ipv6 *p, ifindex_t ifindex)
{
struct agg_node *rp;
struct ripng_info *rinfo;
"infinity metric [delete]",
inet6_ntoa(p->prefix),
p->prefixlen,
- ifindex2ifname(ifindex,
- VRF_DEFAULT));
+ ifindex2ifname(
+ ifindex,
+ ripng->vrf->vrf_id));
- ripng_event(RIPNG_TRIGGERED_UPDATE, 0);
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
}
}
agg_unlock_node(rp);
}
/* Withdraw redistributed route. */
-void ripng_redistribute_withdraw(int type)
+void ripng_redistribute_withdraw(struct ripng *ripng, int type)
{
struct agg_node *rp;
struct ripng_info *rinfo = NULL;
struct list *list = NULL;
- if (!ripng)
- return;
-
for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp))
if ((list = rp->info) != NULL) {
rinfo = listgetdata(listhead(list));
"Poisone %s/%d on the interface %s [withdraw]",
inet6_ntoa(p->prefix),
p->prefixlen,
- ifindex2ifname(rinfo->ifindex,
- VRF_DEFAULT));
+ ifindex2ifname(
+ rinfo->ifindex,
+ ripng->vrf->vrf_id));
}
- ripng_event(RIPNG_TRIGGERED_UPDATE, 0);
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
}
}
}
struct sockaddr_in6 *from,
struct interface *ifp, int hoplimit)
{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
caddr_t lim;
struct rte *rte;
struct ripng_nexthop nexthop;
if (ntohs(from->sin6_port) != RIPNG_PORT_DEFAULT) {
zlog_warn("RIPng packet comes from non RIPng port %d from %s",
ntohs(from->sin6_port), inet6_ntoa(from->sin6_addr));
- ripng_peer_bad_packet(from);
+ ripng_peer_bad_packet(ripng, from);
return;
}
if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) {
zlog_warn("RIPng packet comes from non link local address %s",
inet6_ntoa(from->sin6_addr));
- ripng_peer_bad_packet(from);
+ ripng_peer_bad_packet(ripng, from);
return;
}
zlog_warn(
"RIPng packet comes from my own link local address %s",
inet6_ntoa(from->sin6_addr));
- ripng_peer_bad_packet(from);
+ ripng_peer_bad_packet(ripng, from);
return;
}
zlog_warn(
"RIPng packet comes with non 255 hop count %d from %s",
hoplimit, inet6_ntoa(from->sin6_addr));
- ripng_peer_bad_packet(from);
+ ripng_peer_bad_packet(ripng, from);
return;
}
/* Update RIPng peer. */
- ripng_peer_update(from, packet->version);
+ ripng_peer_update(ripng, from, packet->version);
/* Reset nexthop. */
memset(&nexthop, 0, sizeof(struct ripng_nexthop));
"Destination prefix is a multicast address %s/%d [%d]",
inet6_ntoa(rte->addr), rte->prefixlen,
rte->metric);
- ripng_peer_bad_route(from);
+ ripng_peer_bad_route(ripng, from);
continue;
}
if (IN6_IS_ADDR_LINKLOCAL(&rte->addr)) {
"Destination prefix is a link-local address %s/%d [%d]",
inet6_ntoa(rte->addr), rte->prefixlen,
rte->metric);
- ripng_peer_bad_route(from);
+ ripng_peer_bad_route(ripng, from);
continue;
}
if (IN6_IS_ADDR_LOOPBACK(&rte->addr)) {
"Destination prefix is a loopback address %s/%d [%d]",
inet6_ntoa(rte->addr), rte->prefixlen,
rte->metric);
- ripng_peer_bad_route(from);
+ ripng_peer_bad_route(ripng, from);
continue;
}
zlog_warn("Invalid prefix length %s/%d from %s%%%s",
inet6_ntoa(rte->addr), rte->prefixlen,
inet6_ntoa(from->sin6_addr), ifp->name);
- ripng_peer_bad_route(from);
+ ripng_peer_bad_route(ripng, from);
continue;
}
if (!(rte->metric >= 1 && rte->metric <= 16)) {
zlog_warn("Invalid metric %d from %s%%%s", rte->metric,
inet6_ntoa(from->sin6_addr), ifp->name);
- ripng_peer_bad_route(from);
+ ripng_peer_bad_route(ripng, from);
continue;
}
struct sockaddr_in6 *from,
struct interface *ifp)
{
+ struct ripng *ripng;
caddr_t lim;
struct rte *rte;
struct prefix_ipv6 p;
ri = ifp->info;
if (!ri->running)
return;
+ ripng = ri->ripng;
/* When passive interface is specified, suppress responses */
if (ri->passive)
return;
/* RIPng peer update. */
- ripng_peer_update(from, packet->version);
+ ripng_peer_update(ripng, from, packet->version);
lim = ((caddr_t)packet) + size;
rte = packet->rte;
/* First entry point of reading RIPng packet. */
static int ripng_read(struct thread *thread)
{
+ struct ripng *ripng = THREAD_ARG(thread);
int len;
int sock;
struct sockaddr_in6 from;
ripng->t_read = NULL;
/* Add myself to the next event. */
- ripng_event(RIPNG_READ, sock);
+ ripng_event(ripng, RIPNG_READ, sock);
/* Read RIPng packet. */
len = ripng_recv_packet(sock, STREAM_DATA(ripng->ibuf),
STREAM_SIZE(ripng->ibuf), &from, &ifindex,
&hoplimit);
if (len < 0) {
- zlog_warn("RIPng recvfrom failed: %s.", safe_strerror(errno));
+ zlog_warn("RIPng recvfrom failed (VRF %s): %s.",
+ ripng->vrf_name, safe_strerror(errno));
return len;
}
/* Check RTE boundary. RTE size (Packet length - RIPng header size
(4)) must be multiple size of one RTE size (20). */
if (((len - 4) % 20) != 0) {
- zlog_warn("RIPng invalid packet size %d from %s", len,
- inet6_ntoa(from.sin6_addr));
- ripng_peer_bad_packet(&from);
+ zlog_warn("RIPng invalid packet size %d from %s (VRF %s)", len,
+ inet6_ntoa(from.sin6_addr), ripng->vrf_name);
+ ripng_peer_bad_packet(ripng, &from);
return 0;
}
packet = (struct ripng_packet *)STREAM_DATA(ripng->ibuf);
- ifp = if_lookup_by_index(ifindex, VRF_DEFAULT);
+ ifp = if_lookup_by_index(ifindex, ripng->vrf->vrf_id);
/* RIPng packet received. */
if (IS_RIPNG_DEBUG_EVENT)
- zlog_debug("RIPng packet received from %s port %d on %s",
- inet6_ntoa(from.sin6_addr), ntohs(from.sin6_port),
- ifp ? ifp->name : "unknown");
+ zlog_debug(
+ "RIPng packet received from %s port %d on %s (VRF %s)",
+ inet6_ntoa(from.sin6_addr), ntohs(from.sin6_port),
+ ifp ? ifp->name : "unknown", ripng->vrf_name);
/* Logging before packet checking. */
if (IS_RIPNG_DEBUG_RECV)
/* Packet comes from unknown interface. */
if (ifp == NULL) {
- zlog_warn("RIPng packet comes from unknown interface %d",
- ifindex);
+ zlog_warn(
+ "RIPng packet comes from unknown interface %d (VRF %s)",
+ ifindex, ripng->vrf_name);
return 0;
}
/* Packet version mismatch checking. */
if (packet->version != ripng->version) {
zlog_warn(
- "RIPng packet version %d doesn't fit to my version %d",
- packet->version, ripng->version);
- ripng_peer_bad_packet(&from);
+ "RIPng packet version %d doesn't fit to my version %d (VRF %s)",
+ packet->version, ripng->version, ripng->vrf_name);
+ ripng_peer_bad_packet(ripng, &from);
return 0;
}
ripng_response_process(packet, len, &from, ifp, hoplimit);
break;
default:
- zlog_warn("Invalid RIPng command %d", packet->command);
- ripng_peer_bad_packet(&from);
+ zlog_warn("Invalid RIPng command %d (VRF %s)", packet->command,
+ ripng->vrf_name);
+ ripng_peer_bad_packet(ripng, &from);
break;
}
return 0;
}
/* Walk down the RIPng routing table then clear changed flag. */
-static void ripng_clear_changed_flag(void)
+static void ripng_clear_changed_flag(struct ripng *ripng)
{
struct agg_node *rp;
struct ripng_info *rinfo = NULL;
enabled interface. */
static int ripng_update(struct thread *t)
{
- struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct ripng *ripng = THREAD_ARG(t);
struct interface *ifp;
struct ripng_interface *ri;
zlog_debug("RIPng update timer expired!");
/* Supply routes to each interface. */
- FOR_ALL_INTERFACES (vrf, ifp) {
+ FOR_ALL_INTERFACES (ripng->vrf, ifp) {
ri = ifp->info;
if (if_is_loopback(ifp) || !if_is_up(ifp))
ripng->trigger = 0;
/* Reset flush event. */
- ripng_event(RIPNG_UPDATE_EVENT, 0);
+ ripng_event(ripng, RIPNG_UPDATE_EVENT, 0);
return 0;
}
/* Triggered update interval timer. */
static int ripng_triggered_interval(struct thread *t)
{
+ struct ripng *ripng = THREAD_ARG(t);
+
ripng->t_triggered_interval = NULL;
if (ripng->trigger) {
/* Execute triggered update. */
int ripng_triggered_update(struct thread *t)
{
- struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct ripng *ripng = THREAD_ARG(t);
struct interface *ifp;
struct ripng_interface *ri;
int interval;
/* Split Horizon processing is done when generating triggered
updates as well as normal updates (see section 2.6). */
- FOR_ALL_INTERFACES (vrf, ifp) {
+ FOR_ALL_INTERFACES (ripng->vrf, ifp) {
ri = ifp->info;
if (if_is_loopback(ifp) || !if_is_up(ifp))
/* Once all of the triggered updates have been generated, the route
change flags should be cleared. */
- ripng_clear_changed_flag();
+ ripng_clear_changed_flag(ripng);
/* After a triggered update is sent, a timer should be set for a
random interval between 1 and 5 seconds. If other changes that
interval = (random() % 5) + 1;
ripng->t_triggered_interval = NULL;
- thread_add_timer(master, ripng_triggered_interval, NULL, interval,
+ thread_add_timer(master, ripng_triggered_interval, ripng, interval,
&ripng->t_triggered_interval);
return 0;
void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to,
int route_type)
{
+ struct ripng *ripng;
int ret;
struct agg_node *rp;
struct ripng_info *rinfo;
ifp->name);
}
- /* Get RIPng interface. */
+ /* Get RIPng interface and instance. */
ri = ifp->info;
+ ripng = ri->ripng;
ripng_rte_list = ripng_rte_new();
}
/* Redistribute route-map. */
- if (ripng->route_map[rinfo->type].name) {
- ret = route_map_apply(
- ripng->route_map[rinfo->type].map,
- (struct prefix *)p, RMAP_RIPNG, rinfo);
+ if (ripng->redist[rinfo->type].route_map.name) {
+ ret = route_map_apply(ripng->redist[rinfo->type]
+ .route_map.map,
+ (struct prefix *)p,
+ RMAP_RIPNG, rinfo);
if (ret == RMAP_DENYMATCH) {
if (IS_RIPNG_DEBUG_PACKET)
/* When the route-map does not set metric. */
if (!rinfo->metric_set) {
/* If the redistribute metric is set. */
- if (ripng->route_map[rinfo->type].metric_config
+ if (ripng->redist[rinfo->type].metric_config
&& rinfo->metric != RIPNG_METRIC_INFINITY) {
rinfo->metric_out =
- ripng->route_map[rinfo->type]
+ ripng->redist[rinfo->type]
.metric;
} else {
/* If the route is not connected or
/* Apply offset-list */
if (rinfo->metric_out != RIPNG_METRIC_INFINITY)
- ripng_offset_list_apply_out(p, ifp,
+ ripng_offset_list_apply_out(ripng, p, ifp,
&rinfo->metric_out);
if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
/* Apply offset-list */
if (aggregate->metric_out != RIPNG_METRIC_INFINITY)
ripng_offset_list_apply_out(
- p, ifp, &aggregate->metric_out);
+ ripng, p, ifp, &aggregate->metric_out);
if (aggregate->metric_out > RIPNG_METRIC_INFINITY)
aggregate->metric_out = RIPNG_METRIC_INFINITY;
ripng_rte_free(ripng_rte_list);
}
+struct ripng *ripng_lookup_by_vrf_id(vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf)
+ return NULL;
+
+ return vrf->info;
+}
+
+struct ripng *ripng_lookup_by_vrf_name(const char *vrf_name)
+{
+ struct ripng ripng;
+
+ ripng.vrf_name = (char *)vrf_name;
+
+ return RB_FIND(ripng_instance_head, &ripng_instances, &ripng);
+}
+
/* Create new RIPng instance and set it to global variable. */
-int ripng_create(int socket)
+struct ripng *ripng_create(const char *vrf_name, struct vrf *vrf, int socket)
{
- /* ripng should be NULL. */
- assert(ripng == NULL);
+ struct ripng *ripng;
/* Allocaste RIPng instance. */
ripng = XCALLOC(MTYPE_RIPNG, sizeof(struct ripng));
+ ripng->vrf_name = XSTRDUP(MTYPE_RIPNG_VRF_NAME, vrf_name);
/* Default version and timer values. */
ripng->version = RIPNG_V1;
ripng->ibuf = stream_new(RIPNG_MAX_PACKET_SIZE * 5);
ripng->obuf = stream_new(RIPNG_MAX_PACKET_SIZE);
- /* Initialize RIPng routig table. */
+ /* Initialize RIPng data structures. */
ripng->table = agg_table_init();
-
- /* Distribute list install. */
- ripng->distribute_ctx = distribute_list_ctx_create(
- vrf_lookup_by_id(VRF_DEFAULT));
+ agg_set_table_info(ripng->table, ripng);
+ ripng->peer_list = list_new();
+ ripng->peer_list->cmp = (int (*)(void *, void *))ripng_peer_list_cmp;
+ ripng->peer_list->del = ripng_peer_list_del;
+ ripng->enable_if = vector_init(1);
+ ripng->enable_network = agg_table_init();
+ ripng->passive_interface = vector_init(1);
+ ripng->offset_list_master = list_new();
+ ripng->offset_list_master->cmp =
+ (int (*)(void *, void *))offset_list_cmp;
+ ripng->offset_list_master->del =
+ (void (*)(void *))ripng_offset_list_del;
+ ripng->distribute_ctx = distribute_list_ctx_create(vrf);
distribute_list_add_hook(ripng->distribute_ctx,
ripng_distribute_update);
distribute_list_delete_hook(ripng->distribute_ctx,
ripng_distribute_update);
/* if rmap install. */
- ripng->if_rmap_ctx = if_rmap_ctx_create(VRF_DEFAULT_NAME);
+ ripng->if_rmap_ctx = if_rmap_ctx_create(vrf_name);
if_rmap_hook_add(ripng->if_rmap_ctx, ripng_if_rmap_update);
if_rmap_hook_delete(ripng->if_rmap_ctx, ripng_if_rmap_update);
- /* Make socket. */
- ripng->sock = socket;
+ /* Enable the routing instance if possible. */
+ if (vrf && vrf_is_enabled(vrf))
+ ripng_instance_enable(ripng, vrf, socket);
+ else {
+ ripng->vrf = NULL;
+ ripng->sock = -1;
+ }
- /* Threads. */
- ripng_event(RIPNG_READ, ripng->sock);
- ripng_event(RIPNG_UPDATE_EVENT, 1);
+ RB_INSERT(ripng_instance_head, &ripng_instances, ripng);
- return 0;
+ return ripng;
}
/* Send RIPng request to the interface. */
return ((random() % (time + 1)) - (time / 2));
}
-void ripng_event(enum ripng_event event, int sock)
+void ripng_event(struct ripng *ripng, enum ripng_event event, int sock)
{
int jitter = 0;
switch (event) {
case RIPNG_READ:
- thread_add_read(master, ripng_read, NULL, sock, &ripng->t_read);
+ thread_add_read(master, ripng_read, ripng, sock,
+ &ripng->t_read);
break;
case RIPNG_UPDATE_EVENT:
if (ripng->t_update) {
jitter = ripng_update_jitter(ripng->update_time);
ripng->t_update = NULL;
- thread_add_timer(master, ripng_update, NULL,
+ thread_add_timer(master, ripng_update, ripng,
sock ? 2 : ripng->update_time + jitter,
&ripng->t_update);
break;
if (ripng->t_triggered_interval)
ripng->trigger = 1;
else
- thread_add_event(master, ripng_triggered_update, NULL,
+ thread_add_event(master, ripng_triggered_update, ripng,
0, &ripng->t_triggered_update);
break;
default:
DEFUN (show_ipv6_ripng,
show_ipv6_ripng_cmd,
- "show ipv6 ripng",
+ "show ipv6 ripng [vrf NAME]",
SHOW_STR
IPV6_STR
- "Show RIPng routes\n")
+ "Show RIPng routes\n"
+ VRF_CMD_HELP_STR)
{
+ struct ripng *ripng;
struct agg_node *rp;
struct ripng_info *rinfo;
struct ripng_aggregate *aggregate;
struct list *list = NULL;
struct listnode *listnode = NULL;
int len;
+ const char *vrf_name;
+ int idx = 0;
- if (!ripng)
+ if (argv_find(argv, argc, "vrf", &idx))
+ vrf_name = argv[idx + 1]->arg;
+ else
+ vrf_name = VRF_DEFAULT_NAME;
+
+ ripng = ripng_lookup_by_vrf_name(vrf_name);
+ if (!ripng) {
+ vty_out(vty, "%% RIPng instance not found\n");
+ return CMD_SUCCESS;
+ }
+ if (!ripng->enabled) {
+ vty_out(vty, "%% RIPng instance is disabled\n");
return CMD_SUCCESS;
+ }
/* Header of display. */
vty_out(vty,
&& (rinfo->sub_type == RIPNG_ROUTE_RTE)) {
len = vty_out(
vty, "%s",
- ifindex2ifname(rinfo->ifindex,
- VRF_DEFAULT));
+ ifindex2ifname(
+ rinfo->ifindex,
+ ripng->vrf->vrf_id));
} else if (rinfo->metric
== RIPNG_METRIC_INFINITY) {
len = vty_out(vty, "kill");
DEFUN (show_ipv6_ripng_status,
show_ipv6_ripng_status_cmd,
- "show ipv6 ripng status",
+ "show ipv6 ripng [vrf NAME] status",
SHOW_STR
IPV6_STR
"Show RIPng routes\n"
+ VRF_CMD_HELP_STR
"IPv6 routing protocol process parameters and statistics\n")
{
- struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct ripng *ripng;
struct interface *ifp;
+ const char *vrf_name;
+ int idx = 0;
- if (!ripng)
+ if (argv_find(argv, argc, "vrf", &idx))
+ vrf_name = argv[idx + 1]->arg;
+ else
+ vrf_name = VRF_DEFAULT_NAME;
+
+ ripng = ripng_lookup_by_vrf_name(vrf_name);
+ if (!ripng) {
+ vty_out(vty, "%% RIPng instance not found\n");
+ return CMD_SUCCESS;
+ }
+ if (!ripng->enabled) {
+ vty_out(vty, "%% RIPng instance is disabled\n");
return CMD_SUCCESS;
+ }
vty_out(vty, "Routing Protocol is \"RIPng\"\n");
vty_out(vty, " Sending updates every %u seconds with +/-50%%,",
/* Redistribute information. */
vty_out(vty, " Redistributing:");
- ripng_redistribute_write(vty);
+ ripng_redistribute_write(vty, ripng);
vty_out(vty, "\n");
vty_out(vty, " Default version control: send version %d,",
vty_out(vty, " Interface Send Recv\n");
- FOR_ALL_INTERFACES (vrf, ifp) {
+ FOR_ALL_INTERFACES (ripng->vrf, ifp) {
struct ripng_interface *ri;
ri = ifp->info;
}
vty_out(vty, " Routing for Networks:\n");
- ripng_network_write(vty);
+ ripng_network_write(vty, ripng);
vty_out(vty, " Routing Information Sources:\n");
vty_out(vty,
" Gateway BadPackets BadRoutes Distance Last Update\n");
- ripng_peer_display(vty);
+ ripng_peer_display(vty, ripng);
return CMD_SUCCESS;
}
#endif
/* Update ECMP routes to zebra when ECMP is disabled. */
-void ripng_ecmp_disable(void)
+void ripng_ecmp_disable(struct ripng *ripng)
{
struct agg_node *rp;
struct ripng_info *rinfo, *tmp_rinfo;
}
/* Update zebra. */
- ripng_zebra_ipv6_add(rp);
+ ripng_zebra_ipv6_add(ripng, rp);
/* Set the route change flag. */
SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED);
/* Signal the output process to trigger an update. */
- ripng_event(RIPNG_TRIGGERED_UPDATE, 0);
+ ripng_event(ripng, RIPNG_TRIGGERED_UPDATE, 0);
}
}
/* RIPng configuration write function. */
static int ripng_config_write(struct vty *vty)
{
- struct lyd_node *dnode;
+ struct ripng *ripng;
int write = 0;
- dnode = yang_dnode_get(running_config->dnode,
- "/frr-ripngd:ripngd/instance");
- if (dnode) {
- nb_cli_show_dnode_cmds(vty, dnode, false);
+ RB_FOREACH(ripng, ripng_instance_head, &ripng_instances) {
+ char xpath[XPATH_MAXLEN];
+ struct lyd_node *dnode;
- config_write_distribute(vty,
- ripng->distribute_ctx);
+ snprintf(xpath, sizeof(xpath),
+ "/frr-ripngd:ripngd/instance[vrf='%s']",
+ ripng->vrf_name);
+ dnode = yang_dnode_get(running_config->dnode, xpath);
+ assert(dnode);
+
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+
+ config_write_distribute(vty, ripng->distribute_ctx);
config_write_if_rmap(vty, ripng->if_rmap_ctx);
write = 1;
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;
void ripng_distribute_update_interface(struct interface *ifp)
{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
struct distribute *dist;
if (!ripng)
}
/* delete all the added ripng routes. */
-void ripng_clean(void)
+void ripng_clean(struct ripng *ripng)
{
- int i;
- struct agg_node *rp;
- struct ripng_info *rinfo;
- struct ripng_aggregate *aggregate;
- struct list *list = NULL;
- struct listnode *listnode = NULL;
-
- if (ripng) {
- /* Clear RIPng routes */
- for (rp = agg_route_top(ripng->table); rp;
- rp = agg_route_next(rp)) {
- if ((list = rp->info) != NULL) {
- rinfo = listgetdata(listhead(list));
- if (ripng_route_rte(rinfo))
- ripng_zebra_ipv6_delete(rp);
-
- for (ALL_LIST_ELEMENTS_RO(list, listnode,
- rinfo)) {
- RIPNG_TIMER_OFF(rinfo->t_timeout);
- RIPNG_TIMER_OFF(
- rinfo->t_garbage_collect);
- ripng_info_free(rinfo);
- }
- list_delete(&list);
- rp->info = NULL;
- agg_unlock_node(rp);
- }
-
- if ((aggregate = rp->aggregate) != NULL) {
- ripng_aggregate_free(aggregate);
- rp->aggregate = NULL;
- agg_unlock_node(rp);
- }
- }
-
- /* Cancel the RIPng timers */
- RIPNG_TIMER_OFF(ripng->t_update);
- RIPNG_TIMER_OFF(ripng->t_triggered_update);
- RIPNG_TIMER_OFF(ripng->t_triggered_interval);
-
- /* Cancel the read thread */
- if (ripng->t_read) {
- thread_cancel(ripng->t_read);
- ripng->t_read = NULL;
- }
-
- /* Close the RIPng socket */
- if (ripng->sock >= 0) {
- close(ripng->sock);
- ripng->sock = -1;
- }
-
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
- if (ripng->route_map[i].name)
- free(ripng->route_map[i].name);
-
- agg_table_finish(ripng->table);
-
- stream_free(ripng->ibuf);
- stream_free(ripng->obuf);
-
- distribute_list_delete(&ripng->distribute_ctx);
- XFREE(MTYPE_RIPNG, ripng);
- ripng = NULL;
- } /* if (ripng) */
-
- ripng_clean_network();
- ripng_passive_interface_clean();
- ripng_offset_clean();
- ripng_interface_clean();
- ripng_redistribute_clean();
- if_rmap_terminate();
+ if (ripng->enabled)
+ ripng_instance_disable(ripng);
+
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ if (ripng->redist[i].route_map.name)
+ free(ripng->redist[i].route_map.name);
+
+ agg_table_finish(ripng->table);
+ list_delete(&ripng->peer_list);
+ distribute_list_delete(&ripng->distribute_ctx);
+ if_rmap_ctx_delete(ripng->if_rmap_ctx);
+
+ stream_free(ripng->ibuf);
+ stream_free(ripng->obuf);
+
+ ripng_clean_network(ripng);
+ ripng_passive_interface_clean(ripng);
+ vector_free(ripng->enable_if);
+ agg_table_finish(ripng->enable_network);
+ vector_free(ripng->passive_interface);
+ list_delete(&ripng->offset_list_master);
+ ripng_interface_clean(ripng);
+
+ RB_REMOVE(ripng_instance_head, &ripng_instances, ripng);
+ XFREE(MTYPE_RIPNG_VRF_NAME, ripng->vrf_name);
+ XFREE(MTYPE_RIPNG, ripng);
}
static void ripng_if_rmap_update(struct if_rmap_ctx *ctx,
void ripng_if_rmap_update_interface(struct interface *ifp)
{
+ struct ripng_interface *ri = ifp->info;
+ struct ripng *ripng = ri->ripng;
struct if_rmap *if_rmap;
struct if_rmap_ctx *ctx;
- if (ifp->vrf_id != VRF_DEFAULT)
- return;
if (!ripng)
return;
ctx = ripng->if_rmap_ctx;
ripng_if_rmap_update(ctx, if_rmap);
}
-static void ripng_routemap_update_redistribute(void)
+static void ripng_routemap_update_redistribute(struct ripng *ripng)
{
- int i;
-
- if (ripng) {
- for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
- if (ripng->route_map[i].name) {
- ripng->route_map[i].map =
- route_map_lookup_by_name(
- ripng->route_map[i].name);
- route_map_counter_increment(
- ripng->route_map[i].map);
- }
+ for (int i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (ripng->redist[i].route_map.name) {
+ ripng->redist[i].route_map.map =
+ route_map_lookup_by_name(
+ ripng->redist[i].route_map.name);
+ route_map_counter_increment(
+ ripng->redist[i].route_map.map);
}
}
}
static void ripng_routemap_update(const char *unused)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ struct ripng *ripng;
struct interface *ifp;
FOR_ALL_INTERFACES (vrf, ifp)
ripng_if_rmap_update_interface(ifp);
- ripng_routemap_update_redistribute();
+ ripng = vrf->info;
+ if (ripng)
+ ripng_routemap_update_redistribute(ripng);
+}
+
+/* Link RIPng instance to VRF. */
+static void ripng_vrf_link(struct ripng *ripng, struct vrf *vrf)
+{
+ struct interface *ifp;
+
+ ripng->vrf = vrf;
+ ripng->distribute_ctx->vrf = vrf;
+ vrf->info = ripng;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ ripng_interface_sync(ifp);
+}
+
+/* Unlink RIPng instance from VRF. */
+static void ripng_vrf_unlink(struct ripng *ripng, struct vrf *vrf)
+{
+ struct interface *ifp;
+
+ ripng->vrf = NULL;
+ ripng->distribute_ctx->vrf = NULL;
+ vrf->info = NULL;
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ ripng_interface_sync(ifp);
+}
+
+static void ripng_instance_enable(struct ripng *ripng, struct vrf *vrf,
+ int sock)
+{
+ ripng->sock = sock;
+
+ ripng_vrf_link(ripng, vrf);
+ ripng->enabled = true;
+
+ /* Resend all redistribute requests. */
+ ripng_redistribute_enable(ripng);
+
+ /* Create read and timer thread. */
+ ripng_event(ripng, RIPNG_READ, ripng->sock);
+ ripng_event(ripng, RIPNG_UPDATE_EVENT, 1);
+
+ ripng_zebra_vrf_register(vrf);
+}
+
+static void ripng_instance_disable(struct ripng *ripng)
+{
+ struct vrf *vrf = ripng->vrf;
+ struct agg_node *rp;
+
+ /* Clear RIPng routes */
+ for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
+ struct ripng_aggregate *aggregate;
+ struct list *list;
+
+ if ((list = rp->info) != NULL) {
+ struct ripng_info *rinfo;
+ struct listnode *listnode;
+
+ rinfo = listgetdata(listhead(list));
+ if (ripng_route_rte(rinfo))
+ ripng_zebra_ipv6_delete(ripng, rp);
+
+ for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
+ RIPNG_TIMER_OFF(rinfo->t_timeout);
+ RIPNG_TIMER_OFF(rinfo->t_garbage_collect);
+ ripng_info_free(rinfo);
+ }
+ list_delete(&list);
+ rp->info = NULL;
+ agg_unlock_node(rp);
+ }
+
+ if ((aggregate = rp->aggregate) != NULL) {
+ ripng_aggregate_free(aggregate);
+ rp->aggregate = NULL;
+ agg_unlock_node(rp);
+ }
+ }
+
+ /* Flush all redistribute requests. */
+ ripng_redistribute_disable(ripng);
+
+ /* Cancel the RIPng timers */
+ RIPNG_TIMER_OFF(ripng->t_update);
+ RIPNG_TIMER_OFF(ripng->t_triggered_update);
+ RIPNG_TIMER_OFF(ripng->t_triggered_interval);
+
+ /* Cancel the read thread */
+ if (ripng->t_read) {
+ thread_cancel(ripng->t_read);
+ ripng->t_read = NULL;
+ }
+
+ /* Close the RIPng socket */
+ if (ripng->sock >= 0) {
+ close(ripng->sock);
+ ripng->sock = -1;
+ }
+
+ /* Clear existing peers. */
+ list_delete_all_node(ripng->peer_list);
+
+ ripng_zebra_vrf_deregister(vrf);
+
+ ripng_vrf_unlink(ripng, vrf);
+ ripng->enabled = false;
+}
+
+static int ripng_vrf_new(struct vrf *vrf)
+{
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("%s: VRF created: %s(%u)", __func__, vrf->name,
+ vrf->vrf_id);
+
+ return 0;
+}
+
+static int ripng_vrf_delete(struct vrf *vrf)
+{
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("%s: VRF deleted: %s(%u)", __func__, vrf->name,
+ vrf->vrf_id);
+
+ return 0;
+}
+
+static int ripng_vrf_enable(struct vrf *vrf)
+{
+ struct ripng *ripng;
+ int socket;
+
+ ripng = ripng_lookup_by_vrf_name(vrf->name);
+ if (!ripng || ripng->enabled)
+ return 0;
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("%s: VRF %s(%u) enabled", __func__, vrf->name,
+ vrf->vrf_id);
+
+ /* Activate the VRF RIPng instance. */
+ if (!ripng->enabled) {
+ socket = ripng_make_socket(vrf);
+ if (socket < 0)
+ return -1;
+
+ ripng_instance_enable(ripng, vrf, socket);
+ }
+
+ return 0;
+}
+
+static int ripng_vrf_disable(struct vrf *vrf)
+{
+ struct ripng *ripng;
+
+ ripng = ripng_lookup_by_vrf_name(vrf->name);
+ if (!ripng || !ripng->enabled)
+ return 0;
+
+ if (IS_RIPNG_DEBUG_EVENT)
+ zlog_debug("%s: VRF %s(%u) disabled", __func__, vrf->name,
+ vrf->vrf_id);
+
+ /* Deactivate the VRF RIPng instance. */
+ if (ripng->enabled)
+ ripng_instance_disable(ripng);
+
+ return 0;
+}
+
+void ripng_vrf_init(void)
+{
+ vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable,
+ ripng_vrf_delete, NULL);
+}
+
+void ripng_vrf_terminate(void)
+{
+ vrf_terminate();
}
/* Initialize ripng structure and set commands. */
/* Route-map for interface. */
ripng_route_map_init();
- ripng_offset_init();
route_map_add_hook(ripng_routemap_update);
route_map_delete_hook(ripng_routemap_update);