aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/builtinurl/builtinurl.h
AgeCommit message (Expand)AuthorFilesLines
2019-09-18builtinurl: initial working attemptDave Barach1-0/+57
/a> 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
/*
 * vrrp_periodic.c - vrrp plug-in periodic function
 *
 * Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 */

#include <vlib/vlib.h>
#include <vppinfra/error.h>
#include <vrrp/vrrp.h>
#include <vrrp/vrrp_packet.h>

static int
vrrp_vr_timer_compare (const void *v1, const void *v2)
{
  vrrp_main_t *vmp = &vrrp_main;
  const u32 *idx1, *idx2;
  vrrp_vr_timer_t *timer1, *timer2;

  idx1 = v1;
  idx2 = v2;

  timer1 = pool_elt_at_index (vmp->vr_timers, *idx1);
  timer2 = pool_elt_at_index (vmp->vr_timers, *idx2);

  /* don't check equality, they are unlikely to be exactly equal and
   * if it occurs, it won't matter what order they were in.
   * sort the list in reverse so we can pick the next timer off the end */
  if (timer1->expire_time > timer2->expire_time)
    return -1;
  else
    return 1;
}

static u32
vrrp_vr_timer_get_next (void)
{
  vrrp_main_t *vmp = &vrrp_main;
  int n_timers;

  n_timers = vec_len (vmp->pending_timers);

  if (!n_timers)
    return ~0;

  return vec_elt (vmp->pending_timers, n_timers - 1);
}

/* cancel an existing timer. This could happen because:
 * - adv timer expired on master. another adv should be scheduled.
 * - a shutdown event is received
 * - a master is preempted by a higher priority master
 * - adv received on backup. master down timer should be rescheduled.
 */
void
vrrp_vr_timer_cancel (vrrp_vr_t * vr)
{
  vrrp_main_t *vmp = &vrrp_main;
  u32 *t;

  /* don't search for a timer that was already canceled or never set */
  if (vr->runtime.timer_index == ~0)
    return;

  /* timers stored in descending order, start at the end of the list */
  /* vec_foreach_backwards does not deal with 0 pointers, check first */
  if (vmp->pending_timers)
    vec_foreach_backwards (t, vmp->pending_timers)
    {
      if (*t == vr->runtime.timer_index)
	{
	  vec_delete (vmp->pending_timers, 1, t - vmp->pending_timers);
	  break;
	}
    }

  if (!pool_is_free_index (vmp->vr_timers, vr->runtime.timer_index))
    pool_put_index (vmp->vr_timers, vr->runtime.timer_index);

  vr->runtime.timer_index = ~0;

  vlib_process_signal_event (vmp->vlib_main, vrrp_periodic_node.index,
			     VRRP_EVENT_VR_TIMER_UPDATE, 0);
}

void
vrrp_vr_timer_set (vrrp_vr_t * vr, vrrp_vr_timer_type_t type)
{
  vrrp_main_t *vmp = &vrrp_main;
  vlib_main_t *vm = vlib_get_main ();
  vrrp_vr_timer_t *timer;
  f64 now;

  /* Each VR should be waiting on at most 1 timer at any given time.
   * If there is already a timer set for this VR, cancel it.
   */
  if (vr->runtime.timer_index != ~0)
    vrrp_vr_timer_cancel (vr);

  pool_get (vmp->vr_timers, timer);
  vr->runtime.timer_index = timer - vmp->vr_timers;

  timer->vr_index = vr - vmp->vrs;
  timer->type = type;

  now = vlib_time_now (vm);

  /* RFC 5798 specifies that timers are in centiseconds, so x / 100.0 */
  switch (type)
    {
    case VRRP_VR_TIMER_ADV:
      timer->expire_time = now + (vr->config.adv_interval / 100.0);
      break;
    case VRRP_VR_TIMER_MASTER_DOWN:
      timer->expire_time = now + (vr->runtime.master_down_int / 100.0);
      break;
    default:
      /* should never reach here */
      clib_warning ("Unrecognized VRRP timer type (%d)", type);
      return;
    }

  vec_add1 (vmp->pending_timers, vr->runtime.timer_index);

  vec_sort_with_function (vmp->pending_timers, vrrp_vr_timer_compare);

  vlib_process_signal_event (vmp->vlib_main, vrrp_periodic_node.index,
			     VRRP_EVENT_VR_TIMER_UPDATE, 0);
}

void
vrrp_vr_timer_timeout (u32 timer_index)
{
  vrrp_main_t *vmp = &vrrp_main;
  vrrp_vr_timer_t *timer;
  vrrp_vr_t *vr;

  if (pool_is_free_index (vmp->vr_timers, timer_index))
    {
      clib_warning ("Timeout on free timer index %u", timer_index);
      return;
    }

  timer = pool_elt_at_index (vmp->vr_timers, timer_index);
  vr = pool_elt_at_index (vmp->vrs, timer->vr_index);

  switch (timer->type)
    {
    case VRRP_VR_TIMER_ADV:
      vrrp_adv_send (vr, 0);
      vrrp_vr_timer_set (vr, VRRP_VR_TIMER_ADV);
      break;
    case VRRP_VR_TIMER_MASTER_DOWN:
      vrrp_vr_transition (vr, VRRP_VR_STATE_MASTER, NULL);
      break;
    default:
      clib_warning ("Unrecognized timer type %d", timer->type);
      return;
    }

}

static uword
vrrp_periodic_process (vlib_main_t * vm,
		       vlib_node_runtime_t * rt, vlib_frame_t * f)
{
  vrrp_main_t *pm = &vrrp_main;
  f64 now;
  f64 timeout = 10.0;
  uword *event_data = 0;
  uword event_type;
  u32 next_timer = ~0;
  vrrp_vr_timer_t *timer;

  while (1)
    {
      now = vlib_time_now (vm);

      if (next_timer == ~0)
	{
	  vlib_process_wait_for_event (vm);
	}
      else
	{
	  timer = pool_elt_at_index (pm->vr_timers, next_timer);
	  timeout = timer->expire_time - now;

	  vlib_process_wait_for_event_or_clock (vm, timeout);
	}

      event_type = vlib_process_get_events (vm, (uword **) & event_data);

      switch (event_type)
	{
	  /* Handle VRRP_EVENT_VR_TIMER_UPDATE */
	case VRRP_EVENT_VR_TIMER_UPDATE:
	  next_timer = vrrp_vr_timer_get_next ();
	  break;

	  /* Handle periodic timeouts */
	case ~0:
	  vrrp_vr_timer_timeout (next_timer);
	  next_timer = vrrp_vr_timer_get_next ();
	  break;
	}
      vec_reset_length (event_data);
    }
  return 0;
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (vrrp_periodic_node) =
{
  .function = vrrp_periodic_process,
  .type = VLIB_NODE_TYPE_PROCESS,
  .name = "vrrp-periodic-process",
};
/* *INDENT-ON* */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */