Test peer table and update selection functions. Seem to work.
[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_metric_is_infinite(trp_rentry_get_metric(entry)));
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_metric_is_valid(trp_inforec_get_metric(rec))) {
381       tr_debug("trps_validate_inforec: invalid metric.");
382       return TRP_ERROR;
383     }
384
385     /* check for valid interval */
386     if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
387       tr_debug("trps_validate_inforec: invalid interval.");
388       return TRP_ERROR;
389     }
390     break;
391
392   default:
393     tr_notice("trps_validate_inforec: unsupported record type.");
394     return TRP_UNSUPPORTED;
395   }
396
397   return TRP_SUCCESS;
398 }
399
400 /* link cost to a peer */
401 static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
402 {
403   return 1;
404 }
405
406 static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
407 {
408   TRP_RENTRY *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
409   if (entry==NULL)
410     return TRP_METRIC_INFINITY;
411   return trp_rentry_get_metric(entry) + trps_cost(trps, peer);
412 }
413
414 static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
415 {
416   unsigned int rec_metric=trp_inforec_get_metric(rec);
417   unsigned int new_metric=0;
418   unsigned int current_metric=0;
419   TR_NAME *next_hop=NULL;
420
421   /* we check these in the validation stage, but just in case... */
422   if (trp_metric_is_invalid(rec_metric))
423     return 0;
424
425   /* retractions (aka infinite metrics) are always feasible */
426   if (trp_metric_is_infinite(rec_metric))
427     return 1;
428
429   /* updates from our current next hop are always feasible*/
430   next_hop=trps_get_next_hop(trps,
431                              trp_inforec_get_comm(rec),
432                              trp_inforec_get_realm(rec));;
433   if ((next_hop!=NULL)
434      && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
435     return 1;
436   }
437     
438
439   /* compare the existing metric we advertise to what we would advertise
440    * if we accept this update */
441   current_metric=trps_advertised_metric(trps,
442                                         trp_inforec_get_comm(rec),
443                                         trp_inforec_get_realm(rec),
444                                         trp_inforec_get_next_hop(rec));
445   new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
446   if (new_metric <= current_metric)
447     return 1;
448   else
449     return 0;
450 }
451
452 /* uses memory pointed to by *ts, also returns that value. On error, its contents are {0,0} */
453 static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts)
454 {
455   const unsigned int small_factor=3; /* how many intervals we wait before expiring */
456   if (0!=clock_gettime(CLOCK_REALTIME, ts)) {
457     tr_err("trps_compute_expiry: could not read realtime clock.");
458     ts->tv_sec=0;
459     ts->tv_nsec=0;
460   }
461   ts->tv_sec += small_factor*interval;
462   return ts;
463 }
464
465 static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
466 {
467   TRP_RENTRY *entry=NULL;
468
469   entry=trp_rtable_get_entry(trps->rtable,
470                              trp_inforec_get_comm(rec),
471                              trp_inforec_get_realm(rec),
472                              trp_inforec_get_next_hop(rec));
473   if (entry==NULL) {
474     entry=trp_rentry_new(NULL);
475     if (entry==NULL) {
476       tr_err("trps_accept_update: unable to allocate new entry.");
477       return TRP_NOMEM;
478     }
479
480     trp_rentry_set_apc(entry, tr_dup_name(trp_inforec_get_comm(rec)));
481     trp_rentry_set_realm(entry, tr_dup_name(trp_inforec_get_realm(rec)));
482     trp_rentry_set_peer(entry, tr_dup_name(trp_inforec_get_next_hop(rec)));
483     trp_rentry_set_trust_router(entry, tr_dup_name(trp_inforec_get_trust_router(rec)));
484     trp_rentry_set_next_hop(entry, tr_dup_name(trp_inforec_get_next_hop(rec)));
485     if ((trp_rentry_get_apc(entry)==NULL)
486        ||(trp_rentry_get_realm(entry)==NULL)
487        ||(trp_rentry_get_peer(entry)==NULL)
488        ||(trp_rentry_get_trust_router(entry)==NULL)
489        ||(trp_rentry_get_next_hop(entry)==NULL)) {
490       /* at least one field could not be allocated */
491       tr_err("trps_accept_update: unable to allocate all fields for entry.");
492       trp_rentry_free(entry);
493       return TRP_NOMEM;
494     }
495     trp_rtable_add(trps->rtable, entry);
496   }
497
498   /* We now have an entry in the table, whether it's new or not. Update metric and expiry, unless
499    * the metric is infinity. An infinite metric can only occur here if we just retracted an existing
500    * route (we never accept retractions as new routes), so there is no risk of leaving the expiry
501    * time unset on a new route entry. */
502   tr_debug("trps_accept_update: accepting route update.");
503   trp_rentry_set_metric(entry, trp_inforec_get_metric(rec));
504   trp_rentry_set_interval(entry, trp_inforec_get_interval(rec));
505   if (!trps_route_retracted(trps, entry)) {
506     tr_debug("trps_accept_update: route not retracted, setting expiry timer.");
507     trp_rentry_set_expiry(entry, trps_compute_expiry(trps,
508                                                      trp_rentry_get_interval(entry),
509                                                      trp_rentry_get_expiry(entry)));
510   }
511   return TRP_SUCCESS;
512 }
513
514 /* TODO: handle community updates */
515 static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
516 {
517   unsigned int feas=0;
518   TRP_INFOREC *rec=NULL;
519   TRP_RENTRY *route=NULL;
520
521   if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
522     tr_notice("trps_handle_update: received invalid TRP update.");
523     return TRP_ERROR;
524   }
525
526   rec=trp_upd_get_inforec(upd);
527   for (;rec!=NULL; rec=trp_inforec_get_next(rec)) {
528     /* validate/sanity check the record update */
529     if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
530       tr_notice("trps_handle_update: invalid record in TRP update.");
531       continue;
532     }
533
534     /* determine feasibility */
535     feas=trps_check_feasibility(trps, rec);
536     tr_debug("trps_handle_update: record feasibility=%d", feas);
537
538     /* do we have an existing route? */
539     route=trps_get_route(trps, trp_inforec_get_comm(rec), trp_inforec_get_realm(rec), trp_inforec_get_next_hop(rec));
540     if (route!=NULL) {
541       /* there was a route table entry already */
542       tr_debug("trps_handle_updates: route entry already exists.");
543       if (feas) {
544         /* Update is feasible. Accept it. */
545         trps_accept_update(trps, rec);
546       } else {
547         /* Update is infeasible. Ignore it unless the trust router has changed. */
548         if (0!=tr_name_cmp(trp_rentry_get_trust_router(route),
549                            trp_inforec_get_trust_router(rec))) {
550           /* the trust router associated with the route has changed, treat update as a retraction */
551           trps_retract_route(trps, route);
552         }
553       }
554     } else {
555       /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
556       tr_debug("trps_handle_update: no route entry exists yet.");
557       if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
558         trps_accept_update(trps, rec);
559     }
560   }
561   return TRP_SUCCESS;
562 }
563
564 /* choose the best route to comm/realm, optionally excluding routes to a particular peer */
565 static TRP_RENTRY *trps_find_best_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *exclude_peer)
566 {
567   TRP_RENTRY **entry=NULL;
568   TRP_RENTRY *best=NULL;
569   size_t n_entry=0;
570   unsigned int kk=0;
571   unsigned int kk_min=0;
572   unsigned int min_metric=TRP_METRIC_INFINITY;
573
574   entry=trp_rtable_get_realm_entries(trps->rtable, comm, realm, &n_entry);
575   for (kk=0; kk<n_entry; kk++) {
576     if (trp_rentry_get_metric(entry[kk]) < min_metric) {
577       if ((exclude_peer==NULL) || (0!=tr_name_cmp(trp_rentry_get_peer(entry[kk]),
578                                                   exclude_peer))) {
579         kk_min=kk;
580         min_metric=trp_rentry_get_metric(entry[kk]);
581       }
582     }
583   }
584   if (trp_metric_is_finite(min_metric));
585     best=entry[kk_min];
586   
587   talloc_free(entry);
588   return best;
589 }
590
591 /* TODO: think this through more carefully. At least ought to add hysteresis
592  * to avoid flapping between routers or routes. */
593 static TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
594 {
595   size_t n_apc=0, ii=0;
596   TR_NAME **apc=trp_rtable_get_apcs(trps->rtable, &n_apc);
597   size_t n_realm=0, jj=0;
598   TR_NAME **realm=NULL;
599   TRP_RENTRY *best_route=NULL, *cur_route=NULL;
600   unsigned int best_metric=0, cur_metric=0;
601
602   for (ii=0; ii<n_apc; ii++) {
603     realm=trp_rtable_get_apc_realms(trps->rtable, apc[ii], &n_realm);
604     for (jj=0; jj<n_realm; jj++) {
605       best_route=trps_find_best_route(trps, apc[ii], realm[jj], NULL);
606       if (best_route==NULL)
607         best_metric=TRP_METRIC_INFINITY;
608       else
609         best_metric=trp_rentry_get_metric(best_route);
610
611       cur_route=trps_get_selected_route(trps, apc[ii], realm[jj]);
612       if (cur_route!=NULL) {
613         cur_metric=trp_rentry_get_metric(cur_route);
614         if ((best_metric < cur_metric) && (trp_metric_is_finite(best_metric))) {
615           trp_rentry_set_selected(cur_route, 0);
616           trp_rentry_set_selected(best_route, 1);
617         } else if (!trp_metric_is_finite(cur_metric)) /* rejects infinite or invalid metrics */
618           trp_rentry_set_selected(cur_route, 0);
619       } else if (trp_metric_is_finite(best_metric))
620         trp_rentry_set_selected(best_route, 1);
621     }
622     if (realm!=NULL)
623       talloc_free(realm);
624     realm=NULL; n_realm=0;
625   }
626   if (apc!=NULL)
627     talloc_free(apc);
628   apc=NULL; n_apc=0;
629
630   return TRP_SUCCESS;
631 }
632
633 TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
634 {
635   TRP_RC rc=TRP_ERROR;
636
637   switch (tr_msg_get_msg_type(tr_msg)) {
638   case TRP_UPDATE:
639     rc=trps_handle_update(trps, tr_msg_get_trp_upd(tr_msg));
640     if (rc==TRP_SUCCESS) {
641       rc=trps_update_active_routes(trps);
642     }
643     return rc;
644
645   case TRP_REQUEST:
646     return TRP_UNSUPPORTED;
647
648   default:
649     /* unknown error or one we don't care about (e.g., TID messages) */
650     return TRP_ERROR;
651   }
652 }
653
654 /* true if curtime >= expiry */
655 static int trps_expired(struct timespec *expiry, struct timespec *curtime)
656 {
657   return ((curtime->tv_sec > expiry->tv_sec)
658          || ((curtime->tv_sec == expiry->tv_sec)
659             &&(curtime->tv_nsec > expiry->tv_nsec)));
660 }
661
662 /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
663  * If its metric is finite, the metric is set to infinite and the route's expiration time is updated. */
664 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
665 {
666   struct timespec sweep_time={0,0};
667   TRP_RENTRY **entry=NULL;
668   size_t n_entry=0;
669   size_t ii=0;
670
671   /* use a single time for the entire sweep */
672   if (0!=clock_gettime(CLOCK_REALTIME, &sweep_time)) {
673     tr_err("trps_sweep_routes: could not read realtime clock.");
674     sweep_time.tv_sec=0;
675     sweep_time.tv_nsec=0;
676     return TRP_ERROR;
677   }
678
679   entry=trp_rtable_get_entries(trps->rtable, &n_entry); /* must talloc_free *entry */
680
681   /* loop over the entries */
682   for (ii=0; ii<n_entry; ii++) {
683     if (trps_expired(trp_rentry_get_expiry(entry[ii]), &sweep_time)) {
684       tr_debug("trps_sweep_routes: route expired.");
685       if (!trp_metric_is_finite(trp_rentry_get_metric(entry[ii]))) {
686         /* flush route */
687         tr_debug("trps_sweep_routes: metric was infinity, flushing route.");
688         trp_rtable_remove(trps->rtable, entry[ii]); /* entry[ii] is no longer valid */
689         entry[ii]=NULL;
690       } else {
691         /* set metric to infinity and reset timer */
692         tr_debug("trps_sweep_routes: setting metric to infinity and resetting expiry.");
693         trp_rentry_set_metric(entry[ii], TRP_METRIC_INFINITY);
694         trp_rentry_set_expiry(entry[ii], trps_compute_expiry(trps,
695                                                              trp_rentry_get_interval(entry[ii]),
696                                                              trp_rentry_get_expiry(entry[ii])));
697       }
698     }
699   }
700
701   talloc_free(entry);
702   return TRP_SUCCESS;
703 }
704
705 /* select the correct route to comm/realm to be announced to peer */
706 static TRP_RENTRY *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname)
707 {
708   TRP_RENTRY *route;
709
710   /* Take the currently selected route unless it is through the peer we're sending the update to.
711    * I.e., enforce the split horizon rule. */
712   route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
713   if (0==tr_name_cmp(peer_gssname, trp_rentry_get_peer(route))) {
714     /* the selected entry goes through the peer we're reporting to, choose an alternate */
715     route=trps_find_best_route(trps, comm, realm, peer_gssname);
716     if (!trp_metric_is_finite(trp_rentry_get_metric(route)))
717       route=NULL; /* don't advertise a retracted route */
718   }
719   return route;
720 }
721
722 /* returns an array of pointers to updates (*not* an array of updates). Returns number of entries
723  * via n_update parameter. (The allocated space will generally be larger than required, see note in
724  * the code.) */
725 TRP_RENTRY **trps_select_updates_for_peer(TALLOC_CTX *memctx, TRPS_INSTANCE *trps, TR_NAME *peer_gssname, size_t *n_update)
726 {
727   size_t n_apc=0;
728   TR_NAME **apc=trp_rtable_get_apcs(trps->rtable, &n_apc);
729   TR_NAME **realm=NULL;
730   size_t n_realm=0;
731   size_t ii=0, jj=0;
732   TRP_RENTRY *best=NULL;
733   TRP_RENTRY **result=NULL;
734   size_t n_used=0;
735
736   /* Need to allocate space for the results. For simplicity, we just allocate a block
737    * with space for every route table entry to be returned. This is guaranteed to be large
738    * enough. If the routing table gets very large, this may be wasteful, but that seems
739    * unlikely to be significant in the near future. */
740   result=talloc_array(memctx, TRP_RENTRY *, trp_rtable_size(trps->rtable));
741   if (result==NULL) {
742     talloc_free(apc);
743     return NULL;
744   }
745   
746   for (ii=0; ii<n_apc; ii++) {
747     realm=trp_rtable_get_apc_realms(trps->rtable, apc[ii], &n_realm);
748     for (jj=0; jj<n_realm; jj++) {
749       best=trps_select_realm_update(trps, apc[ii], realm[jj], peer_gssname);
750       if (best!=NULL)
751         result[n_used++]=best;
752     }
753     if (realm!=NULL)
754       talloc_free(realm);
755     realm=NULL;
756     n_realm=0;
757   }
758   if (apc!=NULL)
759     talloc_free(apc);
760
761   *n_update=n_used;
762   return result;
763 }
764
765 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
766 {
767   return trp_ptable_add(trps->ptable, peer);
768 }