diff --git a/daemon/aux.h b/daemon/aux.h index 62506cd60..1080016e6 100644 --- a/daemon/aux.h +++ b/daemon/aux.h @@ -382,6 +382,12 @@ INLINE int is_addr_unspecified(const struct in6_addr *a) { return 0; } +INLINE int family_from_address(const struct in6_addr *a) { + if (IN6_IS_ADDR_V4MAPPED(a)) + return AF_INET; + return AF_INET6; +} + /* checks if at least one of the flags is set */ INLINE int bf_isset(const unsigned int *u, unsigned int f) { if ((*u & f)) diff --git a/daemon/call.c b/daemon/call.c index 07c164535..a34bc7337 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -307,8 +307,9 @@ static void stream_unkernelize(struct packet_stream *ps); static void __monologue_destroy(struct call_monologue *monologue); static struct interface_address *get_interface_from_address(struct local_interface *lif, struct in6_addr *addr); static struct interface_address *get_interface_address(struct local_interface *lif, int family); -static struct local_interface *get_local_interface(struct callmaster *m, str *name); +static struct local_interface *get_local_interface(struct callmaster *m, const str *name); static const GQueue *get_interface_addresses(struct local_interface *lif, int family); +static struct interface_address *get_any_interface_address(struct local_interface *lif); @@ -336,14 +337,11 @@ static void stream_fd_closed(int fd, void *p, uintptr_t u) { INLINE void __mp_address_translate(struct mp_address *o, const struct endpoint *ep) { - if (IN6_IS_ADDR_V4MAPPED(&ep->ip46)) { - o->family = AF_INET; + o->family = family_from_address(&ep->ip46); + if (o->family == AF_INET) o->u.ipv4 = in6_to_4(&ep->ip46); - } - else { - o->family = AF_INET6; + else memcpy(o->u.ipv6, &ep->ip46, sizeof(o->u.ipv6)); - } o->port = ep->port; } @@ -781,12 +779,12 @@ update_addr: char ifa_buf[64]; smart_ntop(ifa_buf, dst, sizeof(ifa_buf)); ifa = get_interface_from_address(media->interface, dst); - if (!ifa) + if (!ifa) { ilog(LOG_ERROR, "No matching local interface for destination address %s found", ifa_buf); - else { - ilog(LOG_INFO, "Switching local interface to %s", ifa_buf); - media->local_address = ifa; + goto drop; } + ilog(LOG_INFO, "Switching local interface to %s", ifa_buf); + media->local_address = ifa; update = 1; } @@ -2046,6 +2044,34 @@ static void __tos_change(struct call *call, const struct sdp_ng_flags *flags) { __set_all_tos(call); } +static void __init_interface(struct call_media *media, const str *ifname) { + if (!media->interface || !media->local_address) + goto get; + if (!ifname || !ifname->s) + return; + if (!str_cmp_str(&media->interface->name, ifname)) + return; +get: + media->interface = get_local_interface(media->call->callmaster, ifname); + if (!media->interface) { + media->interface = get_local_interface(media->call->callmaster, NULL); + /* legacy support */ + if (!str_cmp(ifname, "internal")) + media->desired_family = AF_INET; + else if (!str_cmp(ifname, "external")) + media->desired_family = AF_INET6; + else + ilog(LOG_WARNING, "Interface '"STR_FORMAT"' not found, using default", STR_FMT(ifname)); + } + media->local_address = get_interface_address(media->interface, media->desired_family); + if (!media->local_address) { + ilog(LOG_WARNING, "No usable address in interface '"STR_FORMAT"' found, using default", + STR_FMT(ifname)); + media->local_address = get_any_interface_address(media->interface); + media->desired_family = family_from_address(&media->local_address->addr); + } +} + /* called with call->master_lock held in W */ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, const struct sdp_ng_flags *flags) @@ -2057,10 +2083,8 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, struct call_monologue *monologue = other_ml->active_dialogue; struct endpoint_map *em; struct call *call; - struct callmaster *cm; call = monologue->call; - cm = call->callmaster; call->last_signal = poller_now; call->deleted = 0; @@ -2145,25 +2169,17 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, __generate_crypto(flags, media, other_media); /* deduct address family from stream parameters received */ - other_media->desired_family = AF_INET; - if (!IN6_IS_ADDR_V4MAPPED(&sp->rtp_endpoint.ip46)) - other_media->desired_family = AF_INET6; + other_media->desired_family = family_from_address(&sp->rtp_endpoint.ip46); /* for outgoing SDP, use "direction"/DF or default to what was offered */ if (!media->desired_family) media->desired_family = other_media->desired_family; if (sp->desired_family) media->desired_family = sp->desired_family; - else if (sp->direction[1] == DIR_EXTERNAL) - media->desired_family = AF_INET6; - /* local interface selection XXX */ - media->interface = get_local_interface(cm, NULL); - media->local_address = get_interface_address(media->interface, media->desired_family); - other_media->interface = get_local_interface(cm, NULL); - other_media->local_address = get_interface_address(other_media->interface, - other_media->desired_family); - + /* local interface selection */ + __init_interface(media, &sp->direction[1]); + __init_interface(other_media, &sp->direction[0]); /* we now know what's being advertised by the other side */ @@ -2840,10 +2856,10 @@ void callmaster_config_init(struct callmaster *m) { } } -static struct local_interface *get_local_interface(struct callmaster *m, str *name) { +static struct local_interface *get_local_interface(struct callmaster *m, const str *name) { struct local_interface *lif; - if (!name) + if (!name || !name->s) return m->interface_list.head->data; lif = g_hash_table_lookup(m->interfaces, name); @@ -2875,6 +2891,17 @@ static struct interface_address *get_interface_address(struct local_interface *l return q->head->data; } +/* safety fallback */ +static struct interface_address *get_any_interface_address(struct local_interface *lif) { + struct interface_address *ifa; + GQueue q = G_QUEUE_INIT; + + get_all_interface_addresses(&q, lif, AF_INET); + ifa = q.head->data; + g_queue_clear(&q); + return ifa; +} + void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int family) { g_queue_append(q, get_interface_addresses(lif, family)); if (family == AF_INET) diff --git a/daemon/call.h b/daemon/call.h index ee089bc2e..323caa8c5 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -20,11 +20,6 @@ enum stream_address_format { SAF_NG, SAF_ICE, }; -enum stream_direction { - DIR_UNKNOWN = 0, - DIR_INTERNAL, - DIR_EXTERNAL, -}; enum call_opmode { OP_OFFER = 0, OP_ANSWER = 1, @@ -201,7 +196,7 @@ struct stream_params { const struct transport_protocol *protocol; struct crypto_params crypto; unsigned int sdes_tag; - enum stream_direction direction[2]; + str direction[2]; int desired_family; struct dtls_fingerprint fingerprint; unsigned int sp_flags; diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 8fcf24589..2f1abf197 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -108,9 +108,9 @@ static int addr_parse_udp(struct stream_params *sp, char **out) { for (cp =out[RE_UDP_UL_FLAGS]; *cp && i < 2; cp++) { c = chrtoupper(*cp); if (c == 'E') - sp->direction[i++] = DIR_EXTERNAL; + str_init(&sp->direction[i++], "external"); else if (c == 'I') - sp->direction[i++] = DIR_INTERNAL; + str_init(&sp->direction[i++], "internal"); } } @@ -505,15 +505,8 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu diridx = 0; if ((list = bencode_dictionary_get_expect(input, "direction", BENCODE_LIST))) { - for (it = list->child; it && diridx < 2; it = it->sibling) { - if (!bencode_strcmp(it, "internal")) - out->directions[diridx++] = DIR_INTERNAL; - else if (!bencode_strcmp(it, "external")) - out->directions[diridx++] = DIR_EXTERNAL; - else - ilog(LOG_WARN, "Unknown 'direction' flag encountered: '"BENCODE_FORMAT"'", - BENCODE_FMT(it)); - } + for (it = list->child; it && diridx < 2; it = it->sibling) + bencode_get_str(it, &out->direction[diridx++]); } list = bencode_dictionary_get_expect(input, "received from", BENCODE_LIST); diff --git a/daemon/sdp.c b/daemon/sdp.c index 37874390c..1d884396c 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -985,7 +985,7 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl sp->consecutive_ports = media->port_count; sp->protocol = transport_protocol(&media->transport); sp->type = media->media_type; - memcpy(sp->direction, flags->directions, sizeof(sp->direction)); + memcpy(sp->direction, flags->direction, sizeof(sp->direction)); sp->desired_family = flags->address_family; bf_xset(&sp->sp_flags, SP_FLAG_ASYMMETRIC, flags->asymmetric); bf_xset(&sp->sp_flags, SP_FLAG_STRICT_SOURCE, flags->strict_source); diff --git a/daemon/sdp.h b/daemon/sdp.h index 678379c06..4036b550c 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -16,7 +16,7 @@ struct sdp_ng_flags { const struct transport_protocol *transport_protocol; struct in6_addr parsed_received_from; struct in6_addr parsed_media_address; - enum stream_direction directions[2]; + str direction[2]; int address_family; int tos; int asymmetric:1, diff --git a/utils/ng-client b/utils/ng-client index 6606c9027..97f06177a 100755 --- a/utils/ng-client +++ b/utils/ng-client @@ -34,6 +34,7 @@ GetOptions( 'rtcp-mux-accept' => \$options{'rtcp-mux-accept'}, 'rtcp-mux-reject' => \$options{'rtcp-mux-reject'}, 'address-family=s' => \$options{'address family'}, + 'direction=s' => \$options{'direction'}, 'force' => \$options{'force'}, 'v|verbose' => \$options{'verbose'}, 'strict-source' => \$options{'strict source'}, @@ -57,6 +58,10 @@ for my $x (split(',', 'origin,session connection')) { for my $x (split(',', 'offer,demux,accept,reject')) { defined($options{'rtcp-mux-' . $x}) and push(@{$packet{'rtcp-mux'}}, $x); } +if (defined($options{direction})) { + $options{direction} =~ /(.*),(.*)/ or die; + $packet{direction} = [$1,$2]; +} if (defined($options{sdp})) { $packet{sdp} = $options{sdp};