Merge branch 'master' into jennifer/trp-devel
[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=NULL;
51     if (trps_init_rtable(trps) != TRP_SUCCESS) {
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 /* create a new route table, first discarding an old one if necessary */
63 TRP_RC trps_init_rtable(TRPS_INSTANCE *trps)
64 {
65   if (trps->rtable != NULL) {
66     trp_rtable_free(trps->rtable);
67     trps->rtable=NULL;
68   }
69
70   trps->rtable=trp_rtable_new();
71   if (trps->rtable==NULL) {
72     return TRP_NOMEM;
73   }
74   return TRP_SUCCESS;
75 }
76
77 void trps_clear_rtable(TRPS_INSTANCE *trps)
78 {
79   trp_rtable_clear(trps->rtable);
80 }
81
82 void trps_free (TRPS_INSTANCE *trps)
83 {
84   if (trps!=NULL)
85     talloc_free(trps);
86 }
87
88 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
89 {
90   return tr_mq_pop(trps->mq);
91 }
92
93 void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
94 {
95   tr_mq_add(trps->mq, msg);
96 }
97
98 unsigned int trps_get_connect_interval(TRPS_INSTANCE *trps)
99 {
100   return trps->connect_interval.tv_sec;
101 }
102
103 void trps_set_connect_interval(TRPS_INSTANCE *trps, unsigned int interval)
104 {
105   trps->connect_interval.tv_sec=interval;
106   trps->connect_interval.tv_usec=0;
107 }
108
109 unsigned int trps_get_update_interval(TRPS_INSTANCE *trps)
110 {
111   return trps->update_interval.tv_sec;
112 }
113
114 void trps_set_update_interval(TRPS_INSTANCE *trps, unsigned int interval)
115 {
116   trps->update_interval.tv_sec=interval;
117   trps->update_interval.tv_usec=0;
118 }
119
120 unsigned int trps_get_sweep_interval(TRPS_INSTANCE *trps)
121 {
122   return trps->sweep_interval.tv_sec;
123 }
124
125 void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval)
126 {
127   trps->sweep_interval.tv_sec=interval;
128   trps->sweep_interval.tv_usec=0;
129 }
130
131 TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TRP_PEER *peer)
132 {
133   TRPC_INSTANCE *cur=NULL;
134   TR_NAME *name=NULL;
135   TR_NAME *peer_gssname=trp_peer_get_gssname(peer);
136
137   for (cur=trps->trpc; cur!=NULL; cur=trpc_get_next(cur)) {
138     name=trpc_get_gssname(cur);
139     if ((name!=NULL) && (0==tr_name_cmp(peer_gssname, name))) {
140       break;
141     }
142   }
143   return cur;
144 }
145
146 void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
147 {
148   if (trps->conn==NULL)
149     trps->conn=new;
150   else
151     trp_connection_append(trps->conn, new);
152
153   talloc_steal(trps, new);
154 }
155
156 /* ok to call more than once; guarantees connection no longer in the list.
157  * Caller is responsible for freeing the removed element afterwards.  */
158 void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
159 {
160   trps->conn=trp_connection_remove(trps->conn, remove);
161 }
162
163 void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
164 {
165   if (trps->trpc==NULL)
166     trps->trpc=trpc;
167   else
168     trpc_append(trps->trpc, trpc);
169
170   talloc_steal(trps, trpc);
171 }
172
173 /* ok to call more than once; guarantees trpc no longer in the list.
174  * Caller is responsible for freeing the removed element afterwards.  */
175 void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove)
176 {
177   trps->trpc=trpc_remove(trps->trpc, remove);
178 }
179
180 TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg)
181 {
182   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
183   TR_MQ_MSG *mq_msg=NULL;
184   char *msg_dup=NULL;
185   TRP_RC rc=TRP_ERROR;
186   TRPC_INSTANCE *trpc=NULL;
187
188   /* get the connection for this peer */
189   trpc=trps_find_trpc(trps, peer);
190   /* instead, let's let that happen and then clear the queue when an attempt to
191    * connect fails */
192   if (trpc==NULL) {
193     tr_warning("trps_send_msg: skipping message queued for missing TRP client entry.");
194   } else {
195     mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_SEND, TR_MQ_PRIO_NORMAL);
196     msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
197     tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
198     trpc_mq_add(trpc, mq_msg);
199     rc=TRP_SUCCESS;
200   }
201   talloc_free(tmp_ctx);
202   return rc;
203 }
204
205 static int trps_listen (TRPS_INSTANCE *trps, int port) 
206 {
207   int rc = 0;
208   int conn = -1;
209   int optval = 1;
210
211   union {
212     struct sockaddr_storage storage;
213     struct sockaddr_in in4;
214   } addr;
215
216   struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
217
218   saddr->sin_port = htons (port);
219   saddr->sin_family = AF_INET;
220   saddr->sin_addr.s_addr = INADDR_ANY;
221
222   if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
223     return conn;
224
225   setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
226
227   if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
228     return rc;
229
230   if (0 > (rc = listen(conn, 512)))
231     return rc;
232
233   tr_debug("trps_listen: TRP Server listening on port %d", port);
234   return conn;
235 }
236
237 /* get the currently selected route if available */
238 TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
239 {
240   return trp_rtable_get_entry(trps->rtable, comm, realm, peer);
241 }
242
243 TRP_ROUTE *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
244 {
245   return trp_rtable_get_selected_entry(trps->rtable, comm, realm);
246 }
247
248 /* copy the result if you want to keep it */
249 TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
250 {
251   TRP_ROUTE *route=trps_get_selected_route(trps, comm, realm);
252   if (route==NULL)
253     return NULL;
254
255   return trp_route_get_next_hop(route);
256 }
257
258
259 /* mark a route as retracted */
260 static void trps_retract_route(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
261 {
262   trp_route_set_metric(entry, TRP_METRIC_INFINITY);
263   trp_route_set_triggered(entry, 1);
264 }
265
266 /* is this route retracted? */
267 static int trps_route_retracted(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
268 {
269   return (trp_metric_is_infinite(trp_route_get_metric(entry)));
270 }
271
272 static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
273 {
274   int err=0;
275   char *buf=NULL;
276   size_t buflen = 0;
277   TR_NAME *peer=NULL;
278
279   tr_debug("trps_read_message: started");
280   if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
281                                        *(trp_connection_get_gssctx(conn)), 
282                                        &buf,
283                                        &buflen)) {
284     tr_debug("trps_read_message: error");
285     if (buf)
286       free(buf);
287     return TRP_ERROR;
288   }
289
290   tr_debug("trps_read_message(): message received, %u bytes.", (unsigned) buflen);
291   tr_debug("trps_read_message(): %.*s", buflen, buf);
292
293   *msg=tr_msg_decode(buf, buflen);
294   free(buf);
295   if (*msg==NULL)
296     return TRP_NOPARSE;
297
298   peer=trp_connection_get_peer(conn);
299   /* verify we received a message we support, otherwise drop it now */
300   switch (tr_msg_get_msg_type(*msg)) {
301   case TRP_UPDATE:
302     trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(peer));
303     break;
304
305   case TRP_REQUEST:
306     trp_req_set_peer(tr_msg_get_trp_req(*msg), tr_dup_name(peer));
307     break;
308
309   default:
310     tr_debug("trps_read_message: received unsupported message from %.*s", peer->len, peer->buf);
311     tr_msg_free_decoded(*msg);
312     *msg=NULL;
313     return TRP_UNSUPPORTED;
314   }
315   
316   return TRP_SUCCESS;
317 }
318
319 int trps_get_listener(TRPS_INSTANCE *trps,
320                       TRPS_MSG_FUNC msg_handler,
321                       TRP_AUTH_FUNC auth_handler,
322                       const char *hostname,
323                       unsigned int port,
324                       void *cookie)
325 {
326   int listen = -1;
327
328   if (0 > (listen = trps_listen(trps, port))) {
329     char errbuf[256];
330     if (0 == strerror_r(errno, errbuf, 256)) {
331       tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf);
332     } else {
333       tr_debug("trps_get_listener: Unknown error openining port %d.", port);
334     }
335   } 
336
337   if (listen > 0) {
338     /* opening port succeeded */
339     tr_debug("trps_get_listener: Opened port %d.", port);
340     
341     /* make this socket non-blocking */
342     if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
343       tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
344       close(listen);
345       listen=-1;
346     }
347   }
348
349   if (listen > 0) {
350     /* store the caller's request handler & cookie */
351     trps->msg_handler = msg_handler;
352     trps->auth_handler = auth_handler;
353     trps->hostname = talloc_strdup(trps, hostname);
354     trps->port = port;
355     trps->cookie = cookie;
356   }
357
358   return listen;
359 }
360
361 TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
362 {
363   /* try to establish a GSS context */
364   if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
365     tr_notice("trps_authorize_connection: failed to authorize connection");
366     trp_connection_close(conn);
367     return TRP_ERROR;
368   }
369   tr_notice("trps_authorize_connection: authorized connection");
370   return TRP_SUCCESS;
371 }
372
373 void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
374 {
375   TR_MSG *msg=NULL;
376   TRP_RC rc=TRP_ERROR;
377
378   /* loop as long as the connection exists */
379   while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
380     rc=trps_read_message(trps, conn, &msg);
381     switch(rc) {
382     case TRP_SUCCESS:
383       trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
384       break;
385
386     case TRP_ERROR:
387       trp_connection_close(conn);
388       break;
389
390     default:
391       tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
392     }
393   }
394
395   tr_debug("trps_handle_connection: connection closed.");
396 }
397
398 static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
399 {
400   if (upd==NULL) {
401     tr_notice("trps_validate_update: null TRP update.");
402     return TRP_BADARG;
403   }
404
405   if (trp_upd_get_inforec(upd)==NULL) {
406     tr_notice("trps_validate_update: received TRP update with no info records.");
407     return TRP_ERROR;
408   }
409
410   if (trp_upd_get_peer(upd)==NULL) {
411     tr_notice("trps_validate_update: received TRP update without origin peer information.");
412     return TRP_ERROR;
413   }
414   
415   return TRP_SUCCESS;
416 }
417
418 /* ensure that the update could be accepted if feasible */
419 static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
420 {
421   switch(trp_inforec_get_type(rec)) {
422   case TRP_INFOREC_TYPE_ROUTE:
423     if ((trp_inforec_get_comm(rec)==NULL)
424        || (trp_inforec_get_realm(rec)==NULL)
425        || (trp_inforec_get_trust_router(rec)==NULL)
426        || (trp_inforec_get_next_hop(rec)==NULL)) {
427       tr_debug("trps_validate_inforec: missing record info.");
428       return TRP_ERROR;
429     }
430
431     /* check for valid metric */
432     if (trp_metric_is_invalid(trp_inforec_get_metric(rec))) {
433       tr_debug("trps_validate_inforec: invalid metric (%u).", trp_inforec_get_metric(rec));
434       return TRP_ERROR;
435     }
436
437     /* check for valid interval */
438     if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
439       tr_debug("trps_validate_inforec: invalid interval.");
440       return TRP_ERROR;
441     }
442     break;
443
444   default:
445     tr_notice("trps_validate_inforec: unsupported record type.");
446     return TRP_UNSUPPORTED;
447   }
448
449   return TRP_SUCCESS;
450 }
451
452 /* link cost to a peer */
453 static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
454 {
455   return 1;
456 }
457
458 static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
459 {
460   TRP_ROUTE *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
461   if (entry==NULL)
462     return TRP_METRIC_INFINITY;
463   return trp_route_get_metric(entry) + trps_cost(trps, peer);
464 }
465
466 static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
467 {
468   unsigned int rec_metric=trp_inforec_get_metric(rec);
469   unsigned int new_metric=0;
470   unsigned int current_metric=0;
471   TR_NAME *next_hop=NULL;
472
473   /* we check these in the validation stage, but just in case... */
474   if (trp_metric_is_invalid(rec_metric))
475     return 0;
476
477   /* retractions (aka infinite metrics) are always feasible */
478   if (trp_metric_is_infinite(rec_metric))
479     return 1;
480
481   /* updates from our current next hop are always feasible*/
482   next_hop=trps_get_next_hop(trps,
483                              trp_inforec_get_comm(rec),
484                              trp_inforec_get_realm(rec));;
485   if ((next_hop!=NULL)
486      && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
487     return 1;
488   }
489     
490
491   /* compare the existing metric we advertise to what we would advertise
492    * if we accept this update */
493   current_metric=trps_advertised_metric(trps,
494                                         trp_inforec_get_comm(rec),
495                                         trp_inforec_get_realm(rec),
496                                         trp_inforec_get_next_hop(rec));
497   new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
498   if (new_metric <= current_metric)
499     return 1;
500   else
501     return 0;
502 }
503
504 /* uses memory pointed to by *ts, also returns that value. On error, its contents are {0,0} */
505 static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts)
506 {
507   const unsigned int small_factor=3; /* how many intervals we wait before expiring */
508   if (0!=clock_gettime(CLOCK_REALTIME, ts)) {
509     tr_err("trps_compute_expiry: could not read realtime clock.");
510     ts->tv_sec=0;
511     ts->tv_nsec=0;
512   }
513   ts->tv_sec += small_factor*interval;
514   return ts;
515 }
516
517 static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
518 {
519   TRP_ROUTE *entry=NULL;
520
521   entry=trp_rtable_get_entry(trps->rtable,
522                              trp_inforec_get_comm(rec),
523                              trp_inforec_get_realm(rec),
524                              trp_inforec_get_next_hop(rec));
525   if (entry==NULL) {
526     entry=trp_route_new(NULL);
527     if (entry==NULL) {
528       tr_err("trps_accept_update: unable to allocate new entry.");
529       return TRP_NOMEM;
530     }
531
532     trp_route_set_comm(entry, trp_inforec_dup_comm(rec));
533     trp_route_set_realm(entry, trp_inforec_dup_realm(rec));
534     trp_route_set_peer(entry, trp_upd_dup_peer(upd));
535     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec));
536     trp_route_set_next_hop(entry, trp_inforec_dup_next_hop(rec));
537     if ((trp_route_get_comm(entry)==NULL)
538        ||(trp_route_get_realm(entry)==NULL)
539        ||(trp_route_get_peer(entry)==NULL)
540        ||(trp_route_get_trust_router(entry)==NULL)
541        ||(trp_route_get_next_hop(entry)==NULL)) {
542       /* at least one field could not be allocated */
543       tr_err("trps_accept_update: unable to allocate all fields for entry.");
544       trp_route_free(entry);
545       return TRP_NOMEM;
546     }
547     trp_rtable_add(trps->rtable, entry);
548   }
549
550   /* We now have an entry in the table, whether it's new or not. Update metric and expiry, unless
551    * the metric is infinity. An infinite metric can only occur here if we just retracted an existing
552    * route (we never accept retractions as new routes), so there is no risk of leaving the expiry
553    * time unset on a new route entry. */
554   tr_debug("trps_accept_update: accepting route update.");
555   trp_route_set_metric(entry, trp_inforec_get_metric(rec));
556   trp_route_set_interval(entry, trp_inforec_get_interval(rec));
557
558   /* check whether the trust router has changed */
559   if (0!=tr_name_cmp(trp_route_get_trust_router(entry),
560                      trp_inforec_get_trust_router(rec))) {
561     /* The name changed. Set this route as triggered. */
562     tr_debug("trps_accept_update: trust router for route changed.");
563     trp_route_set_triggered(entry, 1);
564     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec)); /* frees old name */
565   }
566   if (!trps_route_retracted(trps, entry)) {
567     tr_debug("trps_accept_update: route not retracted, setting expiry timer.");
568     trp_route_set_expiry(entry, trps_compute_expiry(trps,
569                                                      trp_route_get_interval(entry),
570                                                      trp_route_get_expiry(entry)));
571   }
572   return TRP_SUCCESS;
573 }
574
575 static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
576 {
577   unsigned int feas=0;
578   TRP_INFOREC *rec=NULL;
579   TRP_ROUTE *route=NULL;
580
581   if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
582     tr_notice("trps_handle_update: received invalid TRP update.");
583     return TRP_ERROR;
584   }
585
586   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
587     /* validate/sanity check the record update */
588     if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
589       tr_notice("trps_handle_update: invalid record in TRP update, discarding entire update.");
590       return TRP_ERROR;
591     }
592   }
593
594   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
595     /* determine feasibility */
596     feas=trps_check_feasibility(trps, rec);
597     tr_debug("trps_handle_update: record feasibility=%d", feas);
598
599     /* do we have an existing route? */
600     route=trps_get_route(trps,
601                          trp_inforec_get_comm(rec),
602                          trp_inforec_get_realm(rec),
603                          trp_upd_get_peer(upd));
604     if (route!=NULL) {
605       /* there was a route table entry already */
606       tr_debug("trps_handle_updates: route entry already exists.");
607       if (feas) {
608         /* Update is feasible. Accept it. */
609         trps_accept_update(trps, upd, rec);
610       } else {
611         /* Update is infeasible. Ignore it unless the trust router has changed. */
612         if (0!=tr_name_cmp(trp_route_get_trust_router(route),
613                            trp_inforec_get_trust_router(rec))) {
614           /* the trust router associated with the route has changed, treat update as a retraction */
615           trps_retract_route(trps, route);
616         }
617       }
618     } else {
619       /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
620       tr_debug("trps_handle_update: no route entry exists yet.");
621       if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
622         trps_accept_update(trps, upd, rec);
623     }
624   }
625   return TRP_SUCCESS;
626 }
627
628 static TRP_RC trps_validate_request(TRPS_INSTANCE *trps, TRP_REQ *req)
629 {
630   if (req==NULL) {
631     tr_notice("trps_validate_request: null TRP request.");
632     return TRP_BADARG;
633   }
634
635   if (trp_req_get_comm(req)==NULL) {
636     tr_notice("trps_validate_request: received TRP request with null community.");
637     return TRP_ERROR;
638   }
639   
640   if (trp_req_get_realm(req)==NULL) {
641     tr_notice("trps_validate_request: received TRP request with null realm.");
642     return TRP_ERROR;
643   }
644   
645   if (trp_req_get_peer(req)==NULL) {
646     tr_notice("trps_validate_request: received TRP request without origin peer information.");
647     return TRP_ERROR;
648   }
649   
650   return TRP_SUCCESS;
651 }
652
653 /* choose the best route to comm/realm, optionally excluding routes to a particular peer */
654 static TRP_ROUTE *trps_find_best_route(TRPS_INSTANCE *trps,
655                                         TR_NAME *comm,
656                                         TR_NAME *realm,
657                                         TR_NAME *exclude_peer)
658 {
659   TRP_ROUTE **entry=NULL;
660   TRP_ROUTE *best=NULL;
661   size_t n_entry=0;
662   unsigned int kk=0;
663   unsigned int kk_min=0;
664   unsigned int min_metric=TRP_METRIC_INFINITY;
665
666   entry=trp_rtable_get_realm_entries(trps->rtable, comm, realm, &n_entry);
667   for (kk=0; kk<n_entry; kk++) {
668     if (trp_route_get_metric(entry[kk]) < min_metric) {
669       if ((exclude_peer==NULL) || (0!=tr_name_cmp(trp_route_get_peer(entry[kk]),
670                                                   exclude_peer))) {
671         kk_min=kk;
672         min_metric=trp_route_get_metric(entry[kk]);
673       } 
674     }
675   }
676   if (trp_metric_is_finite(min_metric))
677     best=entry[kk_min];
678   
679   talloc_free(entry);
680   return best;
681 }
682
683 /* TODO: think this through more carefully. At least ought to add hysteresis
684  * to avoid flapping between routers or routes. */
685 TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
686 {
687   size_t n_comm=0, ii=0;
688   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
689   size_t n_realm=0, jj=0;
690   TR_NAME **realm=NULL;
691   TRP_ROUTE *best_route=NULL, *cur_route=NULL;
692   unsigned int best_metric=0, cur_metric=0;
693
694   for (ii=0; ii<n_comm; ii++) {
695     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
696     for (jj=0; jj<n_realm; jj++) {
697       best_route=trps_find_best_route(trps, comm[ii], realm[jj], NULL);
698       if (best_route==NULL)
699         best_metric=TRP_METRIC_INFINITY;
700       else
701         best_metric=trp_route_get_metric(best_route);
702
703       cur_route=trps_get_selected_route(trps, comm[ii], realm[jj]);
704       if (cur_route!=NULL) {
705         cur_metric=trp_route_get_metric(cur_route);
706         if ((best_metric < cur_metric) && (trp_metric_is_finite(best_metric))) {
707           /* The new route has a lower metric than the previous, and is finite. Accept. */
708           trp_route_set_selected(cur_route, 0);
709           trp_route_set_selected(best_route, 1);
710         } else if (!trp_metric_is_finite(cur_metric)) /* rejects infinite or invalid metrics */
711           trp_route_set_selected(cur_route, 0);
712       } else if (trp_metric_is_finite(best_metric)) {
713         trp_route_set_selected(best_route, 1);
714       }
715     }
716     if (realm!=NULL)
717       talloc_free(realm);
718     realm=NULL; n_realm=0;
719   }
720   if (comm!=NULL)
721     talloc_free(comm);
722   comm=NULL; n_comm=0;
723
724   return TRP_SUCCESS;
725 }
726
727 /* true if curtime >= expiry */
728 static int trps_expired(struct timespec *expiry, struct timespec *curtime)
729 {
730   return ((curtime->tv_sec > expiry->tv_sec)
731          || ((curtime->tv_sec == expiry->tv_sec)
732             &&(curtime->tv_nsec > expiry->tv_nsec)));
733 }
734
735 /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
736  * If its metric is finite, the metric is set to infinite and the route's expiration time is updated. */
737 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
738 {
739   struct timespec sweep_time={0,0};
740   TRP_ROUTE **entry=NULL;
741   size_t n_entry=0;
742   size_t ii=0;
743
744   /* use a single time for the entire sweep */
745   if (0!=clock_gettime(CLOCK_REALTIME, &sweep_time)) {
746     tr_err("trps_sweep_routes: could not read realtime clock.");
747     sweep_time.tv_sec=0;
748     sweep_time.tv_nsec=0;
749     return TRP_ERROR;
750   }
751
752   entry=trp_rtable_get_entries(trps->rtable, &n_entry); /* must talloc_free *entry */
753
754   /* loop over the entries */
755   for (ii=0; ii<n_entry; ii++) {
756     if (!trp_route_is_local(entry[ii]) && trps_expired(trp_route_get_expiry(entry[ii]), &sweep_time)) {
757       tr_debug("trps_sweep_routes: route expired.");
758       if (!trp_metric_is_finite(trp_route_get_metric(entry[ii]))) {
759         /* flush route */
760         tr_debug("trps_sweep_routes: metric was infinity, flushing route.");
761         trp_rtable_remove(trps->rtable, entry[ii]); /* entry[ii] is no longer valid */
762         entry[ii]=NULL;
763       } else {
764         /* set metric to infinity and reset timer */
765         tr_debug("trps_sweep_routes: setting metric to infinity and resetting expiry.");
766         trp_route_set_metric(entry[ii], TRP_METRIC_INFINITY);
767         trp_route_set_expiry(entry[ii], trps_compute_expiry(trps,
768                                                              trp_route_get_interval(entry[ii]),
769                                                              trp_route_get_expiry(entry[ii])));
770       }
771     }
772   }
773
774   talloc_free(entry);
775   return TRP_SUCCESS;
776 }
777
778 /* select the correct route to comm/realm to be announced to peer */
779 static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname)
780 {
781   TRP_ROUTE *route;
782
783   /* Take the currently selected route unless it is through the peer we're sending the update to.
784    * I.e., enforce the split horizon rule. */
785   route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
786   if (route==NULL) {
787     /* No selected route, this should only happen if the only route has been retracted,
788      * in which case we do not want to advertise it. */
789     return NULL;
790   }
791   tr_debug("trps_select_realm_update: %s vs %s", peer_gssname->buf,
792            trp_route_get_peer(route)->buf);
793   if (0==tr_name_cmp(peer_gssname, trp_route_get_peer(route))) {
794     tr_debug("trps_select_realm_update: matched, finding alternate route");
795     /* the selected entry goes through the peer we're reporting to, choose an alternate */
796     route=trps_find_best_route(trps, comm, realm, peer_gssname);
797     if ((route==NULL) || (!trp_metric_is_finite(trp_route_get_metric(route))))
798       return NULL; /* don't advertise a nonexistent or retracted route */
799   }
800   return route;
801 }
802
803 /* returns an array of pointers to updates (*not* an array of updates). Returns number of entries
804  * via n_update parameter. (The allocated space will generally be larger than required, see note in
805  * the code.) If triggered is set, sends only triggered updates. */
806 static TRP_ROUTE **trps_select_updates_for_peer(TALLOC_CTX *memctx,
807                                                  TRPS_INSTANCE *trps,
808                                                  TR_NAME *peer_gssname,
809                                                  int triggered,
810                                                  size_t *n_update)
811 {
812   size_t n_comm=0;
813   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
814   TR_NAME **realm=NULL;
815   size_t n_realm=0;
816   size_t ii=0, jj=0;
817   TRP_ROUTE *best=NULL;
818   TRP_ROUTE **result=NULL;
819   size_t n_used=0;
820
821   /* Need to allocate space for the results. For simplicity, we just allocate a block
822    * with space for every route table entry to be returned. This is guaranteed to be large
823    * enough. If the routing table gets very large, this may be wasteful, but that seems
824    * unlikely to be significant in the near future. */
825   result=talloc_array(memctx, TRP_ROUTE *, trp_rtable_size(trps->rtable));
826   if (result==NULL) {
827     talloc_free(comm);
828     *n_update=0;
829     return NULL;
830   }
831   
832   for (ii=0; ii<n_comm; ii++) {
833     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
834     for (jj=0; jj<n_realm; jj++) {
835       best=trps_select_realm_update(trps, comm[ii], realm[jj], peer_gssname);
836       /* If we found a route, add it to the list. If triggered!=0, then only
837        * add triggered routes. */
838       if ((best!=NULL) && ((!triggered) || trp_route_is_triggered(best)))
839         result[n_used++]=best;
840     }
841     if (realm!=NULL)
842       talloc_free(realm);
843     realm=NULL;
844     n_realm=0;
845   }
846   if (comm!=NULL)
847     talloc_free(comm);
848
849   *n_update=n_used;
850   return result;
851 }
852
853 /* add metrics */
854 static unsigned int trps_metric_add(unsigned int m1, unsigned int m2)
855 {
856   if (trp_metric_is_invalid(m1) || trp_metric_is_invalid(m2))
857     return TRP_METRIC_INVALID;
858
859   if (trp_metric_is_infinite(m1) || trp_metric_is_infinite(m2))
860     return TRP_METRIC_INFINITY;
861
862   if (trp_metric_is_finite(m1+m2))
863     return m1+m2;
864   else
865     return TRP_METRIC_INFINITY;
866 }
867
868 /* convert an rentry into a new trp update info record */
869 static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
870 {
871   TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE);
872   unsigned int linkcost=0;
873
874   if (rec!=NULL) {
875     if (trp_route_is_local(route))
876       linkcost=0;
877     else {
878       linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps,
879                                                               trp_route_get_peer(route)));
880     }
881
882     /* Note that we leave the next hop empty since the recipient fills that in.
883      * This is where we add the link cost (currently always 1) to the next peer. */
884     if ((trp_inforec_set_comm(rec, trp_route_dup_comm(route)) != TRP_SUCCESS)
885        ||(trp_inforec_set_realm(rec, trp_route_dup_realm(route)) != TRP_SUCCESS)
886        ||(trp_inforec_set_trust_router(rec, trp_route_dup_trust_router(route)) != TRP_SUCCESS)
887        ||(trp_inforec_set_metric(rec,
888                                  trps_metric_add(trp_route_get_metric(route),
889                                                  linkcost)) != TRP_SUCCESS)
890        ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) {
891       tr_err("trps_route_to_inforec: error creating route update.");
892       talloc_free(rec);
893       rec=NULL;
894     }
895   }
896   return rec;
897 }
898
899 /* all routes to a single peer, unless comm/realm are specified (both or neither must be NULL) */
900 static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
901                                    TR_NAME *peer_gssname,
902                                    TRP_UPDATE_TYPE update_type,
903                                    TR_NAME *comm,
904                                    TR_NAME *realm)
905 {
906   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
907   TR_MSG msg; /* not a pointer! */
908   TRP_UPD *upd=NULL;
909   TRP_ROUTE **update_list=NULL;
910   TRP_INFOREC *rec=NULL;
911   size_t n_updates=0, ii=0;
912   char *encoded=NULL;
913   TRP_RC rc=TRP_ERROR;
914   TRP_PEER *peer=trps_get_peer_by_gssname(trps, peer_gssname);
915
916   switch (update_type) {
917   case TRP_UPDATE_TRIGGERED:
918     tr_debug("trps_update_one_peer: preparing triggered route update for %.*s",
919              peer_gssname->len, peer_gssname->buf);
920     break;
921   case TRP_UPDATE_SCHEDULED:
922     tr_debug("trps_update_one_peer: preparing scheduled route update for %.*s",
923              peer_gssname->len, peer_gssname->buf);
924     break;
925   case TRP_UPDATE_REQUESTED:
926     tr_debug("trps_update_one_peer: preparing requested route update for %.*s",
927              peer_gssname->len, peer_gssname->buf);
928   }
929
930   /* do not fill in peer, recipient does that */
931   if ((comm==NULL) && (realm==NULL)) {
932     /* do all realms */
933     update_list=trps_select_updates_for_peer(tmp_ctx,
934                                              trps,
935                                              peer_gssname,
936                                              update_type==TRP_UPDATE_TRIGGERED,
937                                             &n_updates);
938   } else if ((comm!=NULL) && (realm!=NULL)) {
939     /* a single community/realm was requested */
940     update_list=talloc(tmp_ctx, TRP_ROUTE *);
941     if (update_list==NULL) {
942       tr_err("trps_update_one_peer: could not allocate update_list.");
943       rc=TRP_NOMEM;
944       goto cleanup;
945     }
946     *update_list=trps_select_realm_update(trps, comm, realm, peer_gssname);
947     if (*update_list==NULL) {
948       /* we have no actual update to send back, MUST send a retraction */
949       tr_debug("trps_update_one_peer: community/realm without route requested, sending mandatory retraction.");
950       *update_list=trp_route_new(update_list);
951       trp_route_set_comm(*update_list, tr_dup_name(comm));
952       trp_route_set_realm(*update_list, tr_dup_name(realm));
953       trp_route_set_peer(*update_list, tr_new_name(""));
954       trp_route_set_metric(*update_list, TRP_METRIC_INFINITY);
955       trp_route_set_trust_router(*update_list, tr_new_name(""));
956       trp_route_set_next_hop(*update_list, tr_new_name(""));
957     }
958     n_updates=1;
959   } else {
960     tr_err("trps_update_one_peer: error: only comm or realm was specified.");
961     rc=TRP_ERROR;
962     goto cleanup;
963   }
964   if ((n_updates>0) && (update_list!=NULL)) {
965     tr_debug("trps_update_one_peer: sending %u update records.", (unsigned int)n_updates);
966     upd=trp_upd_new(tmp_ctx);
967
968     for (ii=0; ii<n_updates; ii++) {
969       rec=trps_route_to_inforec(tmp_ctx, trps, update_list[ii]);
970       if (rec==NULL) {
971         tr_err("trps_update_one_peer: could not create all update records.");
972         rc=TRP_ERROR;
973         goto cleanup;
974       }
975       trp_upd_add_inforec(upd, rec);
976     }
977     talloc_free(update_list);
978     update_list=NULL;
979
980     /* now encode the update message */
981     tr_msg_set_trp_upd(&msg, upd);
982     encoded=tr_msg_encode(&msg);
983     if (encoded==NULL) {
984       tr_err("trps_update_one_peer: error encoding update.");
985       rc=TRP_ERROR;
986       goto cleanup;
987     }
988
989     tr_debug("trps_update_one_peer: adding message to queue.");
990     if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS)
991       tr_err("trps_update_one_peer: error queueing update.");
992     else
993       tr_debug("trps_update_one_peer: update queued successfully.");
994
995     tr_msg_free_encoded(encoded);
996     encoded=NULL;
997     trp_upd_free(upd);
998     upd=NULL;
999   }
1000
1001 cleanup:
1002   talloc_free(tmp_ctx);
1003   return rc;
1004 }
1005
1006 /* all routes to all peers */
1007 TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type)
1008 {
1009   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1010   TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
1011   TRP_PEER *peer=NULL;
1012   TRP_RC rc=TRP_SUCCESS;
1013
1014   if (iter==NULL) {
1015     tr_err("trps_update: failed to allocate peer table iterator.");
1016     talloc_free(tmp_ctx);
1017     return TRP_NOMEM;
1018   }
1019
1020   for (peer=trp_ptable_iter_first(iter, trps->ptable);
1021        peer!=NULL && rc==TRP_SUCCESS;
1022        peer=trp_ptable_iter_next(iter))
1023   {
1024     if (!trps_peer_connected(trps, peer)) {
1025       TR_NAME *peer_gssname=trp_peer_get_gssname(peer);
1026       tr_debug("trps_update: no TRP connection to %.*s, skipping.",
1027                peer_gssname->len, peer_gssname->buf);
1028       continue;
1029     }
1030     rc=trps_update_one_peer(trps, trp_peer_get_gssname(peer), update_type, NULL, NULL);
1031   }
1032   
1033   trp_ptable_iter_free(iter);
1034   trp_rtable_clear_triggered(trps->rtable); /* don't re-send triggered updates */
1035   talloc_free(tmp_ctx);
1036   return rc;
1037 }        
1038
1039 TRP_RC trps_add_route(TRPS_INSTANCE *trps, TRP_ROUTE *route)
1040 {
1041   trp_rtable_add(trps->rtable, route); /* should return status */
1042   return TRP_SUCCESS; 
1043 }
1044
1045 /* steals the peer object */
1046 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
1047 {
1048   return trp_ptable_add(trps->ptable, peer);
1049 }
1050
1051 TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname)
1052 {
1053   return trp_ptable_find_gssname(trps->ptable, gssname);
1054 }
1055
1056 TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename)
1057 {
1058   return trp_ptable_find_servicename(trps->ptable, servicename);
1059 }
1060
1061 int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer)
1062 {
1063   TRPC_INSTANCE *trpc=trps_find_trpc(trps, peer);
1064   if (trpc==NULL)
1065     return 0;
1066
1067   if (trpc_get_status(trpc)==TRP_CONNECTION_UP)
1068     return 1;
1069   else
1070     return 0;
1071 }
1072
1073
1074 static TRP_RC trps_handle_request(TRPS_INSTANCE *trps, TRP_REQ *req)
1075 {
1076   TR_NAME *comm=NULL;
1077   TR_NAME *realm=NULL;
1078
1079   tr_debug("trps_handle_request: handling TRP request.");
1080
1081   if (trps_validate_request(trps, req) != TRP_SUCCESS) {
1082     tr_notice("trps_handle_request: received invalid TRP request.");
1083     return TRP_ERROR;
1084   }
1085
1086   if (!trp_req_is_wildcard(req)) {
1087     comm=trp_req_get_comm(req);
1088     realm=trp_req_get_realm(req);
1089     tr_debug("trps_handle_request: route for %.*s/%.*s requested.",
1090              comm->len, comm->buf, realm->len, realm->buf);
1091   } else {
1092     tr_debug("trps_handle_request: all routes requested.");
1093     /* leave comm/realm NULL */
1094   }
1095   return trps_update_one_peer(trps, trp_req_get_peer(req), TRP_UPDATE_REQUESTED, comm, realm);
1096 }
1097
1098
1099 TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
1100 {
1101   TRP_RC rc=TRP_ERROR;
1102
1103   switch (tr_msg_get_msg_type(tr_msg)) {
1104   case TRP_UPDATE:
1105     rc=trps_handle_update(trps, tr_msg_get_trp_upd(tr_msg));
1106     if (rc==TRP_SUCCESS) {
1107       rc=trps_update_active_routes(trps);
1108       trps_update(trps, TRP_UPDATE_TRIGGERED); /* send any triggered routes */
1109     }
1110     return rc;
1111
1112   case TRP_REQUEST:
1113     rc=trps_handle_request(trps, tr_msg_get_trp_req(tr_msg));
1114     return rc;
1115
1116   default:
1117     /* unknown error or one we don't care about (e.g., TID messages) */
1118     return TRP_ERROR;
1119   }
1120 }
1121
1122 /* send wildcard route request to a peer */
1123 TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_servicename)
1124 {
1125   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1126   TRP_PEER *peer=trps_get_peer_by_servicename(trps, peer_servicename);
1127   TR_MSG msg; /* not a pointer */
1128   TRP_REQ *req=trp_req_new(tmp_ctx);
1129   char *encoded=NULL;
1130   TRP_RC rc=TRP_ERROR;
1131
1132   if (peer==NULL) {
1133     tr_err("trps_wildcard_route_req: unknown peer (%.*s).", peer_servicename->len, peer_servicename->buf);
1134     rc=TRP_BADARG;
1135     goto cleanup;
1136   }
1137   if ((req==NULL) || (trp_req_make_wildcard(req)!=TRP_SUCCESS)) {
1138     tr_err("trps_wildcard_route_req: unable to create wildcard TRP request.");
1139     rc=TRP_NOMEM;
1140     goto cleanup;
1141   }
1142
1143   tr_msg_set_trp_req(&msg, req);
1144   encoded=tr_msg_encode(&msg);
1145   if (encoded==NULL) {
1146     tr_err("trps_wildcard_route_req: error encoding wildcard TRP request.");
1147     rc=TRP_ERROR;
1148     goto cleanup;
1149   }
1150
1151   tr_debug("trps_wildcard_route_req: adding message to queue.");
1152   if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS) {
1153     tr_err("trps_wildcard_route_req: error queueing request.");
1154     rc=TRP_ERROR;
1155   } else {
1156     tr_debug("trps_wildcard_route_req: request queued successfully.");
1157     rc=TRP_SUCCESS;
1158   }
1159
1160 cleanup:
1161   if (encoded!=NULL)
1162     tr_msg_free_encoded(encoded);
1163   if (req!=NULL)
1164     trp_req_free(req);
1165
1166   talloc_free(tmp_ctx);
1167   return rc;
1168 }