Correctly set peer when an update is received
[trust_router.git] / trp / trps.c
1 /*
2  * Copyright (c) 2016, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34
35 #include <fcntl.h>
36 #include <talloc.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <sys/time.h>
40 #include <glib.h>
41 #include <string.h>
42 #include <poll.h> // for nfds_t
43
44 #include <gsscon.h>
45 #include <tr_comm.h>
46 #include <tr_apc.h>
47 #include <tr_rp.h>
48 #include <tr_name_internal.h>
49 #include <trp_route.h>
50 #include <trp_internal.h>
51 #include <tr_gss_names.h>
52 #include <trp_peer.h>
53 #include <trp_ptable.h>
54 #include <trp_rtable.h>
55 #include <tr_debug.h>
56 #include <tr_util.h>
57 #include <tr_socket.h>
58
59 static int trps_destructor(void *object)
60 {
61   TRPS_INSTANCE *trps=talloc_get_type_abort(object, TRPS_INSTANCE);
62   if (trps->rtable!=NULL)
63     trp_rtable_free(trps->rtable);
64   return 0;
65 }
66
67 TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
68 {
69   TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
70   if (trps!=NULL)  {
71     trps->hostname=NULL;
72     trps->trps_port=0;
73     trps->cookie=NULL;
74     trps->conn=NULL;
75     trps->trpc=NULL;
76     trps->update_interval=(struct timeval){0,0};
77     trps->sweep_interval=(struct timeval){0,0};
78     trps->ptable=NULL;
79
80     trps->mq=tr_mq_new(trps);
81     if (trps->mq==NULL) {
82       /* failed to allocate mq */
83       talloc_free(trps);
84       return NULL;
85     }
86
87     trps->rtable=NULL;
88     if (trps_init_rtable(trps) != TRP_SUCCESS) {
89       /* failed to allocate rtable */
90       talloc_free(trps);
91       return NULL;
92     }
93
94     talloc_set_destructor((void *)trps, trps_destructor);
95   }
96   return trps;
97 }
98
99 /* create a new route table, first discarding an old one if necessary */
100 TRP_RC trps_init_rtable(TRPS_INSTANCE *trps)
101 {
102   if (trps->rtable != NULL) {
103     trp_rtable_free(trps->rtable);
104     trps->rtable=NULL;
105   }
106
107   trps->rtable=trp_rtable_new();
108   if (trps->rtable==NULL) {
109     return TRP_NOMEM;
110   }
111   return TRP_SUCCESS;
112 }
113
114 void trps_clear_rtable(TRPS_INSTANCE *trps)
115 {
116   trp_rtable_clear(trps->rtable);
117 }
118
119 void trps_free (TRPS_INSTANCE *trps)
120 {
121   if (trps!=NULL)
122     talloc_free(trps);
123 }
124
125 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
126 {
127   return tr_mq_pop(trps->mq, 0);
128 }
129
130 void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
131 {
132   tr_mq_add(trps->mq, msg);
133 }
134
135 unsigned int trps_get_connect_interval(TRPS_INSTANCE *trps)
136 {
137   return trps->connect_interval.tv_sec;
138 }
139
140 void trps_set_connect_interval(TRPS_INSTANCE *trps, unsigned int interval)
141 {
142   trps->connect_interval.tv_sec=interval;
143   trps->connect_interval.tv_usec=0;
144 }
145
146 unsigned int trps_get_update_interval(TRPS_INSTANCE *trps)
147 {
148   return trps->update_interval.tv_sec;
149 }
150
151 void trps_set_update_interval(TRPS_INSTANCE *trps, unsigned int interval)
152 {
153   trps->update_interval.tv_sec=interval;
154   trps->update_interval.tv_usec=0;
155 }
156
157 unsigned int trps_get_sweep_interval(TRPS_INSTANCE *trps)
158 {
159   return trps->sweep_interval.tv_sec;
160 }
161
162 void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval)
163 {
164   trps->sweep_interval.tv_sec=interval;
165   trps->sweep_interval.tv_usec=0;
166 }
167
168 void trps_set_ctable(TRPS_INSTANCE *trps, TR_COMM_TABLE *comm)
169 {
170   trps->ctable=comm;
171 }
172
173 void trps_set_ptable(TRPS_INSTANCE *trps, TRP_PTABLE *ptable)
174 {
175   if (trps->ptable!=NULL)
176     trp_ptable_free(trps->ptable);
177   trps->ptable=ptable;
178 }
179
180 void trps_set_peer_status_callback(TRPS_INSTANCE *trps, void (*cb)(TRP_PEER *, void *), void *cookie)
181 {
182   TRP_PTABLE_ITER *iter=NULL;
183   TRP_PEER *peer=NULL;
184   if (trps->ptable==NULL)
185     return;
186
187   iter=trp_ptable_iter_new(NULL);
188   for (peer=trp_ptable_iter_first(iter, trps->ptable); peer!=NULL; peer=trp_ptable_iter_next(iter))
189     trp_peer_set_conn_status_cb(peer, cb, cookie);
190   trp_ptable_iter_free(iter);
191 }
192
193 /* Get the label peers will know us by - needs to match trp_peer_get_label() output.
194  * There is no get, only dup, because we don't store the label except when requested. */
195 TR_NAME *trps_dup_label(TRPS_INSTANCE *trps)
196 {
197   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
198   TR_NAME *label=NULL;
199   char *s=talloc_asprintf(tmp_ctx, "%s:%u", trps->hostname, trps->trps_port);
200   if (s==NULL)
201     goto cleanup;
202   label=tr_new_name(s);
203
204 cleanup:
205   talloc_free(tmp_ctx);
206   return label;
207 }
208
209 TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TRP_PEER *peer)
210 {
211   TRPC_INSTANCE *cur=NULL;
212   TR_NAME *name=NULL;
213   TR_NAME *peer_servicename=trp_peer_get_servicename(peer);
214
215   for (cur=trps->trpc; cur!=NULL; cur=trpc_get_next(cur)) {
216     name=trpc_get_gssname(cur);
217     if ((name!=NULL) && (0==tr_name_cmp(peer_servicename, name))) {
218       break;
219     }
220   }
221   return cur;
222 }
223
224 void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
225 {
226   if (trps->conn==NULL)
227     trps->conn=new;
228   else
229     trp_connection_append(trps->conn, new);
230
231   talloc_steal(trps, new);
232 }
233
234 /* ok to call more than once; guarantees connection no longer in the list.
235  * Caller is responsible for freeing the removed element afterwards.  */
236 void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
237 {
238   trps->conn=trp_connection_remove(trps->conn, remove);
239 }
240
241 void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
242 {
243   if (trps->trpc==NULL)
244     trps->trpc=trpc;
245   else
246     trpc_append(trps->trpc, trpc);
247
248   talloc_steal(trps, trpc);
249 }
250
251 /* ok to call more than once; guarantees trpc no longer in the list.
252  * Caller is responsible for freeing the removed element afterwards.  */
253 void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove)
254 {
255   trps->trpc=trpc_remove(trps->trpc, remove);
256 }
257
258 TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg)
259 {
260   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
261   TR_MQ_MSG *mq_msg=NULL;
262   char *msg_dup=NULL;
263   TRP_RC rc=TRP_ERROR;
264   TRPC_INSTANCE *trpc=NULL;
265
266   /* get the connection for this peer */
267   trpc=trps_find_trpc(trps, peer);
268   /* The peer connection (trpc) usually exists even if the connection is down.
269    * We will queue messages even if the connection is down. To prevent this from
270    * endlessly increasing the size of the queue, the trpc handler needs to clear
271    * its queue periodically, even if it is unable to send the messages
272    */
273   if (trpc==NULL) {
274     tr_warning("trps_send_msg: skipping message queued for missing TRP client entry.");
275   } else {
276     mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_SEND);
277     msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
278     tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
279     trpc_mq_add(trpc, mq_msg);
280     rc=TRP_SUCCESS;
281   }
282   talloc_free(tmp_ctx);
283   return rc;
284 }
285
286 /* get the currently selected route if available */
287 TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
288 {
289   return trp_rtable_get_entry(trps->rtable, comm, realm, peer);
290 }
291
292 TRP_ROUTE *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
293 {
294   tr_debug("trps_get_selected_route: entered. trps=%p, comm=%p, realm=%p", trps, comm, realm);
295   return trp_rtable_get_selected_entry(trps->rtable, comm, realm);
296 }
297
298 /* copy the result if you want to keep it */
299 TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
300 {
301   TRP_ROUTE *route=trps_get_selected_route(trps, comm, realm);
302   if (route==NULL)
303     return NULL;
304
305   return trp_route_get_next_hop(route);
306 }
307
308
309 /* mark a route as retracted */
310 static void trps_retract_route(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
311 {
312   trp_route_set_metric(entry, TRP_METRIC_INFINITY);
313   trp_route_set_triggered(entry, 1);
314 }
315
316 /* is this route retracted? */
317 static int trps_route_retracted(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
318 {
319   return (trp_metric_is_infinite(trp_route_get_metric(entry)));
320 }
321
322 static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
323 {
324   int err=0;
325   char *buf=NULL;
326   size_t buflen = 0;
327   TRP_PEER *peer=NULL; /* entry in the peer table */
328   TR_NAME *conn_peer=NULL; /* name from the TRP_CONN, which comes from the gss context */
329
330   tr_debug("trps_read_message: started");
331   if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
332                                        *(trp_connection_get_gssctx(conn)), 
333                                        &buf,
334                                        &buflen)) {
335     tr_debug("trps_read_message: error");
336     if (buf)
337       free(buf);
338     return TRP_ERROR;
339   }
340
341   tr_debug("trps_read_message: message received, %u bytes.", (unsigned) buflen);
342   tr_debug("trps_read_message: %.*s", buflen, buf);
343
344   *msg= tr_msg_decode(NULL, buf, buflen);
345   free(buf);
346   if (*msg==NULL)
347     return TRP_NOPARSE;
348
349   conn_peer=trp_connection_get_peer(conn);
350   if (conn_peer==NULL) {
351     tr_err("trps_read_message: connection has no peer name");
352     return TRP_ERROR;
353   }
354
355   peer=trps_get_peer_by_gssname(trps, conn_peer);
356   if (peer==NULL) {
357     tr_err("trps_read_message: could not find peer with gssname=%s", trp_connection_get_gssname(conn));
358     return TRP_ERROR;
359   }
360
361   /* verify we received a message we support, otherwise drop it now */
362   switch (tr_msg_get_msg_type(*msg)) {
363   case TRP_UPDATE:
364     trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(conn_peer));
365     /* update provenance if necessary */
366     trp_upd_add_to_provenance(tr_msg_get_trp_upd(*msg), trp_peer_get_label(peer));
367     break;
368
369   case TRP_REQUEST:
370     trp_req_set_peer(tr_msg_get_trp_req(*msg), tr_dup_name(conn_peer));
371     break;
372
373   default:
374     tr_debug("trps_read_message: received unsupported message from %.*s", conn_peer->len, conn_peer->buf);
375     tr_msg_free_decoded(*msg);
376     *msg=NULL;
377     return TRP_UNSUPPORTED;
378   }
379   
380   return TRP_SUCCESS;
381 }
382
383 int trps_get_listener(TRPS_INSTANCE *trps,
384                       TRPS_MSG_FUNC msg_handler,
385                       TRP_AUTH_FUNC auth_handler,
386                       const char *hostname,
387                       int port,
388                       void *cookie,
389                       int *fd_out,
390                       size_t max_fd)
391 {
392   nfds_t n_fd=0;
393   nfds_t ii=0;
394
395   n_fd = tr_sock_listen_all(port, fd_out, max_fd);
396
397   if (n_fd == 0)
398     tr_err("trps_get_listener: Error opening port %d.");
399   else {
400     /* opening port succeeded */
401     tr_info("trps_get_listener: Opened port %d.", port);
402     
403     /* make the sockets non-blocking */
404     for (ii=0; ii<n_fd; ii++) {
405       if (0 != fcntl(fd_out[ii], F_SETFL, O_NONBLOCK)) {
406         tr_err("trps_get_listener: Error setting O_NONBLOCK.");
407         for (ii=0; ii<n_fd; ii++) {
408           close(fd_out[ii]);
409           fd_out[ii]=-1;
410         }
411         n_fd = 0;
412         break;
413       }
414     }
415   }
416
417   if (n_fd > 0) {
418     /* store the caller's request handler & cookie */
419     trps->msg_handler = msg_handler;
420     trps->auth_handler = auth_handler;
421     trps->hostname = talloc_strdup(trps, hostname);
422     trps->trps_port = port;
423     trps->cookie = cookie;
424   }
425
426   return (int) n_fd;
427 }
428
429 TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
430 {
431   /* try to establish a GSS context */
432   if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
433     tr_notice("trps_authorize_connection: failed to authorize connection");
434     trp_connection_close(conn);
435     return TRP_ERROR;
436   }
437   tr_notice("trps_authorize_connection: authorized connection");
438   return TRP_SUCCESS;
439 }
440
441 void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
442 {
443   TR_MSG *msg=NULL;
444   TRP_RC rc=TRP_ERROR;
445
446   /* loop as long as the connection exists */
447   while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
448     rc=trps_read_message(trps, conn, &msg);
449     switch(rc) {
450     case TRP_SUCCESS:
451       trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
452       break;
453
454     case TRP_ERROR:
455       trp_connection_close(conn);
456       break;
457
458     default:
459       tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
460     }
461   }
462
463   tr_debug("trps_handle_connection: connection closed.");
464 }
465
466 /* TODO: check realm/comm, now part of the update instead of inforec */
467 static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
468 {
469   if (upd==NULL) {
470     tr_notice("trps_validate_update: null TRP update.");
471     return TRP_BADARG;
472   }
473
474   if (trp_upd_get_realm(upd)==NULL) {
475     tr_notice("trps_validate_update: received TRP update without realm.");
476     return TRP_ERROR;
477   }
478
479   if (trp_upd_get_comm(upd)==NULL) {
480     tr_notice("trps_validate_update: received TRP update without community.");
481     return TRP_ERROR;
482   }
483
484   if (trp_upd_get_inforec(upd)==NULL) {
485     tr_notice("trps_validate_update: received TRP update with no info records.");
486     return TRP_ERROR;
487   }
488
489   if (trp_upd_get_peer(upd)==NULL) {
490     tr_notice("trps_validate_update: received TRP update without origin peer information.");
491     return TRP_ERROR;
492   }
493
494   
495   return TRP_SUCCESS;
496 }
497
498 /* ensure that the update could be accepted if feasible */
499 static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
500 {
501   switch(trp_inforec_get_type(rec)) {
502   case TRP_INFOREC_TYPE_ROUTE:
503     if ((trp_inforec_get_trust_router(rec)==NULL)
504         || (trp_inforec_get_next_hop(rec)==NULL)) {
505       tr_debug("trps_validate_inforec: missing record info.");
506       return TRP_ERROR;
507     }
508
509     /* check for valid ports */
510     if ((trp_inforec_get_trust_router_port(rec) <= 0)
511         || (trp_inforec_get_trust_router_port(rec) > 65535)) {
512       tr_debug("trps_validate_inforec: invalid trust router port (%d)",
513                trp_inforec_get_trust_router_port(rec));
514       return TRP_ERROR;
515     }
516
517       if ((trp_inforec_get_next_hop_port(rec) <= 0)
518           || (trp_inforec_get_next_hop_port(rec) > 65535)) {
519         tr_debug("trps_validate_inforec: invalid next hop port (%d)",
520                  trp_inforec_get_next_hop_port(rec));
521         return TRP_ERROR;
522       }
523
524       /* check for valid metric */
525     if (trp_metric_is_invalid(trp_inforec_get_metric(rec))) {
526       tr_debug("trps_validate_inforec: invalid metric (%u).", trp_inforec_get_metric(rec));
527       return TRP_ERROR;
528     }
529
530     /* check for valid interval */
531     if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
532       tr_debug("trps_validate_inforec: invalid interval.");
533       return TRP_ERROR;
534     }
535     break;
536
537   case TRP_INFOREC_TYPE_COMMUNITY:
538     /* TODO: validate community updates */
539     break;
540     
541   default:
542     tr_notice("trps_validate_inforec: unsupported record type.");
543     return TRP_UNSUPPORTED;
544   }
545
546   return TRP_SUCCESS;
547 }
548
549 /* link cost to a peer */
550 static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
551 {
552   return 1;
553 }
554
555 static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
556 {
557   TRP_ROUTE *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
558   if (entry==NULL)
559     return TRP_METRIC_INFINITY;
560   return trp_route_get_metric(entry) + trps_cost(trps, peer);
561 }
562
563 static int trps_check_feasibility(TRPS_INSTANCE *trps, TR_NAME *realm, TR_NAME *comm, TRP_INFOREC *rec)
564 {
565   unsigned int rec_metric=trp_inforec_get_metric(rec);
566   unsigned int new_metric=0;
567   unsigned int current_metric=0;
568   TR_NAME *next_hop=NULL;
569
570   /* we check these in the validation stage, but just in case... */
571   if (trp_metric_is_invalid(rec_metric))
572     return 0;
573
574   /* retractions (aka infinite metrics) are always feasible */
575   if (trp_metric_is_infinite(rec_metric))
576     return 1;
577
578   /* updates from our current next hop are always feasible*/
579   next_hop=trps_get_next_hop(trps, comm, realm);
580   if ((next_hop!=NULL)
581      && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
582     return 1;
583   }
584     
585
586   /* compare the existing metric we advertise to what we would advertise
587    * if we accept this update */
588   current_metric=trps_advertised_metric(trps, comm, realm, trp_inforec_get_next_hop(rec));
589   new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
590   if (new_metric <= current_metric)
591     return 1;
592   else
593     return 0;
594 }
595
596 /* uses memory pointed to by *ts, also returns that value. On error, its contents are {0,0} */
597 static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts)
598 {
599   const unsigned int small_factor=3; /* how many intervals we wait before expiring */
600   if (0!=clock_gettime(TRP_CLOCK, ts)) {
601     tr_err("trps_compute_expiry: could not read realtime clock.");
602     ts->tv_sec=0;
603     ts->tv_nsec=0;
604   }
605   tr_debug("trps_compute_expiry: tv_sec=%u, interval=%u, small_factor*interval=%u", ts->tv_sec, interval, small_factor*interval);
606   ts->tv_sec += small_factor*interval;
607   return ts;
608 }
609
610
611 /* compare hostname/port of the trust router, return 0 if they match */
612 static int trust_router_changed(TRP_ROUTE *route, TRP_INFOREC *rec)
613 {
614   if (trp_route_get_trust_router_port(route) != trp_inforec_get_trust_router_port(rec))
615     return 1;
616
617   return tr_name_cmp(trp_route_get_trust_router(route),
618                      trp_inforec_get_trust_router(rec));
619 }
620
621 static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
622 {
623   TRP_ROUTE *entry=NULL;
624
625   entry=trp_rtable_get_entry(trps->rtable,
626                              trp_upd_get_comm(upd),
627                              trp_upd_get_realm(upd),
628                              trp_inforec_get_next_hop(rec));
629   if (entry==NULL) {
630     entry=trp_route_new(NULL);
631     if (entry==NULL) {
632       tr_err("trps_accept_update: unable to allocate new entry.");
633       return TRP_NOMEM;
634     }
635
636     trp_route_set_comm(entry, trp_upd_dup_comm(upd));
637     trp_route_set_realm(entry, trp_upd_dup_realm(upd));
638     trp_route_set_peer(entry, trp_upd_dup_peer(upd));
639     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec));
640     trp_route_set_trust_router_port(entry, trp_inforec_get_trust_router_port(rec));
641     trp_route_set_next_hop(entry, trp_inforec_dup_next_hop(rec));
642     trp_route_set_next_hop_port(entry, trp_inforec_get_next_hop_port(rec));
643     if ((trp_route_get_comm(entry)==NULL)
644        ||(trp_route_get_realm(entry)==NULL)
645        ||(trp_route_get_peer(entry)==NULL)
646        ||(trp_route_get_trust_router(entry)==NULL)
647        ||(trp_route_get_next_hop(entry)==NULL)) {
648       /* at least one field could not be allocated */
649       tr_err("trps_accept_update: unable to allocate all fields for entry.");
650       trp_route_free(entry);
651       return TRP_NOMEM;
652     }
653     trp_rtable_add(trps->rtable, entry);
654   }
655
656   /* We now have an entry in the table, whether it's new or not. Update metric and expiry, unless
657    * the metric is infinity. An infinite metric can only occur here if we just retracted an existing
658    * route (we never accept retractions as new routes), so there is no risk of leaving the expiry
659    * time unset on a new route entry. */
660   tr_debug("trps_accept_update: accepting route update.");
661   trp_route_set_metric(entry, trp_inforec_get_metric(rec));
662   trp_route_set_interval(entry, trp_inforec_get_interval(rec));
663
664   /* check whether the trust router has changed (either name or port) */
665   if (trust_router_changed(entry, rec)) {
666     /* The name changed. Set this route as triggered. */
667     tr_debug("trps_accept_update: trust router for route changed.");
668     trp_route_set_triggered(entry, 1);
669     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec)); /* frees old name */
670     trp_route_set_trust_router_port(entry, trp_inforec_get_trust_router_port(rec));
671   }
672   if (!trps_route_retracted(trps, entry)) {
673     tr_debug("trps_accept_update: route not retracted, setting expiry timer.");
674     trp_route_set_expiry(entry, trps_compute_expiry(trps,
675                                                      trp_route_get_interval(entry),
676                                                      trp_route_get_expiry(entry)));
677   }
678   return TRP_SUCCESS;
679 }
680
681
682 static TRP_RC trps_handle_inforec_route(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
683 {
684   TRP_ROUTE *route=NULL;
685   TR_COMM *comm = NULL;
686   unsigned int feas=0;
687
688   /* determine feasibility */
689   feas=trps_check_feasibility(trps, trp_upd_get_realm(upd), trp_upd_get_comm(upd), rec);
690   tr_debug("trps_handle_update: record feasibility=%d", feas);
691
692   /* verify that the community is an APC */
693   comm = tr_comm_table_find_comm(trps->ctable, trp_upd_get_comm(upd));
694   if (comm == NULL) {
695     /* We don't know this community. Reject the route. */
696     tr_debug("trps_handle_updates: community %.*s unknown, ignoring route for %.*s",
697              trp_upd_get_comm(upd)->len, trp_upd_get_comm(upd)->buf,
698              trp_upd_get_realm(upd)->len, trp_upd_get_realm(upd)->buf);
699   } else if (tr_comm_get_type(comm) != TR_COMM_APC) {
700     /* The community in a route request *must* be an APC. This was not - ignore it. */
701     tr_debug("trps_handle_updates: community %.*s is not an APC, ignoring route for %.*s",
702              trp_upd_get_comm(upd)->len, trp_upd_get_comm(upd)->buf,
703              trp_upd_get_realm(upd)->len, trp_upd_get_realm(upd)->buf);
704   } else {
705     /* do we have an existing route? */
706     route=trps_get_route(trps,
707                          trp_upd_get_comm(upd),
708                          trp_upd_get_realm(upd),
709                          trp_upd_get_peer(upd));
710     if (route!=NULL) {
711       /* there was a route table entry already */
712       tr_debug("trps_handle_updates: route entry already exists.");
713       if (feas) {
714         /* Update is feasible. Accept it. */
715         trps_accept_update(trps, upd, rec);
716       } else {
717         /* Update is infeasible. Ignore it unless the trust router has changed. */
718         if (trust_router_changed(route, rec)) {
719           /* the trust router associated with the route has changed, treat update as a retraction */
720           trps_retract_route(trps, route);
721         }
722       }
723     } else {
724       /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
725       tr_debug("trps_handle_update: no route entry exists yet.");
726       if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
727         trps_accept_update(trps, upd, rec);
728     }
729   }
730
731   return TRP_SUCCESS;
732 }
733
734 static int trps_name_in_provenance(TR_NAME *name, json_t *prov)
735 {
736   size_t ii=0;
737   TR_NAME *this_name=NULL;
738   const char *s=NULL;
739
740   if (prov==NULL)
741     return 0; /* no provenance list, so it has no names in it */
742
743   /* now check to see if name is in the provenance */
744   for (ii=0; ii<json_array_size(prov); ii++) {
745     s=json_string_value(json_array_get(prov, ii));
746     if (s==NULL) {
747       tr_debug("trps_name_in_provenance: empty entry in provenance list.");
748       continue;
749     }
750
751     this_name=tr_new_name(s);
752     if (this_name==NULL) {
753       tr_debug("trps_name_in_provenance: unable to allocate name.");
754       return -1;
755     }
756     if (0==tr_name_cmp(name, this_name)) {
757       tr_free_name(this_name);
758       return 1;
759     }
760     tr_free_name(this_name);
761   }
762   return 0;
763 }
764
765 static TR_COMM *trps_create_new_comm(TALLOC_CTX *mem_ctx, TR_NAME *comm_id, TRP_INFOREC *rec)
766 {
767   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
768   TR_COMM *comm=tr_comm_new(tmp_ctx);
769   
770   if (comm==NULL) {
771     tr_debug("trps_create_new_comm: unable to allocate new community.");
772     goto cleanup;
773   }
774   /* fill in the community with info */
775   tr_comm_set_id(comm, tr_dup_name(comm_id));
776   if (tr_comm_get_id(comm)==NULL) {
777     tr_debug("trps_create_new_comm: unable to allocate community name.");
778     comm=NULL;
779     goto cleanup;
780   }
781   tr_comm_set_type(comm, trp_inforec_get_comm_type(rec));
782   if (trp_inforec_get_apcs(rec)!=NULL) {
783     tr_comm_set_apcs(comm, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec)));
784     if (tr_comm_get_apcs(comm)==NULL) {
785       tr_debug("trps_create_new_comm: unable to allocate APC list.");
786       comm=NULL;
787       goto cleanup;
788     }
789   }
790   if (trp_inforec_get_owner_realm(rec)!=NULL) {
791     tr_comm_set_owner_realm(comm, tr_dup_name(trp_inforec_get_owner_realm(rec)));
792     if (tr_comm_get_owner_realm(comm)==NULL) {
793       tr_debug("trps_create_new_comm: unable to allocate owner realm name.");
794       comm=NULL;
795       goto cleanup;
796     }
797   }
798   if (trp_inforec_get_owner_contact(rec)!=NULL) {
799     tr_comm_set_owner_contact(comm, tr_dup_name(trp_inforec_get_owner_contact(rec)));
800     if (tr_comm_get_owner_contact(comm)==NULL) {
801       tr_debug("trps_create_new_comm: unable to allocate owner contact.");
802       comm=NULL;
803       goto cleanup;
804     }
805   }
806   comm->expiration_interval=trp_inforec_get_exp_interval(rec);
807   talloc_steal(mem_ctx, comm);
808   
809 cleanup:
810   talloc_free(tmp_ctx);
811   return comm;
812 }
813
814 static TR_RP_REALM *trps_create_new_rp_realm(TALLOC_CTX *mem_ctx, TR_NAME *comm, TR_NAME *realm_id, TRP_INFOREC *rec)
815 {
816   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
817   TR_RP_REALM *rp=tr_rp_realm_new(tmp_ctx);
818   
819   if (rp==NULL) {
820     tr_debug("trps_create_new_rp_realm: unable to allocate new realm.");
821     goto cleanup;
822   }
823   /* fill in the realm */
824   tr_rp_realm_set_id(rp, tr_dup_name(realm_id));
825   if (tr_rp_realm_get_id(rp)==NULL) {
826     tr_debug("trps_create_new_rp_realm: unable to allocate realm name.");
827     rp=NULL;
828     goto cleanup;
829   }
830   talloc_steal(mem_ctx, rp);
831   
832 cleanup:
833   talloc_free(tmp_ctx);
834   return rp;
835 }
836
837 static TR_IDP_REALM *trps_create_new_idp_realm(TALLOC_CTX *mem_ctx,
838                                                TR_NAME *comm_id,
839                                                TR_NAME *realm_id,
840                                                TRP_INFOREC *rec)
841 {
842   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
843   TR_IDP_REALM *idp=tr_idp_realm_new(tmp_ctx);
844   TR_APC *realm_apcs = NULL;
845
846   if (idp==NULL) {
847     tr_debug("trps_create_new_idp_realm: unable to allocate new realm.");
848     goto cleanup;
849   }
850   /* fill in the realm */
851   tr_idp_realm_set_id(idp, tr_dup_name(realm_id));
852   if (tr_idp_realm_get_id(idp)==NULL) {
853     tr_debug("trps_create_new_idp_realm: unable to allocate realm name.");
854     idp=NULL;
855     goto cleanup;
856   }
857
858   /* Set the APCs. If the community is a CoI, copy its APCs. If it is an APC, then
859    * that community itself is the APC for the realm. */
860   if (trp_inforec_get_comm_type(rec) == TR_COMM_APC) {
861     /* the community is an APC for this realm */
862     realm_apcs = tr_apc_new(tmp_ctx);
863     if (realm_apcs == NULL) {
864       tr_debug("trps_create_new_idp_realm: unable to allocate new APC list.");
865       idp = NULL;
866       goto cleanup;
867     }
868
869     tr_apc_set_id(realm_apcs, tr_dup_name(comm_id));
870     if (tr_apc_get_id(realm_apcs) == NULL) {
871       tr_debug("trps_create_new_idp_realm: unable to allocate new APC name.");
872       idp = NULL;
873       goto cleanup;
874     }
875   } else {
876     /* the community is not an APC for this realm */
877     realm_apcs = trp_inforec_get_apcs(rec);
878     if (realm_apcs == NULL) {
879       tr_debug("trps_create_new_idp_realm: no APCs for realm %.*s/%.*s, cannot add.",
880                realm_id->len, realm_id->buf,
881                comm_id->len, comm_id->buf);
882       idp = NULL;
883       goto cleanup;
884     }
885
886     /* we have APCs, make our own copy */
887     realm_apcs = tr_apc_dup(tmp_ctx, realm_apcs);
888     if (realm_apcs == NULL) {
889       tr_debug("trps_create_new_idp_realm: unable to duplicate APC list.");
890       idp = NULL;
891       goto cleanup;
892     }
893   }
894
895   /* Whether the community is an APC or CoI, the APCs for the realm are in realm_apcs */
896   tr_idp_realm_set_apcs(idp, realm_apcs); /* takes realm_apcs out of tmp_ctx on success */
897   if (tr_idp_realm_get_apcs(idp) == NULL) {
898     tr_debug("trps_create_new_idp_realm: unable to set APC list for new realm.");
899     idp=NULL;
900     goto cleanup;
901   }
902
903   idp->origin=TR_REALM_DISCOVERED;
904   
905   talloc_steal(mem_ctx, idp);
906   
907 cleanup:
908   talloc_free(tmp_ctx);
909   return idp;
910 }
911
912 static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
913 {
914   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
915   TR_NAME *comm_id=trp_upd_get_comm(upd);
916   TR_NAME *realm_id=trp_upd_get_realm(upd);
917   TR_NAME *origin_id=NULL;
918   TR_NAME *our_peer_label=NULL;
919   TR_COMM *comm=NULL;
920   TR_RP_REALM *rp_realm=NULL;
921   TR_IDP_REALM *idp_realm=NULL;
922   struct timespec expiry={0,0};
923   TRP_RC rc=TRP_ERROR;
924
925   if ((comm_id==NULL) || (realm_id==NULL))
926     goto cleanup;
927
928   origin_id=trp_inforec_dup_origin(rec);
929   if (origin_id==NULL)
930     goto cleanup;
931     
932   /* see whether we want to add this */
933   our_peer_label=trps_dup_label(trps);
934   if (our_peer_label==NULL) {
935     tr_debug("trps_handle_inforec_comm: unable to allocate peer label.");
936     goto cleanup;
937   }
938
939   if (trps_name_in_provenance(our_peer_label, trp_inforec_get_provenance(rec)))
940     tr_debug("trps_handle_inforec_comm: rejecting community inforec to avoid provenance loop.");
941   else {
942     /* no loop occurring, accept the update */
943     comm=tr_comm_table_find_comm(trps->ctable, comm_id);
944     if (comm==NULL) {
945       tr_debug("trps_handle_inforec_comm: unknown community %.*s in inforec, creating it.",
946                comm_id->len, comm_id->buf);
947       comm=trps_create_new_comm(tmp_ctx, comm_id, rec);
948       if (comm==NULL) {
949         tr_debug("trps_handle_inforec_comm: unable to create new community.");
950         goto cleanup;
951       }
952       if (tr_comm_table_add_comm(trps->ctable, comm) != 0)
953       {
954         tr_debug("trps_handle_inforec_comm: unable to add community to community table.");
955         goto cleanup;
956       }
957     }
958     /* TODO: see if other comm data match the new inforec and update or complain */
959
960     trps_compute_expiry(trps, trp_inforec_get_interval(rec), &expiry);
961     if ((expiry.tv_sec==0)&&(expiry.tv_nsec==0))
962       goto cleanup;
963
964     switch (trp_inforec_get_role(rec)) {
965     case TR_ROLE_RP:
966       rp_realm=tr_rp_realm_lookup(trps->ctable->rp_realms, realm_id);
967       if (rp_realm==NULL) {
968         tr_debug("trps_handle_inforec_comm: unknown RP realm %.*s in inforec, creating it.",
969                  realm_id->len, realm_id->buf);
970         rp_realm= trps_create_new_rp_realm(tmp_ctx, tr_comm_get_id(comm), realm_id, rec);
971         if (rp_realm==NULL) {
972           tr_debug("trps_handle_inforec_comm: unable to create new RP realm.");
973           /* we may leave an unused community in the table, but it will only last until
974            * the next table sweep if it does not get any realms before that happens */
975           goto cleanup;
976         }
977         tr_comm_table_add_rp_realm(trps->ctable, rp_realm);
978       }
979       /* TODO: if realm existed, see if data match the new inforec and update or complain */
980       tr_comm_add_rp_realm(trps->ctable, comm, rp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry);
981       tr_debug("trps_handle_inforec_comm: added RP realm %.*s to comm %.*s (origin %.*s).",
982                realm_id->len, realm_id->buf,
983                comm_id->len, comm_id->buf,
984                origin_id->len, origin_id->buf);
985       break;
986     case TR_ROLE_IDP:
987       idp_realm=tr_idp_realm_lookup(trps->ctable->idp_realms, realm_id);
988       if (idp_realm==NULL) {
989         tr_debug("trps_handle_inforec_comm: unknown IDP realm %.*s in inforec, creating it.",
990                  realm_id->len, realm_id->buf);
991         idp_realm= trps_create_new_idp_realm(tmp_ctx, tr_comm_get_id(comm), realm_id, rec);
992         if (idp_realm==NULL) {
993           tr_debug("trps_handle_inforec_comm: unable to create new IDP realm.");
994           /* we may leave an unused community in the table, but it will only last until
995            * the next table sweep if it does not get any realms before that happens */
996           goto cleanup;
997         }
998         tr_comm_table_add_idp_realm(trps->ctable, idp_realm);
999       }
1000       /* TODO: if realm existed, see if data match the new inforec and update or complain */
1001       tr_comm_add_idp_realm(trps->ctable, comm, idp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry);
1002       tr_debug("trps_handle_inforec_comm: added IDP realm %.*s to comm %.*s (origin %.*s).",
1003                realm_id->len, realm_id->buf,
1004                comm_id->len, comm_id->buf,
1005                origin_id->len, origin_id->buf);
1006       break;
1007     default:
1008       tr_debug("trps_handle_inforec_comm: unable to add realm.");
1009       goto cleanup;
1010     }
1011   } 
1012
1013   rc=TRP_SUCCESS;
1014
1015 cleanup:
1016   if (our_peer_label!=NULL)
1017     tr_free_name(our_peer_label);
1018   if (origin_id!=NULL)
1019     tr_free_name(origin_id);
1020   talloc_free(tmp_ctx);
1021   return rc;
1022 }
1023
1024 /**
1025  * Apply applicable TRP_INBOUND filters to an inforec. Rejects everything if peer has no filters.
1026  *
1027  * @param trps Active TRPS instance
1028  * @param upd TRP_UPD that contains the inforec to filter
1029  * @param rec Inforec to filter
1030  * @return 1 if accepted by the filter, 0 otherwise
1031  */
1032 static int trps_filter_inbound_inforec(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
1033 {
1034   TRP_PEER *peer=NULL;
1035   TR_NAME *peer_name=NULL;
1036   TR_FILTER_ACTION action=TR_FILTER_ACTION_REJECT;
1037   TR_FILTER_TARGET *target=NULL;
1038   int retval=0;
1039
1040   /* Look up the peer. For inbound messages, the peer is identified by its GSS name */
1041   peer_name=trp_upd_get_peer(upd);
1042   peer=trps_get_peer_by_gssname(trps, peer_name);
1043   if (peer==NULL) {
1044     tr_err("trps_filter_inbound_inforec: received inforec from unknown peer (%.*s), rejecting.",
1045            peer_name->len,
1046            peer_name->buf);
1047     return 0;
1048   }
1049
1050   /* tr_filter_apply() and tr_filter_set_get() handle null filter sets/filters by rejecting */
1051   target= tr_filter_target_trp_inforec(NULL, upd, rec);
1052   if (target==NULL) {
1053     /* TODO: signal that filtering failed. Until then, just filter everything and give an error message. */
1054     tr_crit("trps_filter_inbound_inforec: Unable to allocate filter target, cannot apply filter!");
1055   }
1056   if ((target==NULL)
1057       || (TR_FILTER_NO_MATCH==tr_filter_apply(target,
1058                                               tr_filter_set_get(peer->filters, TR_FILTER_TYPE_TRP_INBOUND),
1059                                               NULL,
1060                                               &action))
1061       || (action!=TR_FILTER_ACTION_ACCEPT)) {
1062     /* either the filter did not match or it matched a reject rule or allocating the target failed */
1063     retval=0;
1064   } else
1065     retval=1;
1066   if (target!=NULL)
1067     tr_filter_target_free(target);
1068
1069   /* filter matched an accept rule */
1070   return retval;
1071 }
1072
1073
1074 static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
1075 {
1076   TRP_INFOREC *rec=NULL;
1077
1078   if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
1079     tr_notice("trps_handle_update: received invalid TRP update.");
1080     return TRP_ERROR;
1081   }
1082
1083   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
1084     /* validate/sanity check the record update */
1085     if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
1086       tr_notice("trps_handle_update: invalid inforec in TRP update, discarding entire update.");
1087       return TRP_ERROR;
1088     }
1089   }
1090
1091   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
1092     if (!trps_filter_inbound_inforec(trps, upd, rec)) {
1093       tr_debug("trps_handle_update: inforec rejected by filter.");
1094       continue; /* just go on to the next record */
1095     }
1096
1097     switch (trp_inforec_get_type(rec)) {
1098     case TRP_INFOREC_TYPE_ROUTE:
1099       tr_debug("trps_handle_update: handling route inforec.");
1100       if (TRP_SUCCESS!=trps_handle_inforec_route(trps, upd, rec))
1101         tr_notice("trps_handle_update: error handling route inforec.");
1102       break;
1103     case TRP_INFOREC_TYPE_COMMUNITY:
1104       tr_debug("trps_handle_update: handling community inforec.");
1105       if (TRP_SUCCESS!=trps_handle_inforec_comm(trps, upd, rec))
1106         tr_notice("trps_handle_update: error handling community inforec.");
1107
1108       break;
1109     default:
1110       tr_notice("trps_handle_update: unsupported inforec in TRP update.");
1111       break;
1112     }
1113   }
1114   return TRP_SUCCESS;
1115 }
1116
1117 static TRP_RC trps_validate_request(TRPS_INSTANCE *trps, TRP_REQ *req)
1118 {
1119   if (req==NULL) {
1120     tr_notice("trps_validate_request: null TRP request.");
1121     return TRP_BADARG;
1122   }
1123
1124   if (trp_req_get_comm(req)==NULL) {
1125     tr_notice("trps_validate_request: received TRP request with null community.");
1126     return TRP_ERROR;
1127   }
1128   
1129   if (trp_req_get_realm(req)==NULL) {
1130     tr_notice("trps_validate_request: received TRP request with null realm.");
1131     return TRP_ERROR;
1132   }
1133   
1134   if (trp_req_get_peer(req)==NULL) {
1135     tr_notice("trps_validate_request: received TRP request without origin peer information.");
1136     return TRP_ERROR;
1137   }
1138   
1139   return TRP_SUCCESS;
1140 }
1141
1142 /* choose the best route to comm/realm, optionally excluding routes to a particular peer */
1143 static TRP_ROUTE *trps_find_best_route(TRPS_INSTANCE *trps,
1144                                        TR_NAME *comm,
1145                                        TR_NAME *realm,
1146                                        TR_NAME *exclude_peer_label)
1147 {
1148   TRP_ROUTE **entry=NULL;
1149   TRP_ROUTE *best=NULL;
1150   TRP_PEER *route_peer = NULL;
1151   size_t n_entry=0;
1152   unsigned int kk=0;
1153   unsigned int kk_min=0;
1154   unsigned int min_metric=TRP_METRIC_INFINITY;
1155
1156   entry=trp_rtable_get_realm_entries(trps->rtable, comm, realm, &n_entry);
1157   for (kk=0; kk<n_entry; kk++) {
1158     if (trp_route_get_metric(entry[kk]) < min_metric) {
1159       if (exclude_peer_label != NULL) {
1160         if (!trp_route_is_local(entry[kk])) {
1161           /* route is not local, check the peer label */
1162           route_peer = trp_ptable_find_gss_name(trps->ptable,
1163                                                 trp_route_get_peer(entry[kk]));
1164           if (route_peer == NULL) {
1165             tr_err("trps_find_best_route: unknown peer GSS name (%.*s) for route %d to %.*s/%.*s",
1166                    trp_route_get_peer(entry[kk])->len, trp_route_get_peer(entry[kk])->buf,
1167                    kk,
1168                    realm->len, realm->buf,
1169                    comm->len, comm->buf);
1170             continue; /* unknown peer, skip the route */
1171           }
1172           if (0 == tr_name_cmp(exclude_peer_label, trp_peer_get_label(route_peer))) {
1173             /* we're excluding this peer - skip the route */
1174             continue;
1175           }
1176         }
1177       }
1178       /* if we get here, we're not excluding the route */
1179       kk_min = kk;
1180       min_metric = trp_route_get_metric(entry[kk]);
1181     }
1182   }
1183
1184   if (trp_metric_is_finite(min_metric))
1185     best=entry[kk_min];
1186   
1187   talloc_free(entry);
1188   return best;
1189 }
1190
1191 /* TODO: think this through more carefully. At least ought to add hysteresis
1192  * to avoid flapping between routers or routes. */
1193 TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
1194 {
1195   size_t n_comm=0, ii=0;
1196   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
1197   size_t n_realm=0, jj=0;
1198   TR_NAME **realm=NULL;
1199   TRP_ROUTE *best_route=NULL, *cur_route=NULL;
1200   unsigned int best_metric=0, cur_metric=0;
1201
1202   for (ii=0; ii<n_comm; ii++) {
1203     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
1204     for (jj=0; jj<n_realm; jj++) {
1205       best_route=trps_find_best_route(trps, comm[ii], realm[jj], NULL);
1206       if (best_route==NULL)
1207         best_metric=TRP_METRIC_INFINITY;
1208       else
1209         best_metric=trp_route_get_metric(best_route);
1210
1211       cur_route=trps_get_selected_route(trps, comm[ii], realm[jj]);
1212       if (cur_route!=NULL) {
1213         cur_metric=trp_route_get_metric(cur_route);
1214         if ((best_metric < cur_metric) && (trp_metric_is_finite(best_metric))) {
1215           /* The new route has a lower metric than the previous, and is finite. Accept. */
1216           trp_route_set_selected(cur_route, 0);
1217           trp_route_set_selected(best_route, 1);
1218         } else if (!trp_metric_is_finite(cur_metric)) /* rejects infinite or invalid metrics */
1219           trp_route_set_selected(cur_route, 0);
1220       } else if (trp_metric_is_finite(best_metric)) {
1221         trp_route_set_selected(best_route, 1);
1222       }
1223     }
1224     if (realm!=NULL)
1225       talloc_free(realm);
1226     realm=NULL; n_realm=0;
1227   }
1228   if (comm!=NULL)
1229     talloc_free(comm);
1230   comm=NULL; n_comm=0;
1231
1232   return TRP_SUCCESS;
1233 }
1234
1235 /* true if curtime >= expiry */
1236 static int trps_expired(struct timespec *expiry, struct timespec *curtime)
1237 {
1238   return (tr_cmp_timespec(curtime, expiry) >= 0);
1239 }
1240
1241 /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
1242  * If its metric is finite, the metric is set to infinite and the route's expiration time is updated. */
1243 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
1244 {
1245   struct timespec sweep_time={0,0};
1246   TRP_ROUTE **entry=NULL;
1247   size_t n_entry=0;
1248   size_t ii=0;
1249
1250   /* use a single time for the entire sweep */
1251   if (0!=clock_gettime(TRP_CLOCK, &sweep_time)) {
1252     tr_err("trps_sweep_routes: could not read realtime clock.");
1253     sweep_time.tv_sec=0;
1254     sweep_time.tv_nsec=0;
1255     return TRP_ERROR;
1256   }
1257
1258   entry= trp_rtable_get_entries(NULL, trps->rtable, &n_entry); /* must talloc_free *entry */
1259
1260   /* loop over the entries */
1261   for (ii=0; ii<n_entry; ii++) {
1262     if (!trp_route_is_local(entry[ii]) && trps_expired(trp_route_get_expiry(entry[ii]), &sweep_time)) {
1263       tr_debug("trps_sweep_routes: route expired.");
1264       if (!trp_metric_is_finite(trp_route_get_metric(entry[ii]))) {
1265         /* flush route */
1266         tr_debug("trps_sweep_routes: metric was infinity, flushing route.");
1267         trp_rtable_remove(trps->rtable, entry[ii]); /* entry[ii] is no longer valid */
1268         entry[ii]=NULL;
1269       } else {
1270         /* set metric to infinity and reset timer */
1271         tr_debug("trps_sweep_routes: setting metric to infinity and resetting expiry.");
1272         trp_route_set_metric(entry[ii], TRP_METRIC_INFINITY);
1273         trp_route_set_expiry(entry[ii], trps_compute_expiry(trps,
1274                                                              trp_route_get_interval(entry[ii]),
1275                                                              trp_route_get_expiry(entry[ii])));
1276       }
1277     }
1278   }
1279
1280   talloc_free(entry);
1281   return TRP_SUCCESS;
1282 }
1283
1284
1285 /* Sweep for expired communities/realms/memberships. */
1286 TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
1287 {
1288   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1289   struct timespec sweep_time={0,0};
1290   struct timespec tmp = {0};
1291   TR_COMM_MEMB *memb=NULL;
1292   TR_COMM_ITER *iter=NULL;
1293   TRP_RC rc=TRP_ERROR;
1294
1295   /* use a single time for the entire sweep */
1296   if (0!=clock_gettime(TRP_CLOCK, &sweep_time)) {
1297     tr_err("trps_sweep_ctable: could not read realtime clock.");
1298     sweep_time.tv_sec=0;
1299     sweep_time.tv_nsec=0;
1300     goto cleanup;
1301   }
1302
1303   /* iterate all memberships */
1304   iter=tr_comm_iter_new(tmp_ctx);
1305   if (iter==NULL) {
1306     tr_err("trps_sweep_ctable: unable to allocate iterator.");
1307     rc=TRP_NOMEM;
1308     goto cleanup;
1309   }
1310   for (memb=tr_comm_memb_iter_all_first(iter, trps->ctable);
1311        memb!=NULL;
1312        memb=tr_comm_memb_iter_all_next(iter)) {
1313     if (tr_comm_memb_get_origin(memb)==NULL)
1314       continue; /* do not expire local entries */
1315
1316     if (tr_comm_memb_is_expired(memb, &sweep_time)) {
1317       if (tr_comm_memb_get_times_expired(memb)>0) {
1318         /* Already expired once; flush. */
1319         tr_debug("trps_sweep_ctable: flushing expired community membership (%.*s in %.*s, origin %.*s, expired %s).",
1320                  tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
1321                  tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
1322                  tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf,
1323                  timespec_to_str(tr_comm_memb_get_expiry_realtime(memb, &tmp)));
1324         tr_comm_table_remove_memb(trps->ctable, memb);
1325         tr_comm_memb_free(memb);
1326       } else {
1327         /* This is the first expiration. Note this and reset the expiry time. */
1328         tr_comm_memb_expire(memb);
1329         trps_compute_expiry(trps, tr_comm_memb_get_interval(memb), tr_comm_memb_get_expiry(memb));
1330         tr_debug("trps_sweep_ctable: community membership expired at %s, resetting expiry to %s (%.*s in %.*s, origin %.*s).",
1331                  timespec_to_str(tr_clock_convert(TRP_CLOCK, &sweep_time, CLOCK_REALTIME, &tmp)),
1332                  timespec_to_str(tr_comm_memb_get_expiry_realtime(memb, &tmp)),
1333                  tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
1334                  tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
1335                  tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf);
1336       }
1337     }
1338   }
1339
1340   /* get rid of any unreferenced realms, etc */
1341   tr_comm_table_sweep(trps->ctable);
1342
1343 cleanup:
1344   talloc_free(tmp_ctx);
1345   return rc;
1346 }
1347
1348 /* add metrics */
1349 static unsigned int trps_metric_add(unsigned int m1, unsigned int m2)
1350 {
1351   if (trp_metric_is_invalid(m1) || trp_metric_is_invalid(m2))
1352     return TRP_METRIC_INVALID;
1353
1354   if (trp_metric_is_infinite(m1) || trp_metric_is_infinite(m2))
1355     return TRP_METRIC_INFINITY;
1356
1357   if (trp_metric_is_finite(m1+m2))
1358     return m1+m2;
1359   else
1360     return TRP_METRIC_INFINITY;
1361 }
1362
1363 /* convert an rentry into a new trp update info record */
1364 static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
1365 {
1366   TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE);
1367   unsigned int linkcost=0;
1368
1369   if (rec!=NULL) {
1370     if (trp_route_is_local(route))
1371       linkcost=0;
1372     else {
1373       linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps,
1374                                                               trp_route_get_peer(route)));
1375     }
1376
1377     /*
1378      * This is where we add the link cost (currently always 1) to the next peer.
1379      *
1380      * Here, set next_hop to our TID address/port rather than passing along our own
1381      * next_hop. That is the one *we* use to forward requests. We are advertising
1382      * ourselves as a hop for our peers.
1383      */
1384     if ((TRP_SUCCESS != trp_inforec_set_trust_router(rec,
1385                                                      trp_route_dup_trust_router(route),
1386                                                      trp_route_get_trust_router_port(route)))
1387         ||(TRP_SUCCESS != trp_inforec_set_next_hop(rec,
1388                                                    tr_new_name(trps->hostname),
1389                                                    trps->tids_port))
1390         ||(TRP_SUCCESS != trp_inforec_set_metric(rec,
1391                                                  trps_metric_add(trp_route_get_metric(route),
1392                                                                  linkcost)))
1393         ||(TRP_SUCCESS != trp_inforec_set_interval(rec, trps_get_update_interval(trps)))) {
1394       tr_err("trps_route_to_inforec: error creating route update.");
1395       talloc_free(rec);
1396       rec=NULL;
1397     }
1398   }
1399   return rec;
1400 }
1401
1402 static TRP_UPD *trps_route_to_upd(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
1403 {
1404   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1405   TRP_UPD *upd=trp_upd_new(tmp_ctx);
1406   TRP_INFOREC *rec=NULL;
1407
1408   if (upd==NULL) {
1409     tr_err("trps_route_to_upd: could not create update message.");
1410     goto cleanup;
1411   }
1412   trp_upd_set_realm(upd, trp_route_dup_realm(route));
1413   if (trp_upd_get_realm(upd)==NULL) {
1414     tr_err("trps_route_to_upd: could not copy realm.");
1415     upd=NULL; /* it's still in tmp_ctx, so it will be freed */
1416     goto cleanup;
1417   }
1418   trp_upd_set_comm(upd, trp_route_dup_comm(route));
1419   if (trp_upd_get_comm(upd)==NULL) {
1420     tr_err("trps_route_to_upd: could not copy comm.");
1421     upd=NULL; /* it's still in tmp_ctx, so it will be freed */
1422     goto cleanup;
1423   }
1424   rec=trps_route_to_inforec(tmp_ctx, trps, route);
1425   if (rec==NULL) {
1426     tr_err("trps_route_to_upd: could not create route info record for realm %.*s in comm %.*s.",
1427            trp_route_get_realm(route)->len, trp_route_get_realm(route)->buf,
1428            trp_route_get_comm(route)->len, trp_route_get_comm(route)->buf);
1429     upd=NULL; /* it's till in tmp_ctx, so it will be freed */
1430     goto cleanup;
1431   }
1432   trp_upd_add_inforec(upd, rec);
1433
1434   /* sucess */
1435   talloc_steal(mem_ctx, upd);
1436
1437 cleanup:
1438   talloc_free(tmp_ctx);
1439   return upd;
1440 }
1441
1442 /* select the correct route to comm/realm to be announced to peer */
1443 static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_label)
1444 {
1445   TRP_ROUTE *route = NULL;
1446   TRP_PEER *route_peer = NULL;
1447   TR_NAME *route_peer_label = NULL;
1448
1449   /* Take the currently selected route unless it is through the peer we're sending the update to.
1450    * I.e., enforce the split horizon rule. Start by looking up the currently selected route. */
1451   route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
1452   if (route==NULL) {
1453     /* No selected route, this should only happen if the only route has been retracted,
1454      * in which case we do not want to advertise it. */
1455     return NULL;
1456   }
1457
1458   /* Check whether it's local. */
1459   if (trp_route_is_local(route)) {
1460     /* It is always ok to announce a local route */
1461     tr_debug("trps_select_realm_update: selected route for %.*s/%.*s is local",
1462              realm->len, realm->buf,
1463              comm->len, comm->buf);
1464   } else {
1465     /* It's not local. Get the route's peer and check whether it's the same place we
1466      * got the selected route from. Peer should always correspond to an entry in our
1467      * peer table. */
1468     tr_debug("trps_select_realm_update: selected route for %.*s/%.*s is not local",
1469              realm->len, realm->buf,
1470              comm->len, comm->buf);
1471     route_peer = trp_ptable_find_gss_name(trps->ptable, trp_route_get_peer(route));
1472     if (route_peer == NULL) {
1473       tr_err("trps_select_realm_update: unknown peer GSS name (%.*s) for selected route for %.*s/%.*s",
1474              trp_route_get_peer(route)->len, trp_route_get_peer(route)->buf,
1475              realm->len, realm->buf,
1476              comm->len, comm->buf);
1477       return NULL;
1478     }
1479     route_peer_label = trp_peer_get_label(route_peer);
1480     if (route_peer_label == NULL) {
1481       tr_err("trps_select_realm_update: error retrieving peer label for selected route for %.*s/%.*s",
1482              realm->len, realm->buf,
1483              comm->len, comm->buf);
1484       return NULL;
1485     }
1486
1487     /* see if these match */
1488     tr_debug("trps_select_realm_update: %.*s vs %.*s",
1489              peer_label->len, peer_label->buf,
1490              route_peer_label->len, route_peer_label->buf);
1491
1492     if (0==tr_name_cmp(peer_label, route_peer_label)) {
1493       /* the selected entry goes through the peer we're reporting to, choose an alternate */
1494       tr_debug("trps_select_realm_update: matched, finding alternate route");
1495       route=trps_find_best_route(trps, comm, realm, peer_label);
1496       if ((route==NULL) || (!trp_metric_is_finite(trp_route_get_metric(route)))) {
1497         tr_debug("trps_select_realm_update: no route to %.*s/%.*s suitable to advertise to %.*s",
1498                  realm->len, realm->buf,
1499                  comm->len, comm->buf,
1500                  peer_label->len, peer_label->buf);
1501         return NULL; /* don't advertise a nonexistent or retracted route */
1502       }
1503     }
1504   }
1505   return route;
1506 }
1507
1508 /* Add TRP_UPD msgs to the updates GPtrArray. Caller needs to arrange for these to be freed. */
1509 static TRP_RC trps_select_route_updates_for_peer(TALLOC_CTX *mem_ctx,
1510                                                  GPtrArray *updates,
1511                                                  TRPS_INSTANCE *trps,
1512                                                  TR_NAME *peer_label,
1513                                                  int triggered)
1514 {
1515   size_t n_comm=0;
1516   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
1517   TR_NAME **realm=NULL;
1518   size_t n_realm=0;
1519   size_t ii=0, jj=0;
1520   TRP_ROUTE *best=NULL;
1521   TRP_UPD *upd=NULL;
1522
1523   if (updates==NULL)
1524     return TRP_BADARG;
1525
1526   for (ii=0; ii<n_comm; ii++) {
1527     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
1528     for (jj=0; jj<n_realm; jj++) {
1529       best=trps_select_realm_update(trps, comm[ii], realm[jj], peer_label);
1530       /* If we found a route, add it to the list. If triggered!=0, then only
1531        * add triggered routes. */
1532       if ((best!=NULL) && ((!triggered) || trp_route_is_triggered(best))) {
1533         upd=trps_route_to_upd(mem_ctx, trps, best);
1534         if (upd==NULL) {
1535           tr_err("trps_select_route_updates_for_peer: unable to create update message.");
1536           continue;
1537         }
1538         g_ptr_array_add(updates, upd);
1539       }
1540     }
1541     
1542     if (realm!=NULL)
1543       talloc_free(realm);
1544     realm=NULL;
1545     n_realm=0;
1546   }
1547
1548   if (comm!=NULL)
1549     talloc_free(comm);
1550   
1551   return TRP_SUCCESS;
1552 }
1553
1554 static TRP_INFOREC *trps_memb_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TR_COMM_MEMB *memb)
1555 {
1556   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1557   TRP_INFOREC *rec=NULL;
1558   TR_COMM *comm=NULL;
1559
1560   if (memb==NULL)
1561     goto cleanup;
1562
1563   comm=tr_comm_memb_get_comm(memb);
1564   rec=trp_inforec_new(tmp_ctx, TRP_INFOREC_TYPE_COMMUNITY);
1565   if (rec==NULL)
1566     goto cleanup;
1567   
1568   if (TRP_SUCCESS!=trp_inforec_set_comm_type(rec, tr_comm_get_type(comm))) {
1569     rec=NULL;
1570     goto cleanup;
1571   }
1572   
1573   if (TRP_SUCCESS!=trp_inforec_set_role(rec, tr_comm_memb_get_role(memb))) {
1574     rec=NULL;
1575     goto cleanup;
1576   }
1577
1578   if ((NULL!=tr_comm_get_apcs(comm)) &&
1579       ( (TRP_SUCCESS!=trp_inforec_set_apcs(rec,
1580                                            tr_apc_dup(rec, tr_comm_get_apcs(comm)))) ||
1581         (NULL==trp_inforec_get_apcs(rec)))) {
1582     rec=NULL;
1583     goto cleanup;
1584   }
1585
1586   if ((NULL!=tr_comm_get_owner_realm(comm)) &&
1587       ( (TRP_SUCCESS!=trp_inforec_set_owner_realm(rec, tr_dup_name(tr_comm_get_owner_realm(comm)))) ||
1588         (NULL==trp_inforec_get_owner_realm(rec)))) {
1589     rec=NULL;
1590     goto cleanup;
1591   }
1592
1593   if ((NULL!=tr_comm_get_owner_contact(comm)) &&
1594       ( (TRP_SUCCESS!=trp_inforec_set_owner_contact(rec, tr_dup_name(tr_comm_get_owner_contact(comm)))) ||
1595         (NULL==trp_inforec_get_owner_contact(rec)))) {
1596     rec=NULL;
1597     goto cleanup;
1598   }
1599
1600   if ((NULL!=tr_comm_memb_get_provenance(memb)) &&
1601       (TRP_SUCCESS!=trp_inforec_set_provenance(rec, tr_comm_memb_get_provenance(memb)))) {
1602     rec=NULL;
1603     goto cleanup;
1604   }
1605
1606   if (TRP_SUCCESS!=trp_inforec_set_interval(rec, trps_get_update_interval(trps))) {
1607     rec=NULL;
1608     goto cleanup;
1609   }
1610
1611   /* success! */
1612   talloc_steal(mem_ctx, rec);
1613
1614 cleanup:
1615   talloc_free(tmp_ctx);
1616   return rec;
1617 }
1618
1619 /* construct an update with all the inforecs for comm/realm/role to be sent to peer */
1620 static TRP_UPD *trps_comm_update(TALLOC_CTX *mem_ctx,
1621                                  TRPS_INSTANCE *trps,
1622                                  TR_NAME *peer_label,
1623                                  TR_COMM *comm,
1624                                  TR_REALM *realm)
1625 {
1626   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1627   TRP_UPD *upd=trp_upd_new(tmp_ctx);
1628   TRP_INFOREC *rec=NULL;
1629   TR_COMM_ITER *iter=NULL;
1630   TR_COMM_MEMB *memb=NULL;
1631
1632   if (upd==NULL)
1633     goto cleanup;
1634   
1635   trp_upd_set_comm(upd, tr_comm_dup_id(comm));
1636   trp_upd_set_realm(upd, tr_realm_dup_id(realm));
1637   /* leave peer empty */
1638
1639   iter=tr_comm_iter_new(tmp_ctx);
1640   if (iter==NULL) {
1641     tr_err("trps_comm_update: unable to allocate iterator.");
1642     upd=NULL;
1643     goto cleanup;
1644   }
1645   
1646   /* now add inforecs */
1647   switch (realm->role) {
1648   case TR_ROLE_IDP:
1649     memb=tr_comm_table_find_idp_memb(trps->ctable,
1650                                      tr_realm_get_id(realm),
1651                                      tr_comm_get_id(comm));
1652     break;
1653   case TR_ROLE_RP:
1654     memb=tr_comm_table_find_rp_memb(trps->ctable,
1655                                     tr_realm_get_id(realm),
1656                                     tr_comm_get_id(comm));
1657     break;
1658   default:
1659     break;
1660   }
1661   if (memb!=NULL) {
1662     for (memb=tr_comm_memb_iter_first(iter, memb);
1663          memb!=NULL;
1664          memb=tr_comm_memb_iter_next(iter)) {
1665       rec=trps_memb_to_inforec(tmp_ctx, trps, memb);
1666       if (rec==NULL) {
1667         tr_err("trps_comm_update: unable to allocate inforec.");
1668         upd=NULL;
1669         goto cleanup;
1670       }
1671       trp_upd_add_inforec(upd, rec);
1672     }
1673   }
1674
1675   if (trp_upd_get_inforec(upd)==NULL)
1676     upd=NULL; /* no inforecs, no reason to send the update */
1677   else
1678     talloc_steal(mem_ctx, upd); /* success! */
1679
1680 cleanup:
1681   talloc_free(tmp_ctx);
1682   return upd;
1683 }
1684
1685 /* Find all community updates to send to a peer and add these as TR_UPD records
1686  * to the updates GPtrArray. */
1687 static TRP_RC trps_select_comm_updates_for_peer(TALLOC_CTX *mem_ctx,
1688                                                 GPtrArray *updates,
1689                                                 TRPS_INSTANCE *trps,
1690                                                 TR_NAME *peer_label,
1691                                                 int triggered)
1692 {
1693   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1694   TR_COMM_ITER *comm_iter=NULL;
1695   TR_COMM *comm=NULL;
1696   TR_COMM_ITER *realm_iter=NULL;
1697   TR_REALM *realm=NULL;
1698   TRP_UPD *upd=NULL;
1699   TRP_RC rc=TRP_ERROR;
1700
1701   /* currently do not send any communities on triggered updates */
1702   if (triggered) {
1703     rc=TRP_SUCCESS;
1704     goto cleanup;
1705   }
1706
1707   comm_iter=tr_comm_iter_new(tmp_ctx);
1708   realm_iter=tr_comm_iter_new(tmp_ctx);
1709   if ((comm_iter==NULL) || (realm_iter==NULL)) {
1710     tr_err("trps_select_comm_updates_for_peer: unable to allocate iterator.");
1711     rc=TRP_NOMEM;
1712     goto cleanup;
1713   }
1714
1715   /* do every community */
1716   for (comm=tr_comm_table_iter_first(comm_iter, trps->ctable);
1717        comm!=NULL;
1718        comm=tr_comm_table_iter_next(comm_iter)) {
1719     /* do every realm in this community */
1720     tr_debug("trps_select_comm_updates_for_peer: looking through community %.*s",
1721              tr_comm_get_id(comm)->len,
1722              tr_comm_get_id(comm)->buf);
1723     for (realm=tr_realm_iter_first(realm_iter, trps->ctable, tr_comm_get_id(comm));
1724          realm!=NULL;
1725          realm=tr_realm_iter_next(realm_iter)) {
1726       /* get the update for this comm/realm */
1727       tr_debug("trps_select_comm_updates_for_peer: adding realm %.*s",
1728                tr_realm_get_id(realm)->len,
1729                tr_realm_get_id(realm)->buf);
1730       upd=trps_comm_update(mem_ctx, trps, peer_label, comm, realm);
1731       if (upd!=NULL)
1732         g_ptr_array_add(updates, upd);
1733     }
1734   }
1735
1736 cleanup:
1737   talloc_free(tmp_ctx);
1738   return rc;
1739 }
1740
1741 /**
1742  * Filter the inforecs in a single update
1743  *
1744  * @param filt The filter to apply
1745  * @param upd The update to filter
1746  */
1747 static void trps_filter_one_outbound_update(TR_FILTER *filt, TRP_UPD *upd)
1748 {
1749   TRP_INFOREC *this=NULL, *next=NULL;
1750   TR_FILTER_ACTION action=TR_FILTER_ACTION_REJECT;
1751   TR_FILTER_TARGET *target=NULL;
1752
1753   for(this=trp_upd_get_inforec(upd); this!=NULL; this=next) {
1754     next=this->next;
1755     target= tr_filter_target_trp_inforec(NULL, upd, this);
1756     if (target==NULL) {
1757       /* TODO: signal that filtering failed. Until then, just filter everything and give an error message. */
1758       tr_crit("trps_filter_one_outbound_update: Unable to allocate filter target, cannot apply filter!");
1759     }
1760     if ((target==NULL)
1761         || (TR_FILTER_NO_MATCH==tr_filter_apply(target, filt, NULL, &action))
1762         || (action!=TR_FILTER_ACTION_ACCEPT)) {
1763       /* Either no filter matched or one matched and rejected this record.
1764        * Also filter out record if we were unable to allocate a target. */
1765       trp_upd_remove_inforec(upd, this); /* "this" is now invalid */
1766     }
1767     if (target!=NULL)
1768       tr_filter_target_free(target);
1769   }
1770 }
1771
1772 /**
1773  * May shuffle the update list.
1774  *
1775  * @param filters The filter set for the relevant TRP peer
1776  * @param updates GPtrArray of updates to filter
1777  */
1778 static void trps_filter_outbound_updates(TR_FILTER_SET *filters, GPtrArray *updates)
1779 {
1780   TRP_UPD *upd=NULL;
1781   guint ii=0;
1782
1783   /* Walk backward through the array so we can remove elements. Careful about loop
1784    * termination - remember that ii is unsigned. */
1785   for (ii=updates->len; ii>0; ii--) {
1786     upd=g_ptr_array_index(updates, ii-1);
1787     trps_filter_one_outbound_update(tr_filter_set_get(filters, TR_FILTER_TYPE_TRP_OUTBOUND), upd);
1788     /* see if we removed all the records from this update */
1789     if (trp_upd_num_inforecs(upd)==0)
1790       g_ptr_array_remove_index_fast(updates, ii-1); /* does not preserve order at index ii or higher */
1791   }
1792 }
1793
1794 /* helper for trps_update_one_peer. Frees the TRP_UPD pointed to by a GPtrArray element */
1795 static void trps_trp_upd_destroy(gpointer data)
1796 {
1797   trp_upd_free((TRP_UPD *)data);
1798 }
1799
1800 /* all routes/communities to a single peer, unless comm/realm are specified (both or neither must be NULL) */
1801 static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
1802                                    TRP_PEER *peer,
1803                                    TRP_UPDATE_TYPE update_type,
1804                                    TR_NAME *realm,
1805                                    TR_NAME *comm)
1806 {
1807   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1808   TR_MSG msg; /* not a pointer! */
1809   TRP_UPD *upd=NULL;
1810   TRP_ROUTE *route=NULL;
1811   size_t ii=0;
1812   char *encoded=NULL;
1813   TRP_RC rc=TRP_ERROR;
1814   TR_NAME *peer_label=trp_peer_get_label(peer);
1815   GPtrArray *updates=g_ptr_array_new_with_free_func(trps_trp_upd_destroy);
1816
1817   if (updates==NULL) {
1818     tr_err("trps_update_one_peer: unable to allocate updates array.");
1819     rc=TRP_NOMEM;
1820     goto cleanup;
1821   }
1822
1823   switch (update_type) {
1824   case TRP_UPDATE_TRIGGERED:
1825     tr_debug("trps_update_one_peer: preparing triggered update for %.*s",
1826              peer_label->len, peer_label->buf);
1827     break;
1828   case TRP_UPDATE_SCHEDULED:
1829     tr_debug("trps_update_one_peer: preparing scheduled update for %.*s",
1830              peer_label->len, peer_label->buf);
1831     break;
1832   case TRP_UPDATE_REQUESTED:
1833     tr_debug("trps_update_one_peer: preparing requested update for %.*s",
1834              peer_label->len, peer_label->buf);
1835     break;
1836   default:
1837     tr_err("trps_update_one_peer: invalid update type requested.");
1838     rc=TRP_BADARG;
1839     goto cleanup;
1840   }
1841
1842   /* First, gather route updates. */
1843   tr_debug("trps_update_one_peer: selecting route updates for %.*s.", peer_label->len, peer_label->buf);
1844   if ((comm==NULL) && (realm==NULL)) {
1845     /* do all realms */
1846     rc=trps_select_route_updates_for_peer(tmp_ctx,
1847                                           updates,
1848                                           trps,
1849                                           peer_label,
1850                                           update_type==TRP_UPDATE_TRIGGERED);
1851   } else if ((comm!=NULL) && (realm!=NULL)) {
1852     /* a single community/realm was requested */
1853     route=trps_select_realm_update(trps, comm, realm, peer_label);
1854     if (route==NULL) {
1855       /* we have no actual update to send back, MUST send a retraction */
1856       tr_debug("trps_update_one_peer: community/realm without route requested, sending mandatory retraction.");
1857       route=trp_route_new(tmp_ctx);
1858       trp_route_set_comm(route, tr_dup_name(comm));
1859       trp_route_set_realm(route, tr_dup_name(realm));
1860       trp_route_set_peer(route, tr_new_name(""));
1861       trp_route_set_metric(route, TRP_METRIC_INFINITY);
1862       trp_route_set_trust_router(route, tr_new_name(""));
1863       trp_route_set_next_hop(route, tr_new_name(""));
1864     }
1865     upd=trps_route_to_upd(tmp_ctx, trps, route);
1866     if (upd==NULL) {
1867       tr_err("trps_update_one_peer: unable to allocate route update.");
1868       rc=TRP_NOMEM;
1869       goto cleanup;
1870     }
1871     g_ptr_array_add(updates, upd);
1872   } else {
1873     tr_err("trps_update_one_peer: error: only comm or realm was specified. Need both or neither.");
1874     rc=TRP_ERROR;
1875     goto cleanup;
1876   }
1877
1878   /* Second, gather community updates */
1879   tr_debug("trps_update_one_peer: selecting community updates for %.*s.", peer_label->len, peer_label->buf);
1880   rc=trps_select_comm_updates_for_peer(tmp_ctx, updates, trps, peer_label, update_type==TRP_UPDATE_TRIGGERED);
1881
1882   /* see if we have anything to send */
1883   if (updates->len<=0)
1884     tr_debug("trps_update_one_peer: no updates for %.*s", peer_label->len, peer_label->buf);
1885   else {
1886     /* Apply outbound TRP filters for this peer */
1887     trps_filter_outbound_updates(peer->filters, updates);
1888
1889     if (updates->len<=0)
1890       tr_debug("trps_update_one_peer: no updates for %.*s after filtering.", peer_label->len, peer_label->buf);
1891     else {
1892       tr_debug("trps_update_one_peer: sending %d update messages.", updates->len);
1893       for (ii=0; ii<updates->len; ii++) {
1894         upd = (TRP_UPD *) g_ptr_array_index(updates, ii);
1895         /* now encode the update message */
1896         tr_msg_set_trp_upd(&msg, upd);
1897         encoded = tr_msg_encode(NULL, &msg);
1898         if (encoded == NULL) {
1899           tr_err("trps_update_one_peer: error encoding update.");
1900           rc = TRP_ERROR;
1901           goto cleanup;
1902         }
1903
1904         tr_debug("trps_update_one_peer: adding message to queue.");
1905         if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS)
1906           tr_err("trps_update_one_peer: error queueing update.");
1907         else
1908           tr_debug("trps_update_one_peer: update queued successfully.");
1909
1910         tr_msg_free_encoded(encoded);
1911         encoded = NULL;
1912       }
1913     }
1914   }
1915
1916   rc=TRP_SUCCESS;
1917
1918 cleanup:
1919   if (updates!=NULL)
1920     g_ptr_array_free(updates, TRUE); /* frees any TRP_UPD records */
1921   talloc_free(tmp_ctx);
1922   return rc;
1923 }
1924
1925 /* all routes/communities to all peers */
1926 TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type)
1927 {
1928   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1929   TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
1930   TRP_PEER *peer=NULL;
1931   TRP_RC rc=TRP_SUCCESS;
1932
1933   if (trps->ptable==NULL)
1934     return TRP_SUCCESS; /* no peers, nothing to do */
1935
1936   if (iter==NULL) {
1937     tr_err("trps_update: failed to allocate peer table iterator.");
1938     talloc_free(tmp_ctx);
1939     return TRP_NOMEM;
1940   }
1941
1942   for (peer=trp_ptable_iter_first(iter, trps->ptable);
1943        (peer!=NULL) && (rc==TRP_SUCCESS);
1944        peer=trp_ptable_iter_next(iter))
1945   {
1946     if (!trps_peer_connected(trps, peer)) {
1947       TR_NAME *peer_label=trp_peer_get_label(peer);
1948       tr_debug("trps_update: no TRP connection to %.*s, skipping.",
1949                peer_label->len, peer_label->buf);
1950       continue;
1951     }
1952     rc=trps_update_one_peer(trps, peer, update_type, NULL, NULL);
1953   }
1954
1955   tr_debug("trps_update: rc=%u after attempting update.", rc);
1956   trp_ptable_iter_free(iter);
1957   trp_rtable_clear_triggered(trps->rtable); /* don't re-send triggered updates */
1958   talloc_free(tmp_ctx);
1959   return rc;
1960 }        
1961
1962 TRP_RC trps_add_route(TRPS_INSTANCE *trps, TRP_ROUTE *route)
1963 {
1964   trp_rtable_add(trps->rtable, route); /* should return status */
1965   return TRP_SUCCESS; 
1966 }
1967
1968 /* steals the peer object */
1969 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
1970 {
1971   if (trps->ptable==NULL) {
1972     trps->ptable=trp_ptable_new(trps);
1973     if (trps->ptable==NULL)
1974       return TRP_NOMEM;
1975   }
1976   return trp_ptable_add(trps->ptable, peer);
1977 }
1978
1979 TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname)
1980 {
1981   if (trps->ptable==NULL)
1982     return NULL;
1983
1984   return trp_ptable_find_gss_name(trps->ptable, gssname);
1985 }
1986
1987 TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename)
1988 {
1989   if (trps->ptable==NULL)
1990     return NULL;
1991
1992   return trp_ptable_find_servicename(trps->ptable, servicename);
1993 }
1994
1995 int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer)
1996 {
1997   TRPC_INSTANCE *trpc=trps_find_trpc(trps, peer);
1998   if (trpc==NULL)
1999     return 0;
2000
2001   if (trpc_get_status(trpc)==TRP_CONNECTION_UP)
2002     return 1;
2003   else
2004     return 0;
2005 }
2006
2007
2008 static TRP_RC trps_handle_request(TRPS_INSTANCE *trps, TRP_REQ *req)
2009 {
2010   TR_NAME *comm=NULL;
2011   TR_NAME *realm=NULL;
2012
2013   tr_debug("trps_handle_request: handling TRP request.");
2014
2015   if (trps_validate_request(trps, req) != TRP_SUCCESS) {
2016     tr_notice("trps_handle_request: received invalid TRP request.");
2017     return TRP_ERROR;
2018   }
2019
2020   if (!trp_req_is_wildcard(req)) {
2021     comm=trp_req_get_comm(req);
2022     realm=trp_req_get_realm(req);
2023     tr_debug("trps_handle_request: route for %.*s/%.*s requested.",
2024              comm->len, comm->buf, realm->len, realm->buf);
2025   } else {
2026     tr_debug("trps_handle_request: all routes requested.");
2027     /* leave comm/realm NULL */
2028   }
2029   return trps_update_one_peer(trps,
2030                               trps_get_peer_by_gssname(trps, trp_req_get_peer(req)),
2031                               TRP_UPDATE_REQUESTED,
2032                               realm,
2033                               comm);
2034 }
2035
2036
2037 TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
2038 {
2039   TRP_RC rc=TRP_ERROR;
2040
2041   switch (tr_msg_get_msg_type(tr_msg)) {
2042   case TRP_UPDATE:
2043     rc=trps_handle_update(trps, tr_msg_get_trp_upd(tr_msg));
2044     if (rc==TRP_SUCCESS) {
2045       rc=trps_update_active_routes(trps);
2046       trps_update(trps, TRP_UPDATE_TRIGGERED); /* send any triggered routes */
2047     }
2048     return rc;
2049
2050   case TRP_REQUEST:
2051     rc=trps_handle_request(trps, tr_msg_get_trp_req(tr_msg));
2052     return rc;
2053
2054   default:
2055     /* unknown error or one we don't care about (e.g., TID messages) */
2056     return TRP_ERROR;
2057   }
2058 }
2059
2060 /* send wildcard route request to a peer */
2061 TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_servicename)
2062 {
2063   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
2064   TRP_PEER *peer=trps_get_peer_by_servicename(trps, peer_servicename);
2065   TR_MSG msg; /* not a pointer */
2066   TRP_REQ *req=trp_req_new(tmp_ctx);
2067   char *encoded=NULL;
2068   TRP_RC rc=TRP_ERROR;
2069
2070   if (peer==NULL) {
2071     tr_err("trps_wildcard_route_req: unknown peer (%.*s).", peer_servicename->len, peer_servicename->buf);
2072     rc=TRP_BADARG;
2073     goto cleanup;
2074   }
2075   if ((req==NULL) || (trp_req_make_wildcard(req)!=TRP_SUCCESS)) {
2076     tr_err("trps_wildcard_route_req: unable to create wildcard TRP request.");
2077     rc=TRP_NOMEM;
2078     goto cleanup;
2079   }
2080
2081   tr_msg_set_trp_req(&msg, req);
2082   encoded= tr_msg_encode(NULL, &msg);
2083   if (encoded==NULL) {
2084     tr_err("trps_wildcard_route_req: error encoding wildcard TRP request.");
2085     rc=TRP_ERROR;
2086     goto cleanup;
2087   }
2088
2089   tr_debug("trps_wildcard_route_req: adding message to queue.");
2090   if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS) {
2091     tr_err("trps_wildcard_route_req: error queueing request.");
2092     rc=TRP_ERROR;
2093   } else {
2094     tr_debug("trps_wildcard_route_req: request queued successfully.");
2095     rc=TRP_SUCCESS;
2096   }
2097
2098 cleanup:
2099   if (encoded!=NULL)
2100     tr_msg_free_encoded(encoded);
2101   if (req!=NULL)
2102     trp_req_free(req);
2103
2104   talloc_free(tmp_ctx);
2105   return rc;
2106 }