static int __attempt_send(struct homer_sender *hs, GString *gs) { int ret; ret = write(hs->socket.fd, gs->str, gs->len); if (ret == gs->len) { // full write g_string_free(gs, TRUE); return 0; } if (ret < 0) { if (errno != EWOULDBLOCK && errno != EAGAIN) { ilog(LOG_ERR, "Write error to Homer at %s: %s", endpoint_print_buf(&hs->endpoint), strerror(errno)); __reset(hs); return 1; } ilog(LOG_DEBUG, "Home write blocked"); // XXX use poller for blocked writes? return 2; } // partial write ilog(LOG_DEBUG, "Home write blocked (partial write)"); g_string_erase(gs, 0, ret); return 3; }
/* agent must be locked */ static struct ice_candidate_pair *__pair_candidate(struct stream_fd *sfd, struct ice_agent *ag, struct ice_candidate *cand) { struct ice_candidate_pair *pair; if (sfd->socket.family != cand->endpoint.address.family) return NULL; pair = g_slice_alloc0(sizeof(*pair)); pair->agent = ag; pair->remote_candidate = cand; pair->local_intf = sfd->local_intf; pair->sfd = sfd; if (cand->component_id != 1) PAIR_SET(pair, FROZEN); __do_ice_pair_priority(pair); __new_stun_transaction(pair); g_queue_push_tail(&ag->candidate_pairs, pair); g_hash_table_insert(ag->pair_hash, pair, pair); g_tree_insert(ag->all_pairs, pair, pair); ilog(LOG_DEBUG, "Created candidate pair "PAIR_FORMAT" between %s and %s, type %s", PAIR_FMT(pair), sockaddr_print_buf(&sfd->socket.local.address), endpoint_print_buf(&cand->endpoint), ice_candidate_type_str(cand->type)); return pair; }
static int __check_conn(struct homer_sender *hs, int ret) { if (ret == 0) { ilog(LOG_INFO, "Connection to Homer at %s has been established", endpoint_print_buf(&hs->endpoint)); hs->state = __established; return hs->state(hs); } if (ret == 1) { ilog(LOG_DEBUG, "connection to Homer is in progress"); hs->state = __in_progress; return 0; } ilog(LOG_ERR, "Failed to connect to Homer at %s: %s", endpoint_print_buf(&hs->endpoint), strerror(errno)); __reset(hs); return -1; }
static int __no_socket(struct homer_sender *hs) { int ret; if (hs->retry > time(NULL)) return 0; ilog(LOG_INFO, "Connecting to Homer at %s", endpoint_print_buf(&hs->endpoint)); ret = connect_socket_nb(&hs->socket, hs->protocol, &hs->endpoint); return __check_conn(hs, ret); }
/* call(W) or call(R)+agent must be locked - no in_lock or out_lock must be held */ static int __check_valid(struct ice_agent *ag) { struct call_media *media = ag->media; struct packet_stream *ps; GList *l, *k, *m; GQueue all_compos; struct ice_candidate_pair *pair; // const struct local_intf *ifa; struct stream_fd *sfd; if (!ag) { ilog(LOG_ERR, "ice ag is NULL"); return 0; } __get_complete_valid_pairs(&all_compos, ag); if (!all_compos.length) { ilog(LOG_DEBUG, "ICE not completed yet"); return 0; } pair = all_compos.head->data; ilog(LOG_DEBUG, "ICE completed, using pair "PAIR_FORMAT, PAIR_FMT(pair)); AGENT_SET(ag, COMPLETED); for (l = media->streams.head, k = all_compos.head; l && k; l = l->next, k = k->next) { ps = l->data; pair = k->data; mutex_lock(&ps->out_lock); if (memcmp(&ps->endpoint, &pair->remote_candidate->endpoint, sizeof(ps->endpoint))) { ilog(LOG_INFO, "ICE negotiated: peer for component %u is %s", ps->component, endpoint_print_buf(&pair->remote_candidate->endpoint)); ps->endpoint = pair->remote_candidate->endpoint; } mutex_unlock(&ps->out_lock); for (m = ps->sfds.head; m; m = m->next) { sfd = m->data; if (sfd->local_intf != pair->local_intf) continue; ps->selected_sfd = sfd; if (ps->component == 1) ilog(LOG_INFO, "ICE negotiated: local interface %s", sockaddr_print_buf(&pair->local_intf->spec->local_address.addr)); } } call_media_unkernelize(media); g_queue_clear(&all_compos); return 1; }
/* agent must NOT be locked, but call must be locked in R */ static void __do_ice_check(struct ice_candidate_pair *pair) { struct stream_fd *sfd = pair->sfd; struct ice_agent *ag = pair->agent; u_int32_t prio, transact[3]; if (PAIR_ISSET(pair, SUCCEEDED) && !PAIR_ISSET(pair, TO_USE)) return; if (!ag->pwd[0].s) return; prio = ice_priority(ICT_PRFLX, pair->local_intf->unique_id, pair->remote_candidate->component_id); mutex_lock(&ag->lock); pair->retransmit = g_now; if (!PAIR_SET(pair, IN_PROGRESS)) { PAIR_CLEAR2(pair, FROZEN, FAILED); pair->retransmit_ms = STUN_RETRANSMIT_INTERVAL; pair->retransmits = 0; } else if (pair->retransmits > STUN_MAX_RETRANSMITS) { __fail_pair(pair); mutex_unlock(&ag->lock); return; } else { pair->retransmit_ms *= 2; pair->retransmits++; } timeval_add_usec(&pair->retransmit, pair->retransmit_ms * 1000); __agent_schedule_abs(pair->agent, &pair->retransmit); memcpy(transact, pair->stun_transaction, sizeof(transact)); pair->was_controlling = AGENT_ISSET(ag, CONTROLLING); pair->was_nominated = PAIR_ISSET(pair, TO_USE); mutex_unlock(&ag->lock); ilog(LOG_DEBUG, "Sending %sICE/STUN request for candidate pair "PAIR_FORMAT" from %s to %s", PAIR_ISSET(pair, TO_USE) ? "nominating " : "", PAIR_FMT(pair), sockaddr_print_buf(&pair->local_intf->spec->local_address.addr), endpoint_print_buf(&pair->remote_candidate->endpoint)); stun_binding_request(&pair->remote_candidate->endpoint, transact, &ag->pwd[0], ag->ufrag, AGENT_ISSET(ag, CONTROLLING), tie_breaker, prio, &sfd->socket, PAIR_ISSET(pair, TO_USE)); }
static int __established(struct homer_sender *hs) { char buf[16]; int ret; GString *gs; // test connection with a dummy read ret = read(hs->socket.fd, buf, sizeof(buf)); if (ret < 0) { if (errno != EWOULDBLOCK && errno != EAGAIN) { ilog(LOG_ERR, "Connection error from Homer at %s: %s", endpoint_print_buf(&hs->endpoint), strerror(errno)); __reset(hs); return -1; } } // XXX handle return data from Homer? if (hs->partial) { ilog(LOG_DEBUG, "dequeue partial packet to Homer"); ret = __attempt_send(hs, hs->partial); if (ret == 3 || ret == 2) // partial write or not sent at all return 0; if (ret == 1) // write error, takes care of deleting hs->partial return -1; // ret == 0 -> sent OK, drop through to unqueue g_string_free(hs->partial, TRUE); hs->partial = NULL; } // unqueue as much as we can while ((gs = g_queue_pop_head(&hs->send_queue))) { ilog(LOG_DEBUG, "dequeue send queue to Homer"); ret = __attempt_send(hs, gs); if (ret == 0) // everything sent OK continue; if (ret == 3) { // partial write hs->partial = gs; return 0; } g_queue_push_head(&hs->send_queue, gs); if (ret == 1) // write error return -1; // ret == 2 -> blocked return 0; } // everything unqueued return 0; }
int connect_to_graphite_server(const endpoint_t *ep) { graphite_ep = ep; int rc; ilog(LOG_INFO, "Connecting to graphite server %s", endpoint_print_buf(ep)); rc = connect_socket_nb(&graphite_sock, SOCK_STREAM, ep); if (rc == -1) { ilog(LOG_ERROR,"Couldn't make socket for connecting to graphite."); return -1; } if (rc == 0) ilog(LOG_INFO, "Graphite server connected."); else { /* EINPROGRESS */ ilog(LOG_INFO, "Connection to graphite is in progress."); connectinprogress = 1; } return 0; }
/* call is locked in R */ int ice_response(struct stream_fd *sfd, const endpoint_t *src, struct stun_attrs *attrs, u_int32_t transaction[3]) { struct ice_candidate_pair *pair, *opair; struct ice_agent *ag; struct packet_stream *ps = sfd->stream; struct call_media *media = ps->media; const char *err; unsigned int component; struct ice_candidate *cand; const struct local_intf *ifa; int ret, was_ctl; __DBG("received ICE response from %s on %s", endpoint_print_buf(src), endpoint_print_buf(&sfd->socket.local)); ag = media->ice_agent; if (!ag) return -1; atomic64_set(&ag->last_activity, poller_now); mutex_lock(&ag->lock); pair = g_hash_table_lookup(ag->transaction_hash, transaction); err = "ICE/STUN response with unknown transaction received"; if (!pair) goto err_unlock; was_ctl = pair->was_controlling; mutex_unlock(&ag->lock); ifa = pair->local_intf; ilog(LOG_DEBUG, "Received ICE/STUN response code %u for candidate pair "PAIR_FORMAT" from %s to %s", attrs->error_code, PAIR_FMT(pair), endpoint_print_buf(&pair->remote_candidate->endpoint), sockaddr_print_buf(&ifa->spec->local_address.addr)); /* verify endpoints */ err = "ICE/STUN response received, but source address didn't match remote candidate address"; if (!endpoint_eq(src, &pair->remote_candidate->endpoint)) goto err; err = "ICE/STUN response received, but destination address didn't match local interface address"; if (pair->sfd != sfd) goto err; PAIR_CLEAR(pair, IN_PROGRESS); ret = 0; /* handle all errors */ if (attrs->error_code) { err = "ICE/STUN error received"; if (attrs->error_code != 487) goto err; __role_change(ag, !was_ctl); __trigger_check(pair); goto out; } /* we don't discover peer reflexive here (RFC 5245 7.1.3.2.1) as we don't expect to be behind NAT */ /* we also skip parts of 7.1.3.2.2 as we don't do server reflexive */ mutex_lock(&ag->lock); /* check if we're in the final (controlling) phase */ if (pair->was_nominated && PAIR_CLEAR(pair, TO_USE)) { ilog(LOG_DEBUG, "Setting nominated ICE candidate pair "PAIR_FORMAT" as valid", PAIR_FMT(pair)); PAIR_SET(pair, VALID); g_tree_insert(ag->valid_pairs, pair, pair); ret = __check_valid(ag); goto out_unlock; } if (PAIR_SET(pair, SUCCEEDED)) goto out_unlock; ilog(LOG_DEBUG, "Setting ICE candidate pair "PAIR_FORMAT" as succeeded", PAIR_FMT(pair)); g_tree_insert(ag->succeeded_pairs, pair, pair); if (!ag->start_nominating.tv_sec) { if (__check_succeeded_complete(ag)) { ag->start_nominating = g_now; timeval_add_usec(&ag->start_nominating, 100000); __agent_schedule_abs(ag, &ag->start_nominating); } } /* now unfreeze all other pairs from the same foundation */ for (component = 1; component <= MAX_COMPONENTS; component++) { if (component == ps->component) continue; cand = __foundation_lookup(ag, &pair->remote_candidate->foundation, component); if (!cand) continue; opair = __pair_lookup(ag, cand, ifa); if (!opair) continue; if (PAIR_ISSET(opair, FAILED)) continue; if (!PAIR_CLEAR(opair, FROZEN)) continue; ilog(LOG_DEBUG, "Unfreezing related ICE pair "PAIR_FORMAT, PAIR_FMT(opair)); } /* if this was previously nominated by the peer, it's now valid */ if (PAIR_ISSET(pair, NOMINATED)) { PAIR_SET(pair, VALID); g_tree_insert(ag->valid_pairs, pair, pair); if (!AGENT_ISSET(ag, CONTROLLING)) ret = __check_valid(ag); } out_unlock: mutex_unlock(&ag->lock); out: return ret; err_unlock: mutex_unlock(&ag->lock); err: if (err) ilog(LOG_NOTICE, "%s (from %s on interface %s)", err, endpoint_print_buf(src), endpoint_print_buf(&sfd->socket.local)); if (pair && attrs->error_code) __fail_pair(pair); return 0; }
/* return values: * 1 = ICE completed, interfaces selected * 0 = packet processed * -1 = generic error, process packet as normal * -2 = role conflict */ int ice_request(struct stream_fd *sfd, const endpoint_t *src, struct stun_attrs *attrs) { struct packet_stream *ps = sfd->stream; struct call_media *media = ps->media; struct ice_agent *ag; const char *err; struct ice_candidate *cand; struct ice_candidate_pair *pair; int ret; __DBG("received ICE request from %s on %s", endpoint_print_buf(src), endpoint_print_buf(&sfd->socket.local)); ag = media->ice_agent; if (!ag) return -1; atomic64_set(&ag->last_activity, poller_now); /* determine candidate pair */ mutex_lock(&ag->lock); cand = __cand_lookup(ag, src, ps->component); if (!cand) pair = __learned_candidate(ag, sfd, src, attrs->priority); else pair = __pair_lookup(ag, cand, sfd->local_intf); err = "Failed to determine ICE candidate from STUN request"; if (!pair) goto err_unlock; mutex_unlock(&ag->lock); /* determine role conflict */ if (attrs->controlling && AGENT_ISSET(ag, CONTROLLING)) { if (tie_breaker >= attrs->tiebreaker) return -2; else __role_change(ag, 0); } else if (attrs->controlled && !AGENT_ISSET(ag, CONTROLLING)) { if (tie_breaker >= attrs->tiebreaker) __role_change(ag, 1); else return -2; } if (PAIR_ISSET(pair, SUCCEEDED)) ; else __trigger_check(pair); ret = 0; if (attrs->use && !PAIR_SET(pair, NOMINATED)) { ilog(LOG_DEBUG, "ICE pair "PAIR_FORMAT" has been nominated by peer", PAIR_FMT(pair)); mutex_lock(&ag->lock); g_tree_insert(ag->nominated_pairs, pair, pair); if (PAIR_ISSET(pair, SUCCEEDED)) { PAIR_SET(pair, VALID); g_tree_insert(ag->valid_pairs, pair, pair); } if (!AGENT_ISSET(ag, CONTROLLING)) ret = __check_valid(ag); mutex_unlock(&ag->lock); } return ret; err_unlock: mutex_unlock(&ag->lock); ilog(LOG_NOTICE, "%s (from %s on interface %s)", err, endpoint_print_buf(src), endpoint_print_buf(&sfd->socket.local)); return 0; }
static void create_everything(void) { struct control_tcp *ct; struct control_udp *cu; struct cli *cl; struct timeval tmp_tv; struct timeval redis_start, redis_stop; double redis_diff = 0; if (rtpe_config.kernel_table < 0) goto no_kernel; if (kernel_setup_table(rtpe_config.kernel_table)) { if (rtpe_config.no_fallback) { ilog(LOG_CRIT, "Userspace fallback disallowed - exiting"); exit(-1); } goto no_kernel; } no_kernel: rtpe_poller = poller_new(); if (!rtpe_poller) die("poller creation failed"); dtls_timer(rtpe_poller); if (call_init()) abort(); rwlock_init(&rtpe_config.config_lock); if (rtpe_config.max_sessions < -1) { rtpe_config.max_sessions = -1; } if (rtpe_config.redis_num_threads < 1) { #ifdef _SC_NPROCESSORS_ONLN rtpe_config.redis_num_threads = sysconf( _SC_NPROCESSORS_ONLN ); #endif if (rtpe_config.redis_num_threads < 1) { rtpe_config.redis_num_threads = REDIS_RESTORE_NUM_THREADS; } } ct = NULL; if (rtpe_config.tcp_listen_ep.port) { ct = control_tcp_new(rtpe_poller, &rtpe_config.tcp_listen_ep); if (!ct) die("Failed to open TCP control connection port"); } cu = NULL; if (rtpe_config.udp_listen_ep.port) { interfaces_exclude_port(rtpe_config.udp_listen_ep.port); cu = control_udp_new(rtpe_poller, &rtpe_config.udp_listen_ep); if (!cu) die("Failed to open UDP control connection port"); } rtpe_control_ng = NULL; if (rtpe_config.ng_listen_ep.port) { interfaces_exclude_port(rtpe_config.ng_listen_ep.port); rtpe_control_ng = control_ng_new(rtpe_poller, &rtpe_config.ng_listen_ep, rtpe_config.control_tos); if (!rtpe_control_ng) die("Failed to open UDP control connection port"); } cl = NULL; if (rtpe_config.cli_listen_ep.port) { interfaces_exclude_port(rtpe_config.cli_listen_ep.port); cl = cli_new(rtpe_poller, &rtpe_config.cli_listen_ep); if (!cl) die("Failed to open UDP CLI connection port"); } if (!is_addr_unspecified(&rtpe_config.redis_write_ep.address)) { rtpe_redis_write = redis_new(&rtpe_config.redis_write_ep, rtpe_config.redis_write_db, rtpe_config.redis_write_auth, ANY_REDIS_ROLE, rtpe_config.no_redis_required); if (!rtpe_redis_write) die("Cannot start up without running Redis %s write database! See also NO_REDIS_REQUIRED parameter.", endpoint_print_buf(&rtpe_config.redis_write_ep)); } if (!is_addr_unspecified(&rtpe_config.redis_ep.address)) { rtpe_redis = redis_new(&rtpe_config.redis_ep, rtpe_config.redis_db, rtpe_config.redis_auth, rtpe_redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, rtpe_config.no_redis_required); rtpe_redis_notify = redis_new(&rtpe_config.redis_ep, rtpe_config.redis_db, rtpe_config.redis_auth, rtpe_redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, rtpe_config.no_redis_required); if (!rtpe_redis || !rtpe_redis_notify) die("Cannot start up without running Redis %s database! See also NO_REDIS_REQUIRED parameter.", endpoint_print_buf(&rtpe_config.redis_ep)); if (!rtpe_redis_write) rtpe_redis_write = rtpe_redis; } daemonize(); wpidfile(); homer_sender_init(&rtpe_config.homer_ep, rtpe_config.homer_protocol, rtpe_config.homer_id); rtcp_init(); // must come after Homer init if (rtpe_redis) { // start redis restore timer gettimeofday(&redis_start, NULL); // restore if (redis_restore(rtpe_redis)) die("Refusing to continue without working Redis database"); // stop redis restore timer gettimeofday(&redis_stop, NULL); // print redis restore duration redis_diff += timeval_diff(&redis_stop, &redis_start) / 1000.0; ilog(LOG_INFO, "Redis restore time = %.0lf ms", redis_diff); } gettimeofday(&rtpe_latest_graphite_interval_start, NULL); timeval_from_us(&tmp_tv, (long long) rtpe_config.graphite_interval*1000000); set_graphite_interval_tv(&tmp_tv); }
static void create_everything(struct main_context *ctx) { struct callmaster_config mc; struct control_tcp *ct; struct control_udp *cu; struct control_ng *cn; struct cli *cl; int kfd = -1; struct timeval tmp_tv; struct timeval redis_start, redis_stop; double redis_diff = 0; if (table < 0) goto no_kernel; if (kernel_create_table(table)) { fprintf(stderr, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table); ilog(LOG_CRIT, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table); if (no_fallback) exit(-1); goto no_kernel; } kfd = kernel_open_table(table); if (kfd == -1) { fprintf(stderr, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table); ilog(LOG_CRIT, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table); if (no_fallback) exit(-1); goto no_kernel; } no_kernel: ctx->p = poller_new(); if (!ctx->p) die("poller creation failed"); ctx->m = callmaster_new(ctx->p); if (!ctx->m) die("callmaster creation failed"); dtls_timer(ctx->p); ZERO(mc); rwlock_init(&mc.config_lock); mc.kernelfd = kfd; mc.kernelid = table; if (max_sessions < -1) { max_sessions = -1; } mc.max_sessions = max_sessions; mc.timeout = timeout; mc.silent_timeout = silent_timeout; mc.final_timeout = final_timeout; mc.delete_delay = delete_delay; mc.default_tos = tos; mc.b2b_url = b2b_url; mc.fmt = xmlrpc_fmt; mc.graphite_ep = graphite_ep; mc.graphite_interval = graphite_interval; mc.redis_subscribed_keyspaces = g_queue_copy(&keyspaces); if (redis_num_threads < 1) { #ifdef _SC_NPROCESSORS_ONLN redis_num_threads = sysconf( _SC_NPROCESSORS_ONLN ); #endif if (redis_num_threads < 1) { redis_num_threads = REDIS_RESTORE_NUM_THREADS; } } mc.redis_num_threads = redis_num_threads; ct = NULL; if (tcp_listen_ep.port) { ct = control_tcp_new(ctx->p, &tcp_listen_ep, ctx->m); if (!ct) die("Failed to open TCP control connection port"); } cu = NULL; if (udp_listen_ep.port) { interfaces_exclude_port(udp_listen_ep.port); cu = control_udp_new(ctx->p, &udp_listen_ep, ctx->m); if (!cu) die("Failed to open UDP control connection port"); } cn = NULL; if (ng_listen_ep.port) { interfaces_exclude_port(ng_listen_ep.port); cn = control_ng_new(ctx->p, &ng_listen_ep, ctx->m); if (!cn) die("Failed to open UDP control connection port"); } cl = NULL; if (cli_listen_ep.port) { interfaces_exclude_port(cli_listen_ep.port); cl = cli_new(ctx->p, &cli_listen_ep, ctx->m); if (!cl) die("Failed to open UDP CLI connection port"); } if (!is_addr_unspecified(&redis_write_ep.address)) { mc.redis_write = redis_new(&redis_write_ep, redis_write_db, redis_write_auth, ANY_REDIS_ROLE, no_redis_required); if (!mc.redis_write) die("Cannot start up without running Redis %s write database! See also NO_REDIS_REQUIRED paramter.", endpoint_print_buf(&redis_write_ep)); } if (!is_addr_unspecified(&redis_ep.address)) { mc.redis = redis_new(&redis_ep, redis_db, redis_auth, mc.redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, no_redis_required); mc.redis_notify = redis_new(&redis_ep, redis_db, redis_auth, mc.redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, no_redis_required); if (!mc.redis || !mc.redis_notify) die("Cannot start up without running Redis %s database! See also NO_REDIS_REQUIRED paramter.", endpoint_print_buf(&redis_ep)); if (!mc.redis_write) mc.redis_write = mc.redis; } mc.redis_expires_secs = redis_expires; ctx->m->conf = mc; if (!foreground) daemonize(); wpidfile(); ctx->m->homer = homer_sender_new(&homer_ep, homer_protocol, homer_id); if (mc.redis) { // start redis restore timer gettimeofday(&redis_start, NULL); // restore if (redis_restore(ctx->m, mc.redis)) die("Refusing to continue without working Redis database"); // stop redis restore timer gettimeofday(&redis_stop, NULL); // print redis restore duration redis_diff += timeval_diff(&redis_stop, &redis_start) / 1000.0; ilog(LOG_INFO, "Redis restore time = %.0lf ms", redis_diff); } gettimeofday(&ctx->m->latest_graphite_interval_start, NULL); timeval_from_us(&tmp_tv, graphite_interval*1000000); set_graphite_interval_tv(&tmp_tv); }