diff -u -r1.586 chan_sip.c --- channels/chan_sip.c 7 Dec 2004 15:42:00 -0000 1.586 +++ channels/chan_sip.c 8 Dec 2004 21:57:40 -0000 @@ -63,6 +63,9 @@ #ifndef DEFAULT_USERAGENT #define DEFAULT_USERAGENT "Asterisk PBX" #endif + +#define DSTADDR_SOCKOPT IP_PKTINFO +#define dstaddr(x) (&(((struct in_pktinfo *)(CMSG_DATA(x)))->ipi_addr)) #define VIDEO_CODEC_MASK 0x1fc0000 /* Video codecs from H.261 thru AST_FORMAT_MAX_VIDEO */ #ifndef IPTOS_MINCOST @@ -603,12 +606,52 @@ { int res; char iabuf[INET_ADDRSTRLEN]; - if (p->nat & SIP_NAT_ROUTE) + if (p->nat & SIP_NAT_ROUTE) { res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->recv, sizeof(struct sockaddr_in)); - else - res=sendto(sipsock, data, len, 0, (struct sockaddr *)&p->sa, sizeof(struct sockaddr_in)); + } else { + struct iovec iov; + struct msghdr mesg; + struct cmsghdr *cmsg; + struct in_pktinfo *pkti; + char b[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)]; + + bzero((char *) &mesg, sizeof(mesg)); + + /* Our data */ + iov.iov_base = data; + iov.iov_len = len; + + mesg.msg_iov = &iov; + mesg.msg_iovlen = 1; + + /* Dst address */ + mesg.msg_name = &(p->sa); + mesg.msg_namelen = sizeof(p->sa); + + /* Control data (source address) */ + + mesg.msg_control = b; + mesg.msg_controllen = sizeof(b); + mesg.msg_flags = 0; + + cmsg = CMSG_FIRSTHDR(&mesg); + cmsg->cmsg_len = sizeof(b); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + + /* Set the actual source address */ + + pkti = (struct in_pktinfo *)CMSG_DATA(cmsg); + pkti->ipi_ifindex = 0; + pkti->ipi_spec_dst = p->ourip; + pkti->ipi_addr = p->ourip; + + res = sendmsg(sipsock, &mesg, 0); + + } if (res != len) { - ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s returned %d: %s\n", data, len, ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), res, strerror(errno)); + ast_log(LOG_WARNING, "sip_xmit of %p (len %d) to %s:%d returned %d: %s\n", data, len, + ast_inet_ntoa(iabuf, sizeof(iabuf), p->sa.sin_addr), p->sa.sin_port, res, strerror(errno)); } return res; } @@ -2234,7 +2277,7 @@ } /*--- sip_alloc: Allocate SIP_PVT structure and set defaults ---*/ -static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useglobal_nat) +static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, struct sockaddr_in *sout, int useglobal_nat) { struct sip_pvt *p; char iabuf[INET_ADDRSTRLEN]; @@ -2255,8 +2298,12 @@ #endif if (sin) { memcpy(&p->sa, sin, sizeof(p->sa)); - if (ast_sip_ouraddrfor(&p->sa.sin_addr,&p->ourip)) - memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); + if (sout) { + memcpy(&p->ourip, &(sout->sin_addr), sizeof(p->ourip)); + } else { + if (ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip)) + memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); + } } else { memcpy(&p->ourip, &__ourip, sizeof(p->ourip)); } @@ -2329,7 +2376,7 @@ /*--- find_call: Connect incoming SIP message to current call or create new call structure */ /* Called by handle_request ,sipsock_read */ -static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin) +static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, struct sockaddr_in *sout) { struct sip_pvt *p; char *callid; @@ -2385,7 +2432,7 @@ p = p->next; } ast_mutex_unlock(&iflock); - p = sip_alloc(callid, sin, 1); + p = sip_alloc(callid, sin, sout, 1); if (p) ast_mutex_lock(&p->lock); return p; @@ -4107,7 +4154,7 @@ build_callid(r->callid, sizeof(r->callid), __ourip, default_fromdomain); r->callid_valid = 1; } - p=sip_alloc( r->callid, NULL, 0); + p=sip_alloc( r->callid, NULL, NULL, 0); if (!p) { ast_log(LOG_WARNING, "Unable to allocate registration call\n"); return 0; @@ -7696,17 +7743,47 @@ static int sipsock_read(int *id, int fd, short events, void *ignore) { struct sip_request req; - struct sockaddr_in sin = { 0, }; + struct sockaddr_in sin, sout; struct sip_pvt *p; int res; int len; int nounlock; int recount = 0; int debug; + struct iovec vec; + struct msghdr msg; + struct cmsghdr * cmsgptr; + char hdrdata[2048]; len = sizeof(sin); memset(&req, 0, sizeof(req)); - res = recvfrom(sipsock, req.data, sizeof(req.data) - 1, 0, (struct sockaddr *)&sin, &len); + memset(&sin, 0, sizeof(sin)); + memset(&sout, 0, sizeof(sout)); + memset(&msg, 0, sizeof(msg)); + + /* Use recvmsg() instead of recvfrom() so we have the dst address -- Theo 02/09/04 */ + + vec.iov_base = req.data; + vec.iov_len = sizeof(req.data) - 1; + + msg.msg_controllen = sizeof(hdrdata) - sizeof(struct iovec) - sizeof(struct msghdr); + msg.msg_control = hdrdata; + + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + + msg.msg_name = &sin; + msg.msg_namelen = sizeof(struct sockaddr_in); + + res = recvmsg(sipsock, &msg, 0); + + for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { + if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == DSTADDR_SOCKOPT) { + memcpy(&sout.sin_addr, dstaddr(cmsgptr), sizeof(sout.sin_addr)); + break; + } + } + if (res < 0) { if (errno == EAGAIN) ast_log(LOG_NOTICE, "SIP: Received packet with bad UDP checksum\n"); @@ -7731,7 +7808,7 @@ /* Process request, with netlock held */ retrylock: ast_mutex_lock(&netlock); - p = find_call(&req, &sin); + p = find_call(&req, &sin, &sout); if (p) { /* Go ahead and lock the owner if it has one -- we may need it */ if (p->owner && ast_mutex_trylock(&p->owner->lock)) { @@ -7782,7 +7859,7 @@ return 0; } - p = sip_alloc(NULL, NULL, 0); + p = sip_alloc(NULL, NULL, NULL, 0); if (!p) { ast_log(LOG_WARNING, "Unable to build sip pvt data for MWI\n"); ast_mutex_unlock(&peerl.lock); @@ -8008,7 +8085,7 @@ ast_log(LOG_NOTICE, "Still have a call...\n"); sip_destroy(peer->call); } - p = peer->call = sip_alloc(NULL, NULL, 0); + p = peer->call = sip_alloc(NULL, NULL, NULL, 0); if (!peer->call) { ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name); return -1; @@ -8119,7 +8196,7 @@ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format %s while capability is %s\n", ast_getformatname(oldformat), ast_getformatname(global_capability)); return NULL; } - p = sip_alloc(NULL, NULL, 0); + p = sip_alloc(NULL, NULL, NULL, 0); if (!p) { ast_log(LOG_WARNING, "Unable to build sip pvt data for '%s'\n", (char *)data); return NULL; @@ -8632,6 +8709,7 @@ int format; int oldport = ntohs(bindaddr.sin_port); char iabuf[INET_ADDRSTRLEN]; + int dstaddr = 1; if (gethostname(ourhost, sizeof(ourhost))) { @@ -8925,6 +9003,10 @@ setsockopt(sipsock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuseFlag, sizeof reuseFlag); + + setsockopt(sipsock, SOL_IP, DSTADDR_SOCKOPT, + (const char*)&dstaddr, + sizeof(dstaddr)); if (bind(sipsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { ast_log(LOG_WARNING, "Failed to bind to %s:%d: %s\n",