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 __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;
}

@ -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);

Loading…
Cancel
Save