Basic peer table, hard coded for testing.
[trust_router.git] / trp / trps.c
1 #include <fcntl.h>
2 #include <talloc.h>
3 #include <errno.h>
4 #include <unistd.h>
5
6 #include <gsscon.h>
7 #include <tr_rp.h>
8 #include <trust_router/tr_name.h>
9 #include <trp_internal.h>
10 #include <trp_ptable.h>
11 #include <trp_rtable.h>
12 #include <tr_debug.h>
13
14
15 static int trps_destructor(void *object)
16 {
17   TRPS_INSTANCE *trps=talloc_get_type_abort(object, TRPS_INSTANCE);
18   if (trps->rtable!=NULL)
19     trp_rtable_free(trps->rtable);
20   return 0;
21 }
22
23 TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
24 {
25   TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
26   if (trps!=NULL)  {
27     trps->hostname=NULL;
28     trps->port=0;
29     trps->cookie=NULL;
30     trps->conn=NULL;
31     trps->trpc=NULL;
32
33     trps->mq=tr_mq_new(trps);
34     if (trps->mq==NULL) {
35       /* failed to allocate mq */
36       talloc_free(trps);
37       return NULL;
38     }
39
40     trps->ptable=trp_ptable_new(trps);
41     if (trps->ptable==NULL) {
42       /* failed to allocate ptable */
43       talloc_free(trps);
44       return NULL;
45     }
46
47     trps->rtable=trp_rtable_new();
48     if (trps->rtable==NULL) {
49       /* failed to allocate rtable */
50       talloc_free(trps);
51       return NULL;
52     }
53
54     talloc_set_destructor((void *)trps, trps_destructor);
55   }
56   return trps;
57 }
58
59 void trps_free (TRPS_INSTANCE *trps)
60 {
61   if (trps!=NULL)
62     talloc_free(trps);
63 }
64
65 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
66 {
67   return tr_mq_pop(trps->mq);
68 }
69
70 void trps_mq_append(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
71 {
72   tr_mq_append(trps->mq, msg);
73 }
74
75 #if 0
76 static TRP_CONNECTION *trps_find_conn(TRPS_INSTANCE *trps, TR_NAME *peer_gssname)
77 {
78   TRP_CONNECTION *cur=NULL;
79   for (cur=trps->conn; cur!=NULL; cur=trp_connection_get_next(cur)) {
80     if (0==tr_name_cmp(peer_gssname, trp_connection_get_gssname(cur)))
81       break;
82   }
83   return cur;
84 }
85 #endif
86
87 void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
88 {
89   if (trps->conn==NULL)
90     trps->conn=new;
91   else
92     trp_connection_append(trps->conn, new);
93
94   talloc_steal(trps, new);
95 }
96
97 /* ok to call more than once; guarantees connection no longer in the list.
98  * Caller is responsible for freeing the removed element afterwards.  */
99 void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
100 {
101   trps->conn=trp_connection_remove(trps->conn, remove);
102 }
103
104 void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
105 {
106   if (trps->trpc==NULL)
107     trps->trpc=trpc;
108   else
109     trpc_append(trps->trpc, trpc);
110
111   talloc_steal(trps, trpc);
112 }
113
114 /* ok to call more than once; guarantees trpc no longer in the list.
115  * Caller is responsible for freeing the removed element afterwards.  */
116 void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove)
117 {
118   trps->trpc=trpc_remove(trps->trpc, remove);
119 }
120
121 TRP_RC trps_send_msg (TRPS_INSTANCE *trps, void *peer, const char *msg)
122 {
123   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
124   TR_MQ_MSG *mq_msg=NULL;
125   char *msg_dup=NULL;
126   TRP_RC rc=TRP_ERROR;
127
128   /* Currently ignore peer and just send to an open connection.
129    * In reality, need to identify the correct peer and send via that
130    * one.  */
131   if (trps->trpc != NULL) {
132     if (trpc_get_status(trps->trpc)!=TRP_CONNECTION_UP)
133       tr_debug("trps_send_msg: skipping message sent while TRPC connection not up.");
134     else {
135       mq_msg=tr_mq_msg_new(tmp_ctx, "trpc_send");
136       msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
137       tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
138       trpc_mq_append(trps->trpc, mq_msg);
139       rc=TRP_SUCCESS;
140     }
141   }
142   talloc_free(tmp_ctx);
143   return rc;
144 }
145
146 static int trps_listen (TRPS_INSTANCE *trps, int port) 
147 {
148   int rc = 0;
149   int conn = -1;
150   int optval = 1;
151
152   union {
153     struct sockaddr_storage storage;
154     struct sockaddr_in in4;
155   } addr;
156
157   struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
158
159   saddr->sin_port = htons (port);
160   saddr->sin_family = AF_INET;
161   saddr->sin_addr.s_addr = INADDR_ANY;
162
163   if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
164     return conn;
165
166   setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
167
168   if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
169     return rc;
170
171   if (0 > (rc = listen(conn, 512)))
172     return rc;
173
174   tr_debug("trps_listen: TRP Server listening on port %d", port);
175   return conn;
176 }
177
178 #if 0 /* remove this if I forget to do so */
179 /* returns EACCES if authorization is denied */
180 int trps_auth_cb(gss_name_t clientName, gss_buffer_t displayName, void *data)
181 {
182   TRPS_INSTANCE *trps = (TRPS_INSTANCE *)data;
183   int result=0;
184
185   if (0!=trps->auth_handler(clientName, displayName, trps->cookie)) {
186     tr_debug("trps_auth_cb: client '%.*s' denied authorization.", displayName->length, displayName->value);
187     result=EACCES; /* denied */
188   }
189
190   return result;
191 }
192 #endif 
193
194 /* get the currently selected route if available */
195 TRP_RENTRY *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
196 {
197   return trp_rtable_get_entry(trps->rtable, comm, realm, peer);
198 }
199
200 TRP_RENTRY *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
201 {
202   return trp_rtable_get_selected_entry(trps->rtable, comm, realm);
203 }
204
205 /* copy the result if you want to keep it */
206 TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
207 {
208   TRP_RENTRY *route=trps_get_selected_route(trps, comm, realm);
209   if (route==NULL)
210     return NULL;
211
212   return trp_rentry_get_next_hop(route);
213 }
214
215
216 /* mark a route as retracted */
217 static void trps_retract_route(TRPS_INSTANCE *trps, TRP_RENTRY *entry)
218 {
219   trp_rentry_set_metric(entry, TRP_METRIC_INFINITY);
220 }
221
222 /* is this route retracted? */
223 static int trps_route_retracted(TRPS_INSTANCE *trps, TRP_RENTRY *entry)
224 {
225   return (trp_rentry_get_metric(entry)==TRP_METRIC_INFINITY);
226 }
227
228 static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
229 {
230   int err=0;
231   char *buf=NULL;
232   size_t buflen = 0;
233   TR_NAME *peer=NULL;
234
235   tr_debug("trps_read_message: started");
236   if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
237                                        *(trp_connection_get_gssctx(conn)), 
238                                        &buf,
239                                        &buflen)) {
240     tr_debug("trps_read_message: error");
241     if (buf)
242       free(buf);
243     return TRP_ERROR;
244   }
245
246   tr_debug("trps_read_message(): Request Received, %u bytes.", (unsigned) buflen);
247   tr_debug("trps_read_message(): %.*s", buflen, buf);
248
249   *msg=tr_msg_decode(buf, buflen);
250   free(buf);
251   if (*msg==NULL)
252     return TRP_NOPARSE;
253
254   peer=trp_connection_get_peer(conn);
255   /* verify we received a message we support, otherwise drop it now */
256   switch (tr_msg_get_msg_type(*msg)) {
257   case TRP_UPDATE:
258     trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(peer));
259     break;
260
261   case TRP_REQUEST:
262     trp_req_set_peer(tr_msg_get_trp_req(*msg), tr_dup_name(peer));
263     break;
264
265   default:
266     tr_debug("trps_read_message: received unsupported message from %.*s", peer->len, peer->buf);
267     tr_msg_free_decoded(*msg);
268     *msg=NULL;
269     return TRP_UNSUPPORTED;
270   }
271   
272   return TRP_SUCCESS;
273 }
274
275 int trps_get_listener(TRPS_INSTANCE *trps,
276                       TRPS_MSG_FUNC msg_handler,
277                       TRP_AUTH_FUNC auth_handler,
278                       const char *hostname,
279                       unsigned int port,
280                       void *cookie)
281 {
282   int listen = -1;
283
284   if (0 > (listen = trps_listen(trps, port))) {
285     char errbuf[256];
286     if (0 == strerror_r(errno, errbuf, 256)) {
287       tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf);
288     } else {
289       tr_debug("trps_get_listener: Unknown error openining port %d.", port);
290     }
291   } 
292
293   if (listen > 0) {
294     /* opening port succeeded */
295     tr_debug("trps_get_listener: Opened port %d.", port);
296     
297     /* make this socket non-blocking */
298     if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
299       tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
300       close(listen);
301       listen=-1;
302     }
303   }
304
305   if (listen > 0) {
306     /* store the caller's request handler & cookie */
307     trps->msg_handler = msg_handler;
308     trps->auth_handler = auth_handler;
309     trps->hostname = talloc_strdup(trps, hostname);
310     trps->port = port;
311     trps->cookie = cookie;
312   }
313
314   return listen;
315 }
316
317 void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
318 {
319   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
320   TR_MSG *msg=NULL;
321   TRP_RC rc=TRP_ERROR;
322
323   /* try to establish a GSS context */
324   if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
325     tr_notice("tr_trps_conn_thread: failed to authorize connection");
326     pthread_exit(NULL);
327   }
328   tr_notice("trps_handle_connection: authorized connection");
329   
330   /* loop as long as the connection exists */
331   while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
332     rc=trps_read_message(trps, conn, &msg);
333     switch(rc) {
334     case TRP_SUCCESS:
335       trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
336       break;
337
338     case TRP_ERROR:
339       trp_connection_close(conn);
340       break;
341
342     default:
343       tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
344     }
345   }
346
347   tr_debug("trps_handle_connection: connection closed.");
348   talloc_free(tmp_ctx);
349 }
350
351 static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
352 {
353   if (trp_upd_get_inforec(upd)==NULL) {
354     tr_notice("trps_validate_update: received TRP update with no info records.");
355     return TRP_ERROR;
356   }
357
358   if (trp_upd_get_peer(upd)==NULL) {
359     tr_notice("trps_validate_update: received TRP update without origin peer information.");
360     return TRP_ERROR;
361   }
362   
363   return TRP_SUCCESS;
364 }
365
366 /* ensure that the update could be accepted if feasible */
367 static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
368 {
369   switch(trp_inforec_get_type(rec)) {
370   case TRP_INFOREC_TYPE_ROUTE:
371     if ((trp_inforec_get_comm(rec)==NULL)
372        || (trp_inforec_get_realm(rec)==NULL)
373        || (trp_inforec_get_trust_router(rec)==NULL)
374        || (trp_inforec_get_next_hop(rec)==NULL)) {
375       tr_debug("trps_validate_inforec: missing record info.");
376       return TRP_ERROR;
377     }
378
379     /* check for valid metric */
380     if ((trp_inforec_get_metric(rec)==TRP_METRIC_INVALID)
381        || (trp_inforec_get_metric(rec)>TRP_METRIC_INFINITY)) {
382       tr_debug("trps_validate_inforec: invalid metric.");
383       return TRP_ERROR;
384     }
385
386     /* check for valid interval */
387     if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
388       tr_debug("trps_validate_inforec: invalid interval.");
389       return TRP_ERROR;
390     }
391     break;
392
393   default:
394     tr_notice("trps_validate_inforec: unsupported record type.");
395     return TRP_UNSUPPORTED;
396   }
397
398   return TRP_SUCCESS;
399 }
400
401 /* link cost to a peer */
402 static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
403 {
404   return 1;
405 }
406
407 static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
408 {
409   TRP_RENTRY *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
410   if (entry==NULL)
411     return TRP_METRIC_INFINITY;
412   return trp_rentry_get_metric(entry) + trps_cost(trps, peer);
413 }
414
415 static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
416 {
417   unsigned int rec_metric=trp_inforec_get_metric(rec);
418   unsigned int new_metric=0;
419   unsigned int current_metric=0;
420   TR_NAME *next_hop=NULL;
421
422   /* we check these in the validation stage, but just in case... */
423   if ((rec_metric==TRP_METRIC_INVALID) || (rec_metric>TRP_METRIC_INFINITY))
424     return 0;
425
426   /* retractions (aka infinite metrics) are always feasible */
427   if (rec_metric==TRP_METRIC_INFINITY)
428     return 1;
429
430   /* updates from our current next hop are always feasible*/
431   next_hop=trps_get_next_hop(trps,
432                              trp_inforec_get_comm(rec),
433                              trp_inforec_get_realm(rec));;
434   if ((next_hop!=NULL)
435      && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
436     return 1;
437   }
438     
439
440   /* compare the existing metric we advertise to what we would advertise
441    * if we accept this update */
442   current_metric=trps_advertised_metric(trps,
443                                         trp_inforec_get_comm(rec),
444                                         trp_inforec_get_realm(rec),
445                                         trp_inforec_get_next_hop(rec));
446   new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
447   if (new_metric <= current_metric)
448     return 1;
449   else
450     return 0;
451 }
452
453 /* uses memory pointed to by *ts, also returns that value. On error, its contents are {0,0} */
454 static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts)
455 {
456   const unsigned int small_factor=3; /* how many intervals we wait before expiring */
457   if (0!=clock_gettime(CLOCK_REALTIME, ts)) {
458     tr_err("trps_compute_expiry: could not read realtime clock.");
459     ts->tv_sec=0;
460     ts->tv_nsec=0;
461   }
462   ts->tv_sec += small_factor*interval;
463   return ts;
464 }
465
466 static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
467 {
468   TRP_RENTRY *entry=NULL;
469
470   entry=trp_rtable_get_entry(trps->rtable,
471                              trp_inforec_get_comm(rec),
472                              trp_inforec_get_realm(rec),
473                              trp_inforec_get_next_hop(rec));
474   if (entry==NULL) {
475     entry=trp_rentry_new(NULL);
476     if (entry==NULL) {
477       tr_err("trps_accept_update: unable to allocate new entry.");
478       return TRP_NOMEM;
479     }
480
481     trp_rentry_set_apc(entry, tr_dup_name(trp_inforec_get_comm(rec)));
482     trp_rentry_set_realm(entry, tr_dup_name(trp_inforec_get_realm(rec)));
483     trp_rentry_set_peer(entry, tr_dup_name(trp_inforec_get_next_hop(rec)));
484     trp_rentry_set_trust_router(entry, tr_dup_name(trp_inforec_get_trust_router(rec)));
485     trp_rentry_set_next_hop(entry, tr_dup_name(trp_inforec_get_next_hop(rec)));
486     if ((trp_rentry_get_apc(entry)==NULL)
487        ||(trp_rentry_get_realm(entry)==NULL)
488        ||(trp_rentry_get_peer(entry)==NULL)
489        ||(trp_rentry_get_trust_router(entry)==NULL)
490        ||(trp_rentry_get_next_hop(entry)==NULL)) {
491       /* at least one field could not be allocated */
492       tr_err("trps_accept_update: unable to allocate all fields for entry.");
493       trp_rentry_free(entry);
494       return TRP_NOMEM;
495     }
496     trp_rtable_add(trps->rtable, entry);
497   }
498
499   /* We now have an entry in the table, whether it's new or not. Update metric and expiry, unless
500    * the metric is infinity. An infinite metric can only occur here if we just retracted an existing
501    * route (we never accept retractions as new routes), so there is no risk of leaving the expiry
502    * time unset on a new route entry. */
503   tr_debug("trps_accept_update: accepting route update.");
504   trp_rentry_set_metric(entry, trp_inforec_get_metric(rec));
505   trp_rentry_set_interval(entry, trp_inforec_get_interval(rec));
506   if (!trps_route_retracted(trps, entry)) {
507     tr_debug("trps_accept_update: route not retracted, setting expiry timer.");
508     trp_rentry_set_expiry(entry, trps_compute_expiry(trps,
509                                                      trp_rentry_get_interval(entry),
510                                                      trp_rentry_get_expiry(entry)));
511   }
512   return TRP_SUCCESS;
513 }
514
515 /* TODO: handle community updates */
516 static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
517 {
518   unsigned int feas=0;
519   TRP_INFOREC *rec=NULL;
520   TRP_RENTRY *route=NULL;
521
522   if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
523     tr_notice("trps_handle_update: received invalid TRP update.");
524     return TRP_ERROR;
525   }
526
527   rec=trp_upd_get_inforec(upd);
528   for (;rec!=NULL; rec=trp_inforec_get_next(rec)) {
529     /* validate/sanity check the record update */
530     if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
531       tr_notice("trps_handle_update: invalid record in TRP update.");
532       continue;
533     }
534
535     /* determine feasibility */
536     feas=trps_check_feasibility(trps, rec);
537     tr_debug("trps_handle_update: record feasibility=%d", feas);
538
539     /* do we have an existing route? */
540     route=trps_get_route(trps, trp_inforec_get_comm(rec), trp_inforec_get_realm(rec), trp_inforec_get_next_hop(rec));
541     if (route!=NULL) {
542       /* there was a route table entry already */
543       tr_debug("trps_handle_updates: route entry already exists.");
544       if (feas) {
545         /* Update is feasible. Accept it. */
546         trps_accept_update(trps, rec);
547       } else {
548         /* Update is infeasible. Ignore it unless the trust router has changed. */
549         if (0!=tr_name_cmp(trp_rentry_get_trust_router(route),
550                            trp_inforec_get_trust_router(rec))) {
551           /* the trust router associated with the route has changed, treat update as a retraction */
552           trps_retract_route(trps, route);
553         }
554       }
555     } else {
556       /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
557       tr_debug("trps_handle_update: no route entry exists yet.");
558       if (feas && (trp_inforec_get_metric(rec) != TRP_METRIC_INFINITY))
559         trps_accept_update(trps, rec);
560     }
561   }
562   return TRP_SUCCESS;
563 }
564
565 /* TODO: think this through more carefully. At least ought to add hysteresis
566  * to avoid flapping between routers or routes. */
567 static TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
568 {
569   size_t n_apc=0, ii=0;
570   TR_NAME **apc=trp_rtable_get_apcs(trps->rtable, &n_apc);
571   size_t n_realm=0, jj=0;
572   TR_NAME **realm=NULL;
573   size_t n_entry=0, kk=0, kk_min=0;
574   TRP_RENTRY **entry=NULL, *cur_route=NULL;
575   unsigned int min_metric=0, cur_metric=0;
576
577   for (ii=0; ii<n_apc; ii++) {
578     realm=trp_rtable_get_apc_realms(trps->rtable, apc[ii], &n_realm);
579     for (jj=0; jj<n_realm; jj++) {
580       entry=trp_rtable_get_realm_entries(trps->rtable, apc[ii], realm[jj], &n_entry);
581       for (kk=0,min_metric=TRP_METRIC_INFINITY; kk<n_entry; kk++) {
582         if (trp_rentry_get_metric(entry[kk]) < min_metric) {
583           kk_min=kk;
584           min_metric=trp_rentry_get_metric(entry[kk]);
585         }
586       }
587
588       cur_route=trps_get_selected_route(trps, apc[ii], realm[jj]);
589       if (cur_route!=NULL) {
590         cur_metric=trp_rentry_get_metric(cur_route);
591         if (min_metric < cur_metric) {
592           trp_rentry_set_selected(cur_route, 0);
593           trp_rentry_set_selected(entry[kk_min], 1);
594         } else if (cur_metric==TRP_METRIC_INFINITY)
595           trp_rentry_set_selected(cur_route, 0);
596       } else if (min_metric<TRP_METRIC_INFINITY)
597         trp_rentry_set_selected(entry[kk_min], 1);
598
599       talloc_free(entry);
600       entry=NULL; n_entry=0;
601     }
602     talloc_free(realm);
603     realm=NULL; n_realm=0;
604   }
605   talloc_free(apc);
606   apc=NULL; n_apc=0;
607
608   return TRP_SUCCESS;
609 }
610
611 TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
612 {
613   TRP_RC rc=TRP_ERROR;
614
615   switch (tr_msg_get_msg_type(tr_msg)) {
616   case TRP_UPDATE:
617     rc=trps_handle_update(trps, tr_msg_get_trp_upd(tr_msg));
618     if (rc==TRP_SUCCESS) {
619       rc=trps_update_active_routes(trps);
620     }
621     return rc;
622
623   case TRP_REQUEST:
624     return TRP_UNSUPPORTED;
625
626   default:
627     /* unknown error or one we don't care about (e.g., TID messages) */
628     return TRP_ERROR;
629   }
630 }
631
632 /* true if curtime >= expiry */
633 static int trps_expired(struct timespec *expiry, struct timespec *curtime)
634 {
635   return ((curtime->tv_sec > expiry->tv_sec)
636          || ((curtime->tv_sec == expiry->tv_sec)
637             &&(curtime->tv_nsec > expiry->tv_nsec)));
638 }
639
640 /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
641  * If its metric is finite, the metric is set to infinite and the route's expiration time is updated. */
642 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
643 {
644   struct timespec sweep_time={0,0};
645   TRP_RENTRY **entry=NULL;
646   size_t n_entry=0;
647   size_t ii=0;
648
649   /* use a single time for the entire sweep */
650   if (0!=clock_gettime(CLOCK_REALTIME, &sweep_time)) {
651     tr_err("trps_sweep_routes: could not read realtime clock.");
652     sweep_time.tv_sec=0;
653     sweep_time.tv_nsec=0;
654     return TRP_ERROR;
655   }
656
657   entry=trp_rtable_get_entries(trps->rtable, &n_entry); /* must talloc_free *entry */
658
659   /* loop over the entries */
660   for (ii=0; ii<n_entry; ii++) {
661     if (trps_expired(trp_rentry_get_expiry(entry[ii]), &sweep_time)) {
662       tr_debug("trps_sweep_routes: route expired.");
663       if (TRP_METRIC_INFINITY==trp_rentry_get_metric(entry[ii])) {
664         /* flush route */
665         tr_debug("trps_sweep_routes: metric was infinity, flushing route.");
666         trp_rtable_remove(trps->rtable, entry[ii]); /* entry[ii] is no longer valid */
667         entry[ii]=NULL;
668       } else {
669         /* set metric to infinity and reset timer */
670         tr_debug("trps_sweep_routes: setting metric to infinity and resetting expiry.");
671         trp_rentry_set_metric(entry[ii], TRP_METRIC_INFINITY);
672         trp_rentry_set_expiry(entry[ii], trps_compute_expiry(trps,
673                                                              trp_rentry_get_interval(entry[ii]),
674                                                              trp_rentry_get_expiry(entry[ii])));
675       }
676     }
677   }
678
679   talloc_free(entry);
680   return TRP_SUCCESS;
681 }
682
683 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
684 {
685   return trp_ptable_add(trps->ptable, peer);
686 }