#include "command.h"
#include "prefix.h"
#include "table.h"
-#include "thread.h"
+#include "frrevent.h"
#include "memory.h"
#include "log.h"
#include "stream.h"
/* Prototypes. */
static void rip_output_process(struct connected *, struct sockaddr_in *, int,
uint8_t);
-static void rip_triggered_update(struct thread *);
+static void rip_triggered_update(struct event *);
static int rip_update_jitter(unsigned long);
static void rip_distance_table_node_cleanup(struct route_table *table,
struct route_node *node);
}
/* RIP route garbage collect timer. */
-static void rip_garbage_collect(struct thread *t)
+static void rip_garbage_collect(struct event *t)
{
struct rip_info *rinfo;
struct route_node *rp;
- rinfo = THREAD_ARG(t);
+ rinfo = EVENT_ARG(t);
/* Off timeout timer. */
- THREAD_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_timeout);
/* Get route_node pointer. */
rp = rinfo->rp;
{
struct route_node *rp = rinfo_new->rp;
struct rip_info *rinfo = NULL;
+ struct rip_info *rinfo_exist = NULL;
struct list *list = NULL;
+ struct listnode *node = NULL;
+ struct listnode *nnode = NULL;
if (rp->info == NULL)
rp->info = list_new();
if (listcount(list) && !rip->ecmp)
return NULL;
+ /* Add or replace an existing ECMP path with lower neighbor IP */
+ if (listcount(list) && listcount(list) >= rip->ecmp) {
+ struct rip_info *from_highest = NULL;
+
+ /* Find the rip_info struct that has the highest nexthop IP */
+ for (ALL_LIST_ELEMENTS(list, node, nnode, rinfo_exist))
+ if (!from_highest ||
+ (from_highest &&
+ IPV4_ADDR_CMP(&rinfo_exist->from,
+ &from_highest->from) > 0)) {
+ from_highest = rinfo_exist;
+ }
+
+ /* If we have a route in ECMP group, delete the old
+ * one that has a higher next-hop address. Lower IP is
+ * preferred.
+ */
+ if (rip->ecmp > 1 && from_highest &&
+ IPV4_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) {
+ rip_ecmp_delete(rip, from_highest);
+ goto add_or_replace;
+ }
+
+ return NULL;
+ }
+
+add_or_replace:
rinfo = rip_info_new();
memcpy(rinfo, rinfo_new, sizeof(struct rip_info));
listnode_add(list, rinfo);
if (tmp_rinfo == rinfo)
continue;
- THREAD_OFF(tmp_rinfo->t_timeout);
- THREAD_OFF(tmp_rinfo->t_garbage_collect);
+ EVENT_OFF(tmp_rinfo->t_timeout);
+ EVENT_OFF(tmp_rinfo->t_garbage_collect);
list_delete_node(list, node);
rip_info_free(tmp_rinfo);
}
- THREAD_OFF(rinfo->t_timeout);
- THREAD_OFF(rinfo->t_garbage_collect);
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
memcpy(rinfo, rinfo_new, sizeof(struct rip_info));
if (rip_route_rte(rinfo)) {
struct route_node *rp = rinfo->rp;
struct list *list = (struct list *)rp->info;
- THREAD_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_timeout);
if (listcount(list) > 1) {
/* Some other ECMP entries still exist. Just delete this entry.
*/
- THREAD_OFF(rinfo->t_garbage_collect);
+ EVENT_OFF(rinfo->t_garbage_collect);
listnode_delete(list, rinfo);
if (rip_route_rte(rinfo)
&& CHECK_FLAG(rinfo->flags, RIP_RTF_FIB))
}
/* Timeout RIP routes. */
-static void rip_timeout(struct thread *t)
+static void rip_timeout(struct event *t)
{
- struct rip_info *rinfo = THREAD_ARG(t);
+ struct rip_info *rinfo = EVENT_ARG(t);
struct rip *rip = rip_info_get_instance(rinfo);
rip_ecmp_delete(rip, rinfo);
static void rip_timeout_update(struct rip *rip, struct rip_info *rinfo)
{
if (rinfo->metric != RIP_METRIC_INFINITY) {
- THREAD_OFF(rinfo->t_timeout);
- thread_add_timer(master, rip_timeout, rinfo, rip->timeout_time,
- &rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_timeout);
+ event_add_timer(master, rip_timeout, rinfo, rip->timeout_time,
+ &rinfo->t_timeout);
}
}
assert(newinfo.metric
!= RIP_METRIC_INFINITY);
- THREAD_OFF(rinfo->t_timeout);
- THREAD_OFF(rinfo->t_garbage_collect);
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
memcpy(rinfo, &newinfo,
sizeof(struct rip_info));
rip_timeout_update(rip, rinfo);
if (from->sin_port != htons(RIP_PORT_DEFAULT)) {
zlog_info("response doesn't come from RIP port: %d",
from->sin_port);
- rip_peer_bad_packet(rip, from);
+ rip_peer_bad_packet(rip, ri, from);
return;
}
zlog_info(
"This datagram doesn't come from a valid neighbor: %pI4",
&from->sin_addr);
- rip_peer_bad_packet(rip, from);
+ rip_peer_bad_packet(rip, ri, from);
return;
}
; /* Alredy done in rip_read () */
/* Update RIP peer. */
- rip_peer_update(rip, from, packet->version);
+ rip_peer_update(rip, ri, from, packet->version);
/* Set RTE pointer. */
rte = packet->rte;
continue;
}
+ if (packet->version == RIPv1 && rte->tag != 0) {
+ zlog_warn("RIPv1 reserved field is nonzero: %d",
+ ntohs(rte->tag));
+ continue;
+ }
+
/* - is the destination address valid (e.g., unicast; not net 0
or 127) */
if (!rip_destination_check(rte->prefix)) {
zlog_info(
"Network is net 0 or net 127 or it is not unicast network");
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
/* - is the metric valid (i.e., between 1 and 16, inclusive) */
if (!(rte->metric >= 1 && rte->metric <= 16)) {
zlog_info("Route's metric is not in the 1-16 range.");
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
&& rte->nexthop.s_addr != INADDR_ANY) {
zlog_info("RIPv1 packet with nexthop value %pI4",
&rte->nexthop);
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
zlog_warn(
"RIPv2 address %pI4 is not mask /%d applied one",
&rte->prefix, ip_masklen(rte->mask));
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
ret = sendmsg(rip->sock, &msg, 0);
if (IS_RIP_DEBUG_EVENT)
- zlog_debug("SEND to %pI4%d", &sin.sin_addr,
+ zlog_debug("SEND to %pI4 port %d", &sin.sin_addr,
ntohs(sin.sin_port));
if (ret < 0)
RIP_TIMER_ON(rinfo->t_garbage_collect,
rip_garbage_collect,
rip->garbage_time);
- THREAD_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_timeout);
rinfo->flags |= RIP_RTF_CHANGED;
if (IS_RIP_DEBUG_EVENT)
return;
/* RIP peer update. */
- rip_peer_update(rip, from, packet->version);
+ rip_peer_update(rip, ri, from, packet->version);
lim = ((caddr_t)packet) + size;
rte = packet->rte;
}
/* First entry point of RIP packet. */
-static void rip_read(struct thread *t)
+static void rip_read(struct event *t)
{
- struct rip *rip = THREAD_ARG(t);
+ struct rip *rip = EVENT_ARG(t);
int sock;
int ret;
int rtenum;
socklen_t fromlen;
struct interface *ifp = NULL;
struct connected *ifc;
- struct rip_interface *ri;
+ struct rip_interface *ri = NULL;
struct prefix p;
/* Fetch socket then register myself. */
- sock = THREAD_FD(t);
+ sock = EVENT_FD(t);
/* Add myself to tne next event */
rip_event(rip, RIP_READ, sock);
/* Which interface is this packet comes from. */
ifc = if_lookup_address((void *)&from.sin_addr, AF_INET,
rip->vrf->vrf_id);
- if (ifc)
+ if (ifc) {
ifp = ifc->ifp;
+ ri = ifp->info;
+ }
/* RIP packet received */
if (IS_RIP_DEBUG_EVENT)
ifp ? ifp->name : "unknown", rip->vrf_name);
/* If this packet come from unknown interface, ignore it. */
- if (ifp == NULL) {
+ if (ifp == NULL || ri == NULL) {
zlog_info(
"%s: cannot find interface for packet from %pI4 port %d (VRF %s)",
__func__, &from.sin_addr, ntohs(from.sin_port),
if (len < RIP_PACKET_MINSIZ) {
zlog_warn("packet size %d is smaller than minimum size %d", len,
RIP_PACKET_MINSIZ);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
if (len > RIP_PACKET_MAXSIZ) {
zlog_warn("packet size %d is larger than max size %d", len,
RIP_PACKET_MAXSIZ);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
if ((len - RIP_PACKET_MINSIZ) % 20) {
zlog_warn("packet size %d is wrong for RIP packet alignment",
len);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
if (packet->version == 0) {
zlog_info("version 0 with command %d received.",
packet->command);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
packet->version = RIPv2;
/* Is RIP running or is this RIP neighbor ?*/
- ri = ifp->info;
if (!ri->running && !rip_neighbor_lookup(rip, &from)) {
if (IS_RIP_DEBUG_EVENT)
zlog_debug("RIP is not enabled on interface %s.",
ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
zlog_debug(
" packet's v%d doesn't fit to if version spec",
packet->version);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
"packet RIPv%d is dropped because authentication disabled",
packet->version);
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
zlog_debug(
"RIPv1 dropped because authentication enabled");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
} else if (ri->auth_type != RIP_NO_AUTH) {
zlog_debug(
"RIPv2 authentication failed: no auth RTE in packet");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
zlog_debug(
"RIPv2 dropped because authentication enabled");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
zlog_debug("RIPv2 %s authentication failure",
auth_desc);
ripd_notif_send_auth_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
}
zlog_info(
"Obsolete command %s received, please sent it to routed",
lookup_msg(rip_msg, packet->command, NULL));
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
case RIP_POLL_ENTRY:
zlog_info("Obsolete command %s received",
lookup_msg(rip_msg, packet->command, NULL));
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
default:
zlog_info("Unknown RIP command %d received", packet->command);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
}
}
}
/* RIP's periodical timer. */
-static void rip_update(struct thread *t)
+static void rip_update(struct event *t)
{
- struct rip *rip = THREAD_ARG(t);
+ struct rip *rip = EVENT_ARG(t);
if (IS_RIP_DEBUG_EVENT)
zlog_debug("update timer fire!");
/* Triggered updates may be suppressed if a regular update is due by
the time the triggered update would be sent. */
- THREAD_OFF(rip->t_triggered_interval);
+ EVENT_OFF(rip->t_triggered_interval);
rip->trigger = 0;
/* Register myself. */
}
/* Triggered update interval timer. */
-static void rip_triggered_interval(struct thread *t)
+static void rip_triggered_interval(struct event *t)
{
- struct rip *rip = THREAD_ARG(t);
+ struct rip *rip = EVENT_ARG(t);
if (rip->trigger) {
rip->trigger = 0;
}
/* Execute triggered update. */
-static void rip_triggered_update(struct thread *t)
+static void rip_triggered_update(struct event *t)
{
- struct rip *rip = THREAD_ARG(t);
+ struct rip *rip = EVENT_ARG(t);
int interval;
/* Cancel interval timer. */
- THREAD_OFF(rip->t_triggered_interval);
+ EVENT_OFF(rip->t_triggered_interval);
rip->trigger = 0;
/* Logging triggered update. */
update is triggered when the timer expires. */
interval = (frr_weak_random() % 5) + 1;
- thread_add_timer(master, rip_triggered_interval, rip, interval,
- &rip->t_triggered_interval);
+ event_add_timer(master, rip_triggered_interval, rip, interval,
+ &rip->t_triggered_interval);
}
/* Withdraw redistributed route. */
rinfo->metric = RIP_METRIC_INFINITY;
RIP_TIMER_ON(rinfo->t_garbage_collect, rip_garbage_collect,
rip->garbage_time);
- THREAD_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_timeout);
rinfo->flags |= RIP_RTF_CHANGED;
if (IS_RIP_DEBUG_EVENT) {
return RB_FIND(rip_instance_head, &rip_instances, &rip);
}
+/* Update ECMP routes to zebra when `allow-ecmp` changed. */
+void rip_ecmp_change(struct rip *rip)
+{
+ struct route_node *rp;
+ struct rip_info *rinfo;
+ struct list *list;
+ struct listnode *node, *nextnode;
+
+ for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
+ list = rp->info;
+ if (list && listcount(list) > 1) {
+ while (listcount(list) > rip->ecmp) {
+ struct rip_info *from_highest = NULL;
+
+ for (ALL_LIST_ELEMENTS(list, node, nextnode,
+ rinfo)) {
+ if (!from_highest ||
+ (from_highest &&
+ IPV4_ADDR_CMP(
+ &rinfo->from,
+ &from_highest->from) > 0))
+ from_highest = rinfo;
+ }
+
+ rip_ecmp_delete(rip, from_highest);
+ }
+ }
+ }
+}
+
/* Create new RIP instance and set it to global variable. */
struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket)
{
rip->vrf_name = XSTRDUP(MTYPE_RIP_VRF_NAME, vrf_name);
/* Set initial value. */
- rip->ecmp = yang_get_default_bool("%s/allow-ecmp", RIP_INSTANCE);
+ rip->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIP_INSTANCE);
rip->default_metric =
yang_get_default_uint8("%s/default-metric", RIP_INSTANCE);
rip->distance =
switch (event) {
case RIP_READ:
- thread_add_read(master, rip_read, rip, sock, &rip->t_read);
+ event_add_read(master, rip_read, rip, sock, &rip->t_read);
break;
case RIP_UPDATE_EVENT:
- THREAD_OFF(rip->t_update);
+ EVENT_OFF(rip->t_update);
jitter = rip_update_jitter(rip->update_time);
- thread_add_timer(master, rip_update, rip,
- sock ? 2 : rip->update_time + jitter,
- &rip->t_update);
+ event_add_timer(master, rip_update, rip,
+ sock ? 2 : rip->update_time + jitter,
+ &rip->t_update);
break;
case RIP_TRIGGERED_UPDATE:
if (rip->t_triggered_interval)
rip->trigger = 1;
else
- thread_add_event(master, rip_triggered_update, rip, 0,
- &rip->t_triggered_update);
+ event_add_event(master, rip_triggered_update, rip, 0,
+ &rip->t_triggered_update);
break;
default:
break;
if (access_list_apply(alist, &rinfo->rp->p)
== FILTER_DENY)
return 0;
-
- return rdistance->distance;
- } else
- return rdistance->distance;
+ }
+ return rdistance->distance;
}
- if (rip->distance)
- return rip->distance;
-
- return 0;
+ return rip->distance;
}
static void rip_distance_show(struct vty *vty, struct rip *rip)
if (tmp_rinfo == rinfo)
continue;
- THREAD_OFF(tmp_rinfo->t_timeout);
- THREAD_OFF(tmp_rinfo->t_garbage_collect);
+ EVENT_OFF(tmp_rinfo->t_timeout);
+ EVENT_OFF(tmp_rinfo->t_garbage_collect);
list_delete_node(list, node);
rip_info_free(tmp_rinfo);
}
struct tm tm;
#define TIME_BUF 25
char timebuf[TIME_BUF];
- struct thread *thread;
+ struct event *thread;
if ((thread = rinfo->t_timeout) != NULL) {
- clock = thread_timer_remain_second(thread);
+ clock = event_timer_remain_second(thread);
gmtime_r(&clock, &tm);
strftime(timebuf, TIME_BUF, "%M:%S", &tm);
vty_out(vty, "%5s", timebuf);
} else if ((thread = rinfo->t_garbage_collect) != NULL) {
- clock = thread_timer_remain_second(thread);
+ clock = event_timer_remain_second(thread);
gmtime_r(&clock, &tm);
strftime(timebuf, TIME_BUF, "%M:%S", &tm);
vty_out(vty, "%5s", timebuf);
vty_out(vty, " Sending updates every %u seconds with +/-50%%,",
rip->update_time);
vty_out(vty, " next due in %lu seconds\n",
- thread_timer_remain_second(rip->t_update));
+ event_timer_remain_second(rip->t_update));
vty_out(vty, " Timeout after %u seconds,", rip->timeout_time);
vty_out(vty, " garbage collect after %u seconds\n", rip->garbage_time);
/* Distribute configuration. */
config_write_distribute(vty, rip->distribute_ctx);
- /* Interface routemap configuration */
- config_write_if_rmap(vty, rip->if_rmap_ctx);
-
vty_out(vty, "exit\n");
write = 1;
route_table_finish(rip->distance_table);
RB_REMOVE(rip_instance_head, &rip_instances, rip);
+ XFREE(MTYPE_TMP, rip->default_bfd_profile);
XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name);
XFREE(MTYPE_RIP, rip);
}
rip_zebra_ipv4_delete(rip, rp);
for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
- THREAD_OFF(rinfo->t_timeout);
- THREAD_OFF(rinfo->t_garbage_collect);
+ EVENT_OFF(rinfo->t_timeout);
+ EVENT_OFF(rinfo->t_garbage_collect);
rip_info_free(rinfo);
}
list_delete(&list);
rip_redistribute_disable(rip);
/* Cancel RIP related timers. */
- THREAD_OFF(rip->t_update);
- THREAD_OFF(rip->t_triggered_update);
- THREAD_OFF(rip->t_triggered_interval);
+ EVENT_OFF(rip->t_update);
+ EVENT_OFF(rip->t_triggered_update);
+ EVENT_OFF(rip->t_triggered_interval);
/* Cancel read thread. */
- THREAD_OFF(rip->t_read);
+ EVENT_OFF(rip->t_read);
/* Close RIP socket. */
close(rip->sock);