receive local address from kernel and use it for outgoing packets

pull/26/head
Richard Fuchs 11 years ago
parent 513ac44a84
commit e365572265

@ -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 stream_unkernelize(struct packet_stream *ps); static void stream_unkernelize(struct packet_stream *ps);
static void __monologue_destroy(struct call_monologue *monologue); 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 */ /* 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, struct packet_stream *stream,
*sink = NULL, *sink = NULL,
*in_srtp, *out_srtp; *in_srtp, *out_srtp;
@ -770,6 +774,23 @@ update_addr:
update = 1; update = 1;
mutex_unlock(&stream->out_lock); 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: kernel_check:
if (PS_ISSET(stream, NO_KERNEL_SUPPORT)) if (PS_ISSET(stream, NO_KERNEL_SUPPORT))
goto forward; goto forward;
@ -857,14 +878,16 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) {
struct stream_fd *sfd = p; struct stream_fd *sfd = p;
char buf[RTP_BUFFER_SIZE]; char buf[RTP_BUFFER_SIZE];
int ret, iters; int ret, iters;
struct sockaddr_storage ss; struct sockaddr_in6 sin6_src;
struct sockaddr_in6 sin6;
struct sockaddr_in *sin;
unsigned int sinlen;
void *sinp;
int update = 0; int update = 0;
struct call *ca; struct call *ca;
str s; 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) if (sfd->fd.fd != fd)
goto out; goto out;
@ -880,9 +903,17 @@ static void stream_fd_readable(int fd, void *p, uintptr_t u) {
} }
#endif #endif
sinlen = sizeof(ss); ZERO(mh);
ret = recvfrom(fd, buf + RTP_BUFFER_HEAD_ROOM, MAX_RTP_PACKET_SIZE, mh.msg_name = &sin6_src;
0, (struct sockaddr *) &ss, &sinlen); 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 (ret < 0) {
if (errno == EINTR) 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) if (ret >= MAX_RTP_PACKET_SIZE)
ilog(LOG_WARNING, "UDP packet possibly truncated"); ilog(LOG_WARNING, "UDP packet possibly truncated");
sinp = &ss; dst = NULL;
if (ss.ss_family == AF_INET) { for (cmh = CMSG_FIRSTHDR(&mh); cmh; cmh = CMSG_NXTHDR(&mh, cmh)) {
sin = sinp; if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO) {
sinp = &sin6; pi6 = (void *) CMSG_DATA(cmh);
ZERO(sin6); dst = &pi6->ipi6_addr;
sin6.sin6_family = AF_INET6; }
sin6.sin6_port = sin->sin_port;
in4_to_6(&sin6.sin6_addr, sin->sin_addr.s_addr);
} }
str_init_len(&s, buf + RTP_BUFFER_HEAD_ROOM, ret); 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) { if (ret == -1) {
ilog(LOG_WARNING, "Write error on RTP socket"); ilog(LOG_WARNING, "Write error on RTP socket");
call_destroy(sfd->call); call_destroy(sfd->call);
@ -1357,6 +1386,12 @@ static void __set_tos(int fd, const struct call *c) {
#endif #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) { static int get_port6(struct udp_fd *r, u_int16_t p, const struct call *c) {
int fd; int fd;
struct sockaddr_in6 sin; 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); reuseaddr(fd);
ipv6only(fd, 0); ipv6only(fd, 0);
__set_tos(fd, c); __set_tos(fd, c);
__get_pktinfo(fd);
ZERO(sin); ZERO(sin);
sin.sin6_family = AF_INET6; 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; struct local_interface *lif;
if (!name) if (!name)
@ -2814,7 +2850,7 @@ struct local_interface *get_local_interface(struct callmaster *m, str *name) {
return lif; 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) if (!lif)
return NULL; 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; const GQueue *q;
q = get_interface_addresses(lif, family); q = get_interface_addresses(lif, family);
@ -2846,3 +2882,22 @@ void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int fam
else else
g_queue_append(q, get_interface_addresses(lif, AF_INET)); 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;
}

@ -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_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 call_stream_address46(char *o, struct packet_stream *ps, enum stream_address_format format,
int *len, struct interface_address *ifa); 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); void get_all_interface_addresses(GQueue *q, struct local_interface *lif, int family);
const struct transport_protocol *transport_protocol(const str *s); const struct transport_protocol *transport_protocol(const str *s);

Loading…
Cancel
Save