From e365572265143f2ba6baa69e0c54e7bf31bbd2ff Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 5 Sep 2014 14:48:47 -0400 Subject: [PATCH] receive local address from kernel and use it for outgoing packets --- daemon/call.c | 97 ++++++++++++++++++++++++++++++++++++++++----------- daemon/call.h | 3 -- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index ef99c025c..07c164535 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -305,6 +305,10 @@ static void unkernelize(struct packet_stream *); static void __stream_unkernelize(struct packet_stream *ps); 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 const GQueue *get_interface_addresses(struct local_interface *lif, int family); @@ -573,7 +577,7 @@ void stream_msg_mh_src(struct packet_stream *ps, struct msghdr *mh) { } /* called lock-free */ -static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsin) { +static int stream_packet(struct stream_fd *sfd, str *s, struct sockaddr_in6 *fsin, struct in6_addr *dst) { struct packet_stream *stream, *sink = NULL, *in_srtp, *out_srtp; @@ -770,6 +774,23 @@ update_addr: update = 1; mutex_unlock(&stream->out_lock); + /* check the destination address of the received packet against what we think our + * local interface to use is */ + if (dst && memcmp(dst, &media->local_address->addr, sizeof(*dst))) { + struct interface_address *ifa; + char ifa_buf[64]; + smart_ntop(ifa_buf, dst, sizeof(ifa_buf)); + ifa = get_interface_from_address(media->interface, dst); + 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; + } + update = 1; + } + + kernel_check: if (PS_ISSET(stream, NO_KERNEL_SUPPORT)) goto forward; @@ -857,14 +878,16 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) { struct stream_fd *sfd = p; char buf[RTP_BUFFER_SIZE]; int ret, iters; - struct sockaddr_storage ss; - struct sockaddr_in6 sin6; - struct sockaddr_in *sin; - unsigned int sinlen; - void *sinp; + struct sockaddr_in6 sin6_src; int update = 0; struct call *ca; str s; + struct msghdr mh; + struct iovec iov; + char control[128]; + struct cmsghdr *cmh; + struct in6_pktinfo *pi6; + struct in6_addr *dst; if (sfd->fd.fd != fd) goto out; @@ -880,9 +903,17 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) { } #endif - sinlen = sizeof(ss); - ret = recvfrom(fd, buf + RTP_BUFFER_HEAD_ROOM, MAX_RTP_PACKET_SIZE, - 0, (struct sockaddr *) &ss, &sinlen); + ZERO(mh); + mh.msg_name = &sin6_src; + mh.msg_namelen = sizeof(sin6_src); + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + mh.msg_control = control; + mh.msg_controllen = sizeof(control); + iov.iov_base = buf + RTP_BUFFER_HEAD_ROOM; + iov.iov_len = MAX_RTP_PACKET_SIZE; + + ret = recvmsg(fd, &mh, 0); if (ret < 0) { if (errno == EINTR) @@ -895,18 +926,16 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) { if (ret >= MAX_RTP_PACKET_SIZE) ilog(LOG_WARNING, "UDP packet possibly truncated"); - sinp = &ss; - if (ss.ss_family == AF_INET) { - sin = sinp; - sinp = &sin6; - ZERO(sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = sin->sin_port; - in4_to_6(&sin6.sin6_addr, sin->sin_addr.s_addr); + dst = NULL; + for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) { + if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO) { + pi6 = (void *) CMSG_DATA(cmh); + dst = &pi6->ipi6_addr; + } } str_init_len(&s, buf + RTP_BUFFER_HEAD_ROOM, ret); - ret = stream_packet(sfd, &s, sinp); + ret = stream_packet(sfd, &s, &sin6_src, dst); if (ret == -1) { ilog(LOG_WARNING, "Write error on RTP socket"); call_destroy(sfd->call); @@ -1357,6 +1386,12 @@ static void __set_tos(int fd, const struct call *c) { #endif } +static void __get_pktinfo(int fd) { + int x; + x = 1; + setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &x, sizeof(x)); +} + static int get_port6(struct udp_fd *r, u_int16_t p, const struct call *c) { int fd; struct sockaddr_in6 sin; @@ -1369,6 +1404,7 @@ static int get_port6(struct udp_fd *r, u_int16_t p, const struct call *c) { reuseaddr(fd); ipv6only(fd, 0); __set_tos(fd, c); + __get_pktinfo(fd); ZERO(sin); sin.sin6_family = AF_INET6; @@ -2804,7 +2840,7 @@ void callmaster_config_init(struct callmaster *m) { } } -struct local_interface *get_local_interface(struct callmaster *m, str *name) { +static struct local_interface *get_local_interface(struct callmaster *m, str *name) { struct local_interface *lif; if (!name) @@ -2814,7 +2850,7 @@ struct local_interface *get_local_interface(struct callmaster *m, str *name) { return lif; } -const GQueue *get_interface_addresses(struct local_interface *lif, int family) { +static const GQueue *get_interface_addresses(struct local_interface *lif, int family) { if (!lif) return NULL; @@ -2830,7 +2866,7 @@ const GQueue *get_interface_addresses(struct local_interface *lif, int family) { } } -struct interface_address *get_interface_address(struct local_interface *lif, int family) { +static struct interface_address *get_interface_address(struct local_interface *lif, int family) { const GQueue *q; q = get_interface_addresses(lif, family); @@ -2846,3 +2882,22 @@ void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int fam else g_queue_append(q, get_interface_addresses(lif, AF_INET)); } + +static struct interface_address *get_interface_from_address(struct local_interface *lif, struct in6_addr *addr) { + GQueue *q; + GList *l; + struct interface_address *ifa; + + if (IN6_IS_ADDR_V4MAPPED(addr)) + q = &lif->ipv4; + else + q = &lif->ipv6; + + for (l = q->head; l; l = l->next) { + ifa = l->data; + if (!memcmp(&ifa->addr, addr, sizeof(*addr))) + return ifa; + } + + return NULL; +} diff --git a/daemon/call.h b/daemon/call.h index 8cf4549fb..ee089bc2e 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -437,9 +437,6 @@ void kernelize(struct packet_stream *); int call_stream_address(char *, struct packet_stream *, enum stream_address_format, int *); int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address_format format, int *len, struct interface_address *ifa); -struct local_interface *get_local_interface(struct callmaster *m, str *name); -const GQueue *get_interface_addresses(struct local_interface *lif, int family); -struct interface_address *get_interface_address(struct local_interface *lif, int family); void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int family); const struct transport_protocol *transport_protocol(const str *s);