9 #include <trp_rtable.h>
10 #include <trp_internal.h>
13 TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
15 TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
22 trps->mq=tr_mq_new(trps);
24 /* failed to allocate mq */
32 void trps_free (TRPS_INSTANCE *trps)
38 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
40 return tr_mq_pop(trps->mq);
43 void trps_mq_append(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
45 tr_mq_append(trps->mq, msg);
48 /* stand-in for a function that finds the connection for a particular peer */
50 static TRP_CONNECTION *trps_find_connection(TRPS_INSTANCE *trps)
56 void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
61 trp_connection_append(trps->conn, new);
63 talloc_steal(trps, new);
66 /* ok to call more than once; guarantees connection no longer in the list.
67 * Caller is responsible for freeing the removed element afterwards. */
68 void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
70 trps->conn=trp_connection_remove(trps->conn, remove);
73 void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
78 trpc_append(trps->trpc, trpc);
80 talloc_steal(trps, trpc);
83 /* ok to call more than once; guarantees trpc no longer in the list.
84 * Caller is responsible for freeing the removed element afterwards. */
85 void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove)
87 trps->trpc=trpc_remove(trps->trpc, remove);
90 TRP_RC trps_send_msg (TRPS_INSTANCE *trps, void *peer, const char *msg)
92 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
93 TR_MQ_MSG *mq_msg=NULL;
97 /* Currently ignore peer and just send to an open connection.
98 * In reality, need to identify the correct peer and send via that
100 if (trps->trpc != NULL) {
101 if (trpc_get_status(trps->trpc)!=TRP_CONNECTION_UP)
102 tr_debug("trps_send_msg: skipping message sent while TRPC connection not up.");
104 mq_msg=tr_mq_msg_new(tmp_ctx, "trpc_send");
105 msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
106 tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
107 trpc_mq_append(trps->trpc, mq_msg);
111 talloc_free(tmp_ctx);
115 static int trps_listen (TRPS_INSTANCE *trps, int port)
122 struct sockaddr_storage storage;
123 struct sockaddr_in in4;
126 struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
128 saddr->sin_port = htons (port);
129 saddr->sin_family = AF_INET;
130 saddr->sin_addr.s_addr = INADDR_ANY;
132 if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
135 setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
137 if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
140 if (0 > (rc = listen(conn, 512)))
143 tr_debug("trps_listen: TRP Server listening on port %d", port);
147 /* returns EACCES if authorization is denied */
148 int trps_auth_cb(gss_name_t clientName, gss_buffer_t displayName, void *data)
150 TRPS_INSTANCE *inst = (TRPS_INSTANCE *)data;
153 if (0!=inst->auth_handler(clientName, displayName, inst->cookie)) {
154 tr_debug("trps_auth_cb: client '%.*s' denied authorization.", displayName->length, displayName->value);
155 result=EACCES; /* denied */
161 /* get the currently selected route if available */
162 TRP_RENTRY *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
164 return trp_rtable_get_selected_entry(trps->rtable, comm, realm);
167 /* mark a route as retracted */
168 static TRP_RC trps_retract_route(TRPS_INSTANCE *trps, TRP_RENTRY *entry)
170 trp_rentry_set_metric(entry, TRP_METRIC_INFINITY);
173 /* is this route retracted? */
174 static int trps_route_retracted(TRPS_INSTANCE *trps, TRP_RENTRY *entry)
176 return (trp_rentry_get_metric(entry)==TRP_METRIC_INFINITY);
179 static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
186 tr_debug("trps_read_message: started");
187 if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
188 *(trp_connection_get_gssctx(conn)),
191 tr_debug("trps_read_message: error");
197 tr_debug("trps_read_message(): Request Received, %u bytes.", (unsigned) buflen);
198 tr_debug("trps_read_message(): %.*s", buflen, buf);
200 *msg=tr_msg_decode(buf, buflen);
205 peer=trp_connection_get_peer(conn);
206 /* verify we received a message we support, otherwise drop it now */
207 switch (tr_msg_get_msg_type(*msg)) {
209 trp_upd_set_peer(tr_msg_get_trp_upd(msg), tr_name_dup(peer));
213 trp_req_set_peer(tr_msg_get_trp_req(msg), tr_name_dup(peer));
217 tr_debug("trps_read_message: received unsupported message from %.*s", peer->len, peer->buf);
218 tr_msg_free_decoded(*msg);
220 return TRP_UNSUPPORTED;
226 int trps_get_listener(TRPS_INSTANCE *trps,
227 TRPS_MSG_FUNC msg_handler,
228 TRP_AUTH_FUNC auth_handler,
229 const char *hostname,
235 if (0 > (listen = trps_listen(trps, port))) {
237 if (0 == strerror_r(errno, errbuf, 256)) {
238 tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf);
240 tr_debug("trps_get_listener: Unknown error openining port %d.", port);
245 /* opening port succeeded */
246 tr_debug("trps_get_listener: Opened port %d.", port);
248 /* make this socket non-blocking */
249 if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
250 tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
257 /* store the caller's request handler & cookie */
258 trps->msg_handler = msg_handler;
259 trps->auth_handler = auth_handler;
260 trps->hostname = talloc_strdup(trps, hostname);
262 trps->cookie = cookie;
268 void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
270 TALLOC_CTX *tmp_ctx=talloc_new(NULL);
274 /* try to establish a GSS context */
275 if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
276 tr_notice("tr_trps_conn_thread: failed to authorize connection");
279 tr_notice("trps_handle_connection: authorized connection");
281 /* loop as long as the connection exists */
282 while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
283 rc=trps_read_message(trps, conn, &msg);
286 trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
290 trp_connection_close(conn);
294 tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
298 tr_debug("trps_handle_connection: connection closed.");
299 talloc_free(tmp_ctx);
302 /* ensure that the update could be accepted if feasible */
303 static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
305 switch(trp_inforec_get_type(rec)) {
306 case TRP_INFOREC_TYPE_ROUTE:
307 if ((trp_inforec_get_comm(rec)==NULL)
308 || (trp_inforec_get_realm(rec)==NULL)
309 || (trp_inforec_get_trust_router(rec)==NULL)
310 || (trp_inforec_get_next_hop(rec)==NULL))
311 tr_debug("trps_validate_inforec: missing record info.");
314 /* check for valid metric */
315 if ((trp_inforec_get_metric(rec)==TRP_METRIC_INVALID)
316 || (trp_inforec_get_metric(rec)>TRP_METRIC_INFINITY)) {
317 tr_debug("trps_validate_inforec: invalid metric.");
321 /* check for valid interval */
322 if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
323 tr_debug("trps_validate_inforec: invalid interval.");
329 return TRP_UNSUPPORTED;
335 /* link cost to a peer */
336 static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
341 static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
343 TRP_RENTRY *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
345 return TRP_METRIC_INFINITY;
346 return trp_rentry_get_metric(entry) + trps_cost(trps, peer);
349 /* returns TRP_SUCCESS if route is feasible, TRP_ERROR otherwise */
350 static TRP_RC trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
352 unsigned int rec_metric=trp_inforec_get_metric(rec);
353 unsigned int new_metric=0;
354 unsigned int current_metric=0;
356 /* we check these in the validation stage, but just in case... */
357 if ((rec_metric==TRP_METRIC_INVALID) || (rec_metric>TRP_METRIC_INFINITY))
360 /* retractions (aka infinite metrics) are always feasible */
361 if (rec_metric==TRP_METRIC_INFINITY)
364 /* updates from our current next hop are always feasible*/
365 if (0==tr_name_cmp(trp_inforec_get_next_hop(rec),
367 trp_inforec_get_comm(rec),
368 trp_inforec_get_realm(rec)))) {
373 /* compare the existing metric we advertise to what we would advertise
374 * if we accept this update */
375 current_metric=trps_advertised_metric(trps,
376 trp_inforec_get_comm(rec),
377 trp_inforec_get_realm(rec),
378 trp_inforec_get_next_hop(rec));
379 new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
380 if (new_metric <= current_metric)
386 /* uses memory pointed to by *ts, also returns that value */
387 static void trps_compute_expiry(unsigned int interval, struct timespec *ts)
389 const unsigned int small_factor=3; /* how many intervals we wait before expiring */
390 if (0!=clock_gettime(CLOCK_REALTIME, ts)) {
391 tr_error("trps_compute_expiry: could not read realtime clock.");
396 ts->tv_sec += small_factor*interval;
400 static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
402 TRP_RENTRY *entry=NULL;
404 entry=trp_rtable_get_entry(trps->rtable,
405 trp_inforec_get_comm(rec),
406 trp_inforec_get_realm(rec),
407 trp_inforec_get_next_hop(rec));
409 entry=trp_rentry_new(NULL);
411 tr_error("trps_accept_update: unable to allocate new entry.");
415 trp_rentry_set_apc(entry, tr_dup_name(trp_inforec_get_comm(rec)));
416 trp_rentry_set_realm(entry, tr_dup_name(trp_inforec_get_realm(rec)));
417 trp_rentry_set_peer(entry, tr_dup_name(trp_inforec_get_next_hop(rec)));
418 trp_rentry_set_trust_router(entry, tr_dup_name(trp_inforec_get_trust_router(rec)));
419 trp_rentry_set_next_hop(entry, tr_dup_name(trp_inforec_get_next_hop(rec)));
420 if ((trp_rentry_get_apc(entry)==NULL)
421 ||(trp_rentry_get_realm(entry)==NULL)
422 ||(trp_rentry_get_peer(entry)==NULL)
423 ||(trp_rentry_get_trust_router(entry)==NULL)
424 ||(trp_rentry_next_hop(entry)==NULL)) {
425 /* at least one field could not be allocated */
426 tr_error("trps_accept_update: unable to allocate all fields for entry.");
427 trp_rentry_free(entry);
430 trp_rtable_add(trps->rtable, entry);
433 /* We now have an entry in the table, whether it's new or not. Update metric and expiry, unless
434 * the metric is infinity. An infinite metric can only occur here if we just retracted an existing
435 * route (we never accept retractions as new routes), so there is no risk of leaving the expiry
436 * time unset on a new route entry. */
437 trp_rentry_set_metric(entry, trp_inforec_get_metric(rec));
438 if (!trps_route_retracted(trps, entry))
439 trp_rentry_set_expiry(entry, trps_compute_expiry(trps, trp_inforec_get_interval(rec)));
443 /* TODO: handle community updates */
444 static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
447 TRP_INFOREC *rec=trp_upd_get_inforec(upd);
448 TRP_RENTRY *route=NULL;
451 tr_notice("trps_handle_update: received TRP update with no info records.");
455 for (;rec!=NULL; rec=trp_inforec_get_next(rec)) {
456 /* validate/sanity check the update */
457 if (trps_validate_inforec(trps, upd) != TRP_SUCCESS) {
458 tr_notice("trps_handle_update: invalid record in TRP update.");
462 /* determine feasibility */
463 feas=trps_check_feasibility(trps, rec);
465 /* do we have an existing route? */
466 route=trps_get_route(trps, trp_inforec_get_comm(rec), trp_inforec_get_realm(rec));
468 /* there was a route table entry already */
470 /* Update is feasible. Accept it. */
471 trps_accept_update(trps, rec);
473 /* Update is infeasible. Ignore it unless the trust router has changed. */
474 if (0!=tr_name_cmp(trp_rentry_get_trust_router(route),
475 trp_inforec_get_trust_router(rec))) {
476 /* the trust router associated with the route has changed, treat update as a retraction */
477 trps_retract_route(trps, route);
481 /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
482 if (feas && (trp_inforec_get_metric != TRP_METRIC_INFINITY))
483 trps_accept_update(trps, rec);
489 TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
491 switch (tr_msg_get_msg_type(tr_msg)) {
493 return trps_handle_update(TRPS_INSTANCE *trps, tr_msg_get_trp_upd(tr_msg));
496 return TRP_UNSUPPORTED;
499 /* unknown error or one we don't care about (e.g., TID messages) */