Generate scheduled updates. Untested, but builds.
[trust_router.git] / trp / trps.c
1 #include <fcntl.h>
2 #include <talloc.h>
3 #include <errno.h>
4 #include <unistd.h>
5 #include <sys/time.h>
6
7 #include <gsscon.h>
8 #include <tr_rp.h>
9 #include <trust_router/tr_name.h>
10 #include <trp_internal.h>
11 #include <trp_ptable.h>
12 #include <trp_rtable.h>
13 #include <tr_debug.h>
14
15
16 static int trps_destructor(void *object)
17 {
18   TRPS_INSTANCE *trps=talloc_get_type_abort(object, TRPS_INSTANCE);
19   if (trps->rtable!=NULL)
20     trp_rtable_free(trps->rtable);
21   return 0;
22 }
23
24 TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
25 {
26   TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
27   if (trps!=NULL)  {
28     trps->hostname=NULL;
29     trps->port=0;
30     trps->cookie=NULL;
31     trps->conn=NULL;
32     trps->trpc=NULL;
33     trps->update_interval=(struct timeval){0,0};
34     trps->sweep_interval=(struct timeval){0,0};
35
36     trps->mq=tr_mq_new(trps);
37     if (trps->mq==NULL) {
38       /* failed to allocate mq */
39       talloc_free(trps);
40       return NULL;
41     }
42
43     trps->ptable=trp_ptable_new(trps);
44     if (trps->ptable==NULL) {
45       /* failed to allocate ptable */
46       talloc_free(trps);
47       return NULL;
48     }
49
50     trps->rtable=trp_rtable_new();
51     if (trps->rtable==NULL) {
52       /* failed to allocate rtable */
53       talloc_free(trps);
54       return NULL;
55     }
56
57     talloc_set_destructor((void *)trps, trps_destructor);
58   }
59   return trps;
60 }
61
62 void trps_free (TRPS_INSTANCE *trps)
63 {
64   if (trps!=NULL)
65     talloc_free(trps);
66 }
67
68 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
69 {
70   return tr_mq_pop(trps->mq);
71 }
72
73 void trps_mq_append(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
74 {
75   tr_mq_append(trps->mq, msg);
76 }
77
78 unsigned int trps_get_update_interval(TRPS_INSTANCE *trps)
79 {
80   return trps->update_interval.tv_sec;
81 }
82
83 void trps_set_update_interval(TRPS_INSTANCE *trps, unsigned int interval)
84 {
85   trps->update_interval.tv_sec=interval;
86   trps->update_interval.tv_usec=0;
87 }
88
89 unsigned int trps_get_sweep_interval(TRPS_INSTANCE *trps)
90 {
91   return trps->sweep_interval.tv_sec;
92 }
93
94 void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval)
95 {
96   trps->sweep_interval.tv_sec=interval;
97   trps->sweep_interval.tv_usec=0;
98 }
99
100 static TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TR_NAME *peer_gssname)
101 {
102   TRPC_INSTANCE *cur=NULL;
103   for (cur=trps->trpc; cur!=NULL; cur=trpc_get_next(cur)) {
104     if (0==tr_name_cmp(peer_gssname,
105                        trp_connection_get_gssname(trpc_get_conn(cur)))) {
106       break;
107     }
108   }
109   return cur;
110 }
111
112 void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
113 {
114   if (trps->conn==NULL)
115     trps->conn=new;
116   else
117     trp_connection_append(trps->conn, new);
118
119   talloc_steal(trps, new);
120 }
121
122 /* ok to call more than once; guarantees connection no longer in the list.
123  * Caller is responsible for freeing the removed element afterwards.  */
124 void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
125 {
126   trps->conn=trp_connection_remove(trps->conn, remove);
127 }
128
129 void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
130 {
131   if (trps->trpc==NULL)
132     trps->trpc=trpc;
133   else
134     trpc_append(trps->trpc, trpc);
135
136   talloc_steal(trps, trpc);
137 }
138
139 /* ok to call more than once; guarantees trpc no longer in the list.
140  * Caller is responsible for freeing the removed element afterwards.  */
141 void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove)
142 {
143   trps->trpc=trpc_remove(trps->trpc, remove);
144 }
145
146 TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TR_NAME *peer_gssname, const char *msg)
147 {
148   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
149   TR_MQ_MSG *mq_msg=NULL;
150   char *msg_dup=NULL;
151   TRP_RC rc=TRP_ERROR;
152   TRPC_INSTANCE *trpc=NULL;
153
154   /* get the connection for this peer */
155   trpc=trps_find_trpc(trps, peer_gssname);
156   if ((trpc==NULL) || (trpc_get_status(trps->trpc)!=TRP_CONNECTION_UP)) {
157     /* We could just let these sit on the queue in the hopes that a connection
158      * is eventually established. However, we'd then have to ensure the queue
159      * didn't keep growing, etc. */
160     tr_warning("trps_send_msg: skipping message queued while TRPC connection not up.");
161   } else {
162     mq_msg=tr_mq_msg_new(tmp_ctx, "trpc_send");
163     msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
164     tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
165     trpc_mq_append(trpc, mq_msg);
166     rc=TRP_SUCCESS;
167   }
168   talloc_free(tmp_ctx);
169   return rc;
170 }
171
172 static int trps_listen (TRPS_INSTANCE *trps, int port) 
173 {
174   int rc = 0;
175   int conn = -1;
176   int optval = 1;
177
178   union {
179     struct sockaddr_storage storage;
180     struct sockaddr_in in4;
181   } addr;
182
183   struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
184
185   saddr->sin_port = htons (port);
186   saddr->sin_family = AF_INET;
187   saddr->sin_addr.s_addr = INADDR_ANY;
188
189   if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
190     return conn;
191
192   setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
193
194   if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
195     return rc;
196
197   if (0 > (rc = listen(conn, 512)))
198     return rc;
199
200   tr_debug("trps_listen: TRP Server listening on port %d", port);
201   return conn;
202 }
203
204 #if 0 /* remove this if I forget to do so */
205 /* returns EACCES if authorization is denied */
206 int trps_auth_cb(gss_name_t clientName, gss_buffer_t displayName, void *data)
207 {
208   TRPS_INSTANCE *trps = (TRPS_INSTANCE *)data;
209   int result=0;
210
211   if (0!=trps->auth_handler(clientName, displayName, trps->cookie)) {
212     tr_debug("trps_auth_cb: client '%.*s' denied authorization.", displayName->length, displayName->value);
213     result=EACCES; /* denied */
214   }
215
216   return result;
217 }
218 #endif 
219
220 /* get the currently selected route if available */
221 TRP_RENTRY *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
222 {
223   return trp_rtable_get_entry(trps->rtable, comm, realm, peer);
224 }
225
226 TRP_RENTRY *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
227 {
228   return trp_rtable_get_selected_entry(trps->rtable, comm, realm);
229 }
230
231 /* copy the result if you want to keep it */
232 TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
233 {
234   TRP_RENTRY *route=trps_get_selected_route(trps, comm, realm);
235   if (route==NULL)
236     return NULL;
237
238   return trp_rentry_get_next_hop(route);
239 }
240
241
242 /* mark a route as retracted */
243 static void trps_retract_route(TRPS_INSTANCE *trps, TRP_RENTRY *entry)
244 {
245   trp_rentry_set_metric(entry, TRP_METRIC_INFINITY);
246 }
247
248 /* is this route retracted? */
249 static int trps_route_retracted(TRPS_INSTANCE *trps, TRP_RENTRY *entry)
250 {
251   return (trp_metric_is_infinite(trp_rentry_get_metric(entry)));
252 }
253
254 static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
255 {
256   int err=0;
257   char *buf=NULL;
258   size_t buflen = 0;
259   TR_NAME *peer=NULL;
260
261   tr_debug("trps_read_message: started");
262   if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
263                                        *(trp_connection_get_gssctx(conn)), 
264                                        &buf,
265                                        &buflen)) {
266     tr_debug("trps_read_message: error");
267     if (buf)
268       free(buf);
269     return TRP_ERROR;
270   }
271
272   tr_debug("trps_read_message(): Request Received, %u bytes.", (unsigned) buflen);
273   tr_debug("trps_read_message(): %.*s", buflen, buf);
274
275   *msg=tr_msg_decode(buf, buflen);
276   free(buf);
277   if (*msg==NULL)
278     return TRP_NOPARSE;
279
280   peer=trp_connection_get_peer(conn);
281   /* verify we received a message we support, otherwise drop it now */
282   switch (tr_msg_get_msg_type(*msg)) {
283   case TRP_UPDATE:
284     trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(peer));
285     break;
286
287   case TRP_REQUEST:
288     trp_req_set_peer(tr_msg_get_trp_req(*msg), tr_dup_name(peer));
289     break;
290
291   default:
292     tr_debug("trps_read_message: received unsupported message from %.*s", peer->len, peer->buf);
293     tr_msg_free_decoded(*msg);
294     *msg=NULL;
295     return TRP_UNSUPPORTED;
296   }
297   
298   return TRP_SUCCESS;
299 }
300
301 int trps_get_listener(TRPS_INSTANCE *trps,
302                       TRPS_MSG_FUNC msg_handler,
303                       TRP_AUTH_FUNC auth_handler,
304                       const char *hostname,
305                       unsigned int port,
306                       void *cookie)
307 {
308   int listen = -1;
309
310   if (0 > (listen = trps_listen(trps, port))) {
311     char errbuf[256];
312     if (0 == strerror_r(errno, errbuf, 256)) {
313       tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf);
314     } else {
315       tr_debug("trps_get_listener: Unknown error openining port %d.", port);
316     }
317   } 
318
319   if (listen > 0) {
320     /* opening port succeeded */
321     tr_debug("trps_get_listener: Opened port %d.", port);
322     
323     /* make this socket non-blocking */
324     if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
325       tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
326       close(listen);
327       listen=-1;
328     }
329   }
330
331   if (listen > 0) {
332     /* store the caller's request handler & cookie */
333     trps->msg_handler = msg_handler;
334     trps->auth_handler = auth_handler;
335     trps->hostname = talloc_strdup(trps, hostname);
336     trps->port = port;
337     trps->cookie = cookie;
338   }
339
340   return listen;
341 }
342
343 void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
344 {
345   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
346   TR_MSG *msg=NULL;
347   TRP_RC rc=TRP_ERROR;
348
349   /* try to establish a GSS context */
350   if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
351     tr_notice("tr_trps_conn_thread: failed to authorize connection");
352     pthread_exit(NULL);
353   }
354   tr_notice("trps_handle_connection: authorized connection");
355   
356   /* loop as long as the connection exists */
357   while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
358     rc=trps_read_message(trps, conn, &msg);
359     switch(rc) {
360     case TRP_SUCCESS:
361       trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
362       break;
363
364     case TRP_ERROR:
365       trp_connection_close(conn);
366       break;
367
368     default:
369       tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
370     }
371   }
372
373   tr_debug("trps_handle_connection: connection closed.");
374   talloc_free(tmp_ctx);
375 }
376
377 static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
378 {
379   if (trp_upd_get_inforec(upd)==NULL) {
380     tr_notice("trps_validate_update: received TRP update with no info records.");
381     return TRP_ERROR;
382   }
383
384   if (trp_upd_get_peer(upd)==NULL) {
385     tr_notice("trps_validate_update: received TRP update without origin peer information.");
386     return TRP_ERROR;
387   }
388   
389   return TRP_SUCCESS;
390 }
391
392 /* ensure that the update could be accepted if feasible */
393 static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
394 {
395   switch(trp_inforec_get_type(rec)) {
396   case TRP_INFOREC_TYPE_ROUTE:
397     if ((trp_inforec_get_comm(rec)==NULL)
398        || (trp_inforec_get_realm(rec)==NULL)
399        || (trp_inforec_get_trust_router(rec)==NULL)
400        || (trp_inforec_get_next_hop(rec)==NULL)) {
401       tr_debug("trps_validate_inforec: missing record info.");
402       return TRP_ERROR;
403     }
404
405     /* check for valid metric */
406     if (trp_metric_is_valid(trp_inforec_get_metric(rec))) {
407       tr_debug("trps_validate_inforec: invalid metric.");
408       return TRP_ERROR;
409     }
410
411     /* check for valid interval */
412     if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
413       tr_debug("trps_validate_inforec: invalid interval.");
414       return TRP_ERROR;
415     }
416     break;
417
418   default:
419     tr_notice("trps_validate_inforec: unsupported record type.");
420     return TRP_UNSUPPORTED;
421   }
422
423   return TRP_SUCCESS;
424 }
425
426 /* link cost to a peer */
427 static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
428 {
429   return 1;
430 }
431
432 static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
433 {
434   TRP_RENTRY *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
435   if (entry==NULL)
436     return TRP_METRIC_INFINITY;
437   return trp_rentry_get_metric(entry) + trps_cost(trps, peer);
438 }
439
440 static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
441 {
442   unsigned int rec_metric=trp_inforec_get_metric(rec);
443   unsigned int new_metric=0;
444   unsigned int current_metric=0;
445   TR_NAME *next_hop=NULL;
446
447   /* we check these in the validation stage, but just in case... */
448   if (trp_metric_is_invalid(rec_metric))
449     return 0;
450
451   /* retractions (aka infinite metrics) are always feasible */
452   if (trp_metric_is_infinite(rec_metric))
453     return 1;
454
455   /* updates from our current next hop are always feasible*/
456   next_hop=trps_get_next_hop(trps,
457                              trp_inforec_get_comm(rec),
458                              trp_inforec_get_realm(rec));;
459   if ((next_hop!=NULL)
460      && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
461     return 1;
462   }
463     
464
465   /* compare the existing metric we advertise to what we would advertise
466    * if we accept this update */
467   current_metric=trps_advertised_metric(trps,
468                                         trp_inforec_get_comm(rec),
469                                         trp_inforec_get_realm(rec),
470                                         trp_inforec_get_next_hop(rec));
471   new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
472   if (new_metric <= current_metric)
473     return 1;
474   else
475     return 0;
476 }
477
478 /* uses memory pointed to by *ts, also returns that value. On error, its contents are {0,0} */
479 static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts)
480 {
481   const unsigned int small_factor=3; /* how many intervals we wait before expiring */
482   if (0!=clock_gettime(CLOCK_REALTIME, ts)) {
483     tr_err("trps_compute_expiry: could not read realtime clock.");
484     ts->tv_sec=0;
485     ts->tv_nsec=0;
486   }
487   ts->tv_sec += small_factor*interval;
488   return ts;
489 }
490
491 static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
492 {
493   TRP_RENTRY *entry=NULL;
494
495   entry=trp_rtable_get_entry(trps->rtable,
496                              trp_inforec_get_comm(rec),
497                              trp_inforec_get_realm(rec),
498                              trp_inforec_get_next_hop(rec));
499   if (entry==NULL) {
500     entry=trp_rentry_new(NULL);
501     if (entry==NULL) {
502       tr_err("trps_accept_update: unable to allocate new entry.");
503       return TRP_NOMEM;
504     }
505
506     trp_rentry_set_apc(entry, tr_dup_name(trp_inforec_get_comm(rec)));
507     trp_rentry_set_realm(entry, tr_dup_name(trp_inforec_get_realm(rec)));
508     trp_rentry_set_peer(entry, tr_dup_name(trp_inforec_get_next_hop(rec)));
509     trp_rentry_set_trust_router(entry, tr_dup_name(trp_inforec_get_trust_router(rec)));
510     trp_rentry_set_next_hop(entry, tr_dup_name(trp_inforec_get_next_hop(rec)));
511     if ((trp_rentry_get_apc(entry)==NULL)
512        ||(trp_rentry_get_realm(entry)==NULL)
513        ||(trp_rentry_get_peer(entry)==NULL)
514        ||(trp_rentry_get_trust_router(entry)==NULL)
515        ||(trp_rentry_get_next_hop(entry)==NULL)) {
516       /* at least one field could not be allocated */
517       tr_err("trps_accept_update: unable to allocate all fields for entry.");
518       trp_rentry_free(entry);
519       return TRP_NOMEM;
520     }
521     trp_rtable_add(trps->rtable, entry);
522   }
523
524   /* We now have an entry in the table, whether it's new or not. Update metric and expiry, unless
525    * the metric is infinity. An infinite metric can only occur here if we just retracted an existing
526    * route (we never accept retractions as new routes), so there is no risk of leaving the expiry
527    * time unset on a new route entry. */
528   tr_debug("trps_accept_update: accepting route update.");
529   trp_rentry_set_metric(entry, trp_inforec_get_metric(rec));
530   trp_rentry_set_interval(entry, trp_inforec_get_interval(rec));
531   if (!trps_route_retracted(trps, entry)) {
532     tr_debug("trps_accept_update: route not retracted, setting expiry timer.");
533     trp_rentry_set_expiry(entry, trps_compute_expiry(trps,
534                                                      trp_rentry_get_interval(entry),
535                                                      trp_rentry_get_expiry(entry)));
536   }
537   return TRP_SUCCESS;
538 }
539
540 /* TODO: handle community updates */
541 static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
542 {
543   unsigned int feas=0;
544   TRP_INFOREC *rec=NULL;
545   TRP_RENTRY *route=NULL;
546
547   if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
548     tr_notice("trps_handle_update: received invalid TRP update.");
549     return TRP_ERROR;
550   }
551
552   rec=trp_upd_get_inforec(upd);
553   for (;rec!=NULL; rec=trp_inforec_get_next(rec)) {
554     /* validate/sanity check the record update */
555     if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
556       tr_notice("trps_handle_update: invalid record in TRP update.");
557       continue;
558     }
559
560     /* determine feasibility */
561     feas=trps_check_feasibility(trps, rec);
562     tr_debug("trps_handle_update: record feasibility=%d", feas);
563
564     /* do we have an existing route? */
565     route=trps_get_route(trps, trp_inforec_get_comm(rec), trp_inforec_get_realm(rec), trp_inforec_get_next_hop(rec));
566     if (route!=NULL) {
567       /* there was a route table entry already */
568       tr_debug("trps_handle_updates: route entry already exists.");
569       if (feas) {
570         /* Update is feasible. Accept it. */
571         trps_accept_update(trps, rec);
572       } else {
573         /* Update is infeasible. Ignore it unless the trust router has changed. */
574         if (0!=tr_name_cmp(trp_rentry_get_trust_router(route),
575                            trp_inforec_get_trust_router(rec))) {
576           /* the trust router associated with the route has changed, treat update as a retraction */
577           trps_retract_route(trps, route);
578         }
579       }
580     } else {
581       /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
582       tr_debug("trps_handle_update: no route entry exists yet.");
583       if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
584         trps_accept_update(trps, rec);
585     }
586   }
587   return TRP_SUCCESS;
588 }
589
590 /* choose the best route to comm/realm, optionally excluding routes to a particular peer */
591 static TRP_RENTRY *trps_find_best_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *exclude_peer)
592 {
593   TRP_RENTRY **entry=NULL;
594   TRP_RENTRY *best=NULL;
595   size_t n_entry=0;
596   unsigned int kk=0;
597   unsigned int kk_min=0;
598   unsigned int min_metric=TRP_METRIC_INFINITY;
599
600   entry=trp_rtable_get_realm_entries(trps->rtable, comm, realm, &n_entry);
601   for (kk=0; kk<n_entry; kk++) {
602     if (trp_rentry_get_metric(entry[kk]) < min_metric) {
603       if ((exclude_peer==NULL) || (0!=tr_name_cmp(trp_rentry_get_peer(entry[kk]),
604                                                   exclude_peer))) {
605         kk_min=kk;
606         min_metric=trp_rentry_get_metric(entry[kk]);
607       }
608     }
609   }
610   if (trp_metric_is_finite(min_metric));
611     best=entry[kk_min];
612   
613   talloc_free(entry);
614   return best;
615 }
616
617 /* TODO: think this through more carefully. At least ought to add hysteresis
618  * to avoid flapping between routers or routes. */
619 static TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
620 {
621   size_t n_apc=0, ii=0;
622   TR_NAME **apc=trp_rtable_get_apcs(trps->rtable, &n_apc);
623   size_t n_realm=0, jj=0;
624   TR_NAME **realm=NULL;
625   TRP_RENTRY *best_route=NULL, *cur_route=NULL;
626   unsigned int best_metric=0, cur_metric=0;
627
628   for (ii=0; ii<n_apc; ii++) {
629     realm=trp_rtable_get_apc_realms(trps->rtable, apc[ii], &n_realm);
630     for (jj=0; jj<n_realm; jj++) {
631       best_route=trps_find_best_route(trps, apc[ii], realm[jj], NULL);
632       if (best_route==NULL)
633         best_metric=TRP_METRIC_INFINITY;
634       else
635         best_metric=trp_rentry_get_metric(best_route);
636
637       cur_route=trps_get_selected_route(trps, apc[ii], realm[jj]);
638       if (cur_route!=NULL) {
639         cur_metric=trp_rentry_get_metric(cur_route);
640         if ((best_metric < cur_metric) && (trp_metric_is_finite(best_metric))) {
641           trp_rentry_set_selected(cur_route, 0);
642           trp_rentry_set_selected(best_route, 1);
643         } else if (!trp_metric_is_finite(cur_metric)) /* rejects infinite or invalid metrics */
644           trp_rentry_set_selected(cur_route, 0);
645       } else if (trp_metric_is_finite(best_metric))
646         trp_rentry_set_selected(best_route, 1);
647     }
648     if (realm!=NULL)
649       talloc_free(realm);
650     realm=NULL; n_realm=0;
651   }
652   if (apc!=NULL)
653     talloc_free(apc);
654   apc=NULL; n_apc=0;
655
656   return TRP_SUCCESS;
657 }
658
659 TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
660 {
661   TRP_RC rc=TRP_ERROR;
662
663   switch (tr_msg_get_msg_type(tr_msg)) {
664   case TRP_UPDATE:
665     rc=trps_handle_update(trps, tr_msg_get_trp_upd(tr_msg));
666     if (rc==TRP_SUCCESS) {
667       rc=trps_update_active_routes(trps);
668     }
669     return rc;
670
671   case TRP_REQUEST:
672     return TRP_UNSUPPORTED;
673
674   default:
675     /* unknown error or one we don't care about (e.g., TID messages) */
676     return TRP_ERROR;
677   }
678 }
679
680 /* true if curtime >= expiry */
681 static int trps_expired(struct timespec *expiry, struct timespec *curtime)
682 {
683   return ((curtime->tv_sec > expiry->tv_sec)
684          || ((curtime->tv_sec == expiry->tv_sec)
685             &&(curtime->tv_nsec > expiry->tv_nsec)));
686 }
687
688 /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
689  * If its metric is finite, the metric is set to infinite and the route's expiration time is updated. */
690 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
691 {
692   struct timespec sweep_time={0,0};
693   TRP_RENTRY **entry=NULL;
694   size_t n_entry=0;
695   size_t ii=0;
696
697   /* use a single time for the entire sweep */
698   if (0!=clock_gettime(CLOCK_REALTIME, &sweep_time)) {
699     tr_err("trps_sweep_routes: could not read realtime clock.");
700     sweep_time.tv_sec=0;
701     sweep_time.tv_nsec=0;
702     return TRP_ERROR;
703   }
704
705   entry=trp_rtable_get_entries(trps->rtable, &n_entry); /* must talloc_free *entry */
706
707   /* loop over the entries */
708   for (ii=0; ii<n_entry; ii++) {
709     if (trps_expired(trp_rentry_get_expiry(entry[ii]), &sweep_time)) {
710       tr_debug("trps_sweep_routes: route expired.");
711       if (!trp_metric_is_finite(trp_rentry_get_metric(entry[ii]))) {
712         /* flush route */
713         tr_debug("trps_sweep_routes: metric was infinity, flushing route.");
714         trp_rtable_remove(trps->rtable, entry[ii]); /* entry[ii] is no longer valid */
715         entry[ii]=NULL;
716       } else {
717         /* set metric to infinity and reset timer */
718         tr_debug("trps_sweep_routes: setting metric to infinity and resetting expiry.");
719         trp_rentry_set_metric(entry[ii], TRP_METRIC_INFINITY);
720         trp_rentry_set_expiry(entry[ii], trps_compute_expiry(trps,
721                                                              trp_rentry_get_interval(entry[ii]),
722                                                              trp_rentry_get_expiry(entry[ii])));
723       }
724     }
725   }
726
727   talloc_free(entry);
728   return TRP_SUCCESS;
729 }
730
731 /* select the correct route to comm/realm to be announced to peer */
732 static TRP_RENTRY *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname)
733 {
734   TRP_RENTRY *route;
735
736   /* Take the currently selected route unless it is through the peer we're sending the update to.
737    * I.e., enforce the split horizon rule. */
738   route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
739   if (0==tr_name_cmp(peer_gssname, trp_rentry_get_peer(route))) {
740     /* the selected entry goes through the peer we're reporting to, choose an alternate */
741     route=trps_find_best_route(trps, comm, realm, peer_gssname);
742     if (!trp_metric_is_finite(trp_rentry_get_metric(route)))
743       route=NULL; /* don't advertise a retracted route */
744   }
745   return route;
746 }
747
748 /* returns an array of pointers to updates (*not* an array of updates). Returns number of entries
749  * via n_update parameter. (The allocated space will generally be larger than required, see note in
750  * the code.) */
751 static TRP_RENTRY **trps_select_updates_for_peer(TALLOC_CTX *memctx, TRPS_INSTANCE *trps, TR_NAME *peer_gssname, size_t *n_update)
752 {
753   size_t n_apc=0;
754   TR_NAME **apc=trp_rtable_get_apcs(trps->rtable, &n_apc);
755   TR_NAME **realm=NULL;
756   size_t n_realm=0;
757   size_t ii=0, jj=0;
758   TRP_RENTRY *best=NULL;
759   TRP_RENTRY **result=NULL;
760   size_t n_used=0;
761
762   /* Need to allocate space for the results. For simplicity, we just allocate a block
763    * with space for every route table entry to be returned. This is guaranteed to be large
764    * enough. If the routing table gets very large, this may be wasteful, but that seems
765    * unlikely to be significant in the near future. */
766   result=talloc_array(memctx, TRP_RENTRY *, trp_rtable_size(trps->rtable));
767   if (result==NULL) {
768     talloc_free(apc);
769     return NULL;
770   }
771   
772   for (ii=0; ii<n_apc; ii++) {
773     realm=trp_rtable_get_apc_realms(trps->rtable, apc[ii], &n_realm);
774     for (jj=0; jj<n_realm; jj++) {
775       best=trps_select_realm_update(trps, apc[ii], realm[jj], peer_gssname);
776       if (best!=NULL)
777         result[n_used++]=best;
778     }
779     if (realm!=NULL)
780       talloc_free(realm);
781     realm=NULL;
782     n_realm=0;
783   }
784   if (apc!=NULL)
785     talloc_free(apc);
786
787   *n_update=n_used;
788   return result;
789 }
790
791 /* convert an rentry into a new trp update info record */
792 static TRP_INFOREC *trps_rentry_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_RENTRY *entry)
793 {
794   TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE);
795   unsigned int linkcost=0;
796
797   if (rec!=NULL) {
798     linkcost=trp_peer_get_linkcost(trps_get_peer(trps,
799                                                  trp_rentry_get_next_hop(entry)));
800
801     /* Note that we leave the next hop empty since the recipient fills that in.
802      * This is where we add the link cost (currently always 1) to the next peer. */
803     if ((trp_inforec_set_comm(rec, trp_rentry_get_apc(entry)) != TRP_SUCCESS)
804        ||(trp_inforec_set_realm(rec, trp_rentry_get_realm(entry)) != TRP_SUCCESS)
805        ||(trp_inforec_set_trust_router(rec, trp_rentry_get_trust_router(entry)) != TRP_SUCCESS)
806        ||(trp_inforec_set_metric(rec, trp_rentry_get_metric(entry)+linkcost) != TRP_SUCCESS)
807        ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) {
808       tr_err("trps_rentry_to_inforec: error creating route update.");
809       talloc_free(rec);
810       rec=NULL;
811     }
812   }
813   return rec;
814 }
815
816 TRP_RC trps_scheduled_update(TRPS_INSTANCE *trps)
817 {
818   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
819   TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
820   TRP_PEER *peer=NULL;
821   TR_MSG msg; /* not a pointer! */
822   TRP_UPD *upd=NULL;
823   TRP_RENTRY **update_list=NULL;
824   TRP_INFOREC *rec=NULL;
825   size_t n_updates=0, ii=0;
826   char *encoded=NULL;
827   TRP_RC rc=TRP_ERROR;
828
829   if (iter==NULL) {
830     tr_err("trps_scheduled_update: failed to allocate peer table iterator.");
831     talloc_free(tmp_ctx);
832     return TRP_NOMEM;
833   }
834
835   for (peer=trp_ptable_iter_first(iter, trps->ptable);
836        peer!=NULL;
837        peer=trp_ptable_iter_next(iter))
838   {
839     tr_debug("trps_scheduled_update: preparing scheduled route update for %.*%s",
840              trp_peer_get_gssname(peer)->len, trp_peer_get_gssname(peer)->buf);
841     upd=trp_upd_new(tmp_ctx);
842     /* do not fill in peer, recipient does that */
843     update_list=trps_select_updates_for_peer(tmp_ctx, trps, trp_peer_get_gssname(peer), &n_updates);
844     for (ii=0; ii<n_updates; ii++) {
845       rec=trps_rentry_to_inforec(tmp_ctx, trps, update_list[ii]);
846       if (rec==NULL) {
847         tr_err("trps_scheduled_update: could not create all update records.");
848         rc=TRP_ERROR;
849         goto cleanup;
850       }
851       trp_upd_add_inforec(upd, rec);
852     }
853     talloc_free(update_list);
854     update_list=NULL;
855
856     /* now encode the update message */
857     tr_msg_set_trp_upd(&msg, upd);
858     encoded=tr_msg_encode(&msg);
859     if (encoded==NULL) {
860       tr_err("trps_scheduled_update: error encoding update.");
861       rc=TRP_ERROR;
862       goto cleanup;
863     }
864     tr_debug("trps_scheduled_update: adding message to queue.");
865     if (trps_send_msg(trps, trp_peer_get_gssname(peer), encoded) != TRP_SUCCESS) {
866       tr_err("trps_scheduled_update: error queueing update.");
867       rc=TRP_ERROR;
868       tr_msg_free_encoded(encoded);
869       encoded=NULL;
870       goto cleanup;
871     }
872     tr_debug("trps_scheduled_update: message queued successfully.");
873     tr_msg_free_encoded(encoded);
874     encoded=NULL;
875     trp_upd_free(upd);
876     upd=NULL;
877   }
878   
879 cleanup:
880   trp_ptable_iter_free(iter);
881   talloc_free(tmp_ctx);
882   return rc;
883 }        
884
885 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
886 {
887   return trp_ptable_add(trps->ptable, peer);
888 }
889
890 TRP_PEER *trps_get_peer(TRPS_INSTANCE *trps, TR_NAME *gssname)
891 {
892   return trp_ptable_find(trps->ptable, gssname);
893 }