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