diff -urpN src.org/kdc/network.c src.udp/kdc/network.c
--- src.org/kdc/network.c	Tue Jul 12 23:59:52 2005
+++ src.udp/kdc/network.c	Mon Dec 19 00:25:04 2005
@@ -485,6 +485,19 @@ setup_tcp_listener_ports(struct socksetu
     return 0;
 }
 
+//vda
+static void
+socket_want_pktinfo(int s)
+{
+    int sockopt = 1;
+#ifdef IP_PKTINFO
+    setsockopt(s, IPPROTO_IP, IP_PKTINFO, &sockopt, sizeof(sockopt));
+#endif
+#ifdef IPV6_PKTINFO
+    setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &sockopt, sizeof(sockopt));
+#endif
+}
+
 static int
 setup_udp_port(void *P_data, struct sockaddr *addr)
 {
@@ -549,6 +562,8 @@ setup_udp_port(void *P_data, struct sock
 		    port, haddrbuf);
 	    return 1;
 	}
+//vda
+	socket_want_pktinfo (sock);
 	FD_SET (sock, &sstate.rfds);
 	if (sock >= sstate.max)
 	    sstate.max = sock + 1;
@@ -659,13 +674,28 @@ setup_network(const char *prog)
     setup_data.prog = prog;
     setup_data.retval = 0;
     krb5_klog_syslog (LOG_INFO, "setting up network...");
-    /* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO,
-       so we might need only one UDP socket; fall back to binding
-       sockets on each address only if IPV6_PKTINFO isn't
-       supported.  */
+//vda
+#ifdef IP_PKTINFO
+    /* bind to 0.0.0.0 and use IP_PKTINFO to detect dst ip */
+    struct sockaddr_in sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sin_family = AF_INET;
+    setup_udp_port(&setup_data, (struct sockaddr*)&sa);
+#ifdef KRB5_USE_INET6
+#ifdef IPV6_PKTINFO
+    struct sockaddr_in6 sa6;
+    memset(&sa6, 0, sizeof(sa6));
+    sa6.sin6_family = AF_INET6;
+    setup_udp_port(&setup_data, (struct sockaddr*)&sa6);
+#endif
+#endif
+#else
+    /* Old and buggy way */
     if (foreach_localaddr (&setup_data, setup_udp_port, 0, 0)) {
 	return setup_data.retval;
     }
+#endif
+
     setup_tcp_listener_ports(&setup_data);
     krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets);
     if (n_sockets == 0) {
@@ -676,6 +706,7 @@ setup_network(const char *prog)
     return 0;
 }
 
+//vda
 static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa)
 {
     switch (sa->sa_family) {
@@ -708,14 +739,157 @@ static void init_addr(krb5_fulladdr *fad
     }
 }
 
+//vda
+static int
+recv_from_to(int s, void *buf, size_t len, int flags,
+	struct sockaddr *from, socklen_t *fromlen,
+	struct sockaddr *to, socklen_t *tolen)
+{
+#ifndef IP_PKTINFO
+    return recvfrom(s, buf, len, flags, from, fromlen);
+#else
+    int r;
+
+    struct iovec iov[1];
+    struct sockaddr_in remote;
+    char cmsg[CMSG_SPACE(
+		sizeof(struct in6_pktinfo) > sizeof(struct in_pktinfo) ?
+		sizeof(struct in6_pktinfo) : sizeof(struct in_pktinfo)
+	)];
+    struct cmsghdr *cmsgptr;
+    struct msghdr msg;
+
+    if(!to || !tolen)
+	return recvfrom(s, buf, len, flags, from, fromlen);
+
+    iov[0].iov_base = buf;
+    iov[0].iov_len = len;
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = from;
+    msg.msg_namelen = *fromlen;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = &cmsg;
+    msg.msg_controllen = sizeof(cmsg);
+
+    r = recvmsg(s, &msg, flags);
+    if (r < 0)
+	return r;
+
+    /* Here we try to retrieve destination IP and memorize it */
+    cmsgptr = CMSG_FIRSTHDR(&msg);
+    if (*tolen >= sizeof(struct sockaddr_in)) {
+	while (cmsgptr) {
+	    if (cmsgptr->cmsg_level == IPPROTO_IP
+	    && cmsgptr->cmsg_type == IP_PKTINFO) {
+		struct in_pktinfo* pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsgptr);
+		to->sa_family = AF_INET;
+		((struct sockaddr_in*)to)->sin_addr = pktinfo->ipi_addr;
+		*tolen = sizeof(struct sockaddr_in);
+		return r;
+	    }
+#ifdef IPV6_PKTINFO
+	    if (cmsgptr->cmsg_level == IPPROTO_IPV6
+	    && cmsgptr->cmsg_type == IPV6_PKTINFO
+	    && *tolen >= sizeof(struct sockaddr_in6)) {
+		struct in6_pktinfo* pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsgptr);
+		to->sa_family = AF_INET6;
+		((struct sockaddr_in6*)to)->sin6_addr = pktinfo->ipi6_addr;
+		*tolen = sizeof(struct sockaddr_in6);
+		return r;
+	    }
+#endif
+	    cmsgptr = CMSG_NXTHDR(&msg, cmsgptr);
+	}
+    }
+    /* No info about dst addr was available */
+    *tolen = 0;
+    return r;
+#endif
+}
+
+static int
+send_to_from(int fd, void *buf, size_t len, int flags,
+	const struct sockaddr *to, socklen_t tolen,
+	const struct sockaddr *from, socklen_t fromlen)
+{
+#ifndef IP_PKTINFO
+    return sendto(fd, buf, len, flags, to, tolen);
+#else
+    struct iovec iov[1];
+    struct msghdr msg;
+    struct cmsghdr* cmsgptr;
+    char cbuf[CMSG_SPACE(
+		sizeof(struct in6_pktinfo) > sizeof(struct in_pktinfo) ?
+		sizeof(struct in6_pktinfo) : sizeof(struct in_pktinfo)
+	)];
+
+    if(!from || !fromlen)
+	return sendto(fd, buf, len, flags, to, tolen);
+
+    if(from->sa_family != AF_INET && from->sa_family != AF_INET6)
+	return sendto(fd, buf, len, flags, to, tolen);
+
+    /* read man recvmsg and man cmsg to make sense of the code below */
+
+    iov[0].iov_base = buf;
+    iov[0].iov_len = len;
+    memset(cbuf, 0, sizeof(cbuf));
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = (void *)to; /* or compiler will annoy us */
+    msg.msg_namelen = tolen;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = cbuf;
+    /* msg.msg_flags = 0;   -- already done by memset(0) */
+
+    /* Or else CMSG_FIRSTHDR will return NULL */
+    msg.msg_controllen = sizeof(cbuf);
+    cmsgptr = CMSG_FIRSTHDR(&msg);
+    msg.msg_controllen = 0;
+
+    if (from->sa_family == AF_INET) {
+	if (to->sa_family == AF_INET) {
+	    struct in_pktinfo *pktinfo;
+	    cmsgptr->cmsg_level = IPPROTO_IP;
+	    cmsgptr->cmsg_type = IP_PKTINFO;
+	    cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+	    pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsgptr);
+	    /* pktinfo->ipi_ifindex = 0;   -- already done by memset(0) */
+	    pktinfo->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
+	    msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+	}
+    }
+#ifdef IPV6_PKTINFO
+    else { /* from->sa_family == AF_INET6 */
+	if (to->sa_family == AF_INET6) {
+	    struct in6_pktinfo *pktinfo;
+	    cmsgptr->cmsg_level = IPPROTO_IPV6;
+	    cmsgptr->cmsg_type = IPV6_PKTINFO;
+	    cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+	    pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsgptr);
+	    /* pktinfo->ipi6_ifindex = 0;   -- already done by memset(0) */
+	    pktinfo->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr;
+	    msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+	}
+    }
+#endif
+    return sendmsg(fd, &msg, flags);
+#endif
+}
+
 static void process_packet(struct connection *conn, const char *prog,
 			   int selflags)
 {
     int cc;
     socklen_t saddr_len;
+    socklen_t daddr_len;
     krb5_fulladdr faddr;
     krb5_error_code retval;
     struct sockaddr_storage saddr;
+    struct sockaddr_storage daddr;
     krb5_address addr;
     krb5_data request;
     krb5_data *response;
@@ -724,8 +898,11 @@ static void process_packet(struct connec
 
     response = NULL;
     saddr_len = sizeof(saddr);
-    cc = recvfrom(port_fd, pktbuf, sizeof(pktbuf), 0,
-		  (struct sockaddr *)&saddr, &saddr_len);
+    daddr_len = sizeof(daddr);
+
+    cc = recv_from_to(port_fd, pktbuf, sizeof(pktbuf), 0,
+		  (struct sockaddr *)&saddr, &saddr_len,
+		  (struct sockaddr *)&daddr, &daddr_len);
     if (cc == -1) {
 	if (errno != EINTR
 	    /* This is how Linux indicates that a previous
@@ -748,8 +925,9 @@ static void process_packet(struct connec
 	com_err(prog, retval, "while dispatching (udp)");
 	return;
     }
-    cc = sendto(port_fd, response->data, (socklen_t) response->length, 0,
-		(struct sockaddr *)&saddr, saddr_len);
+    cc = send_to_from(port_fd, response->data, (socklen_t) response->length, 0,
+		(struct sockaddr *)&saddr, saddr_len,
+		(struct sockaddr *)&daddr, daddr_len);
     if (cc == -1) {
 	char addrbuf[46];
         krb5_free_data(kdc_context, response);
diff -urpN src.org/lib/rpc/svc.h src.udp/lib/rpc/svc.h
--- src.org/lib/rpc/svc.h	Tue Sep 21 21:20:16 2004
+++ src.udp/lib/rpc/svc.h	Sun Dec 18 20:32:01 2005
@@ -98,7 +98,10 @@ typedef struct SVCXPRT {
 	void		*xp_p1;		 /* private */
 	void		*xp_p2;		 /* private */
 	int		xp_laddrlen;	 /* lenght of local address */
+//vda: this is not enough to hold ipv6 addr!!! Should we use sockaddr_storage?
+// Using ugly hack for now...
 	struct sockaddr_in xp_laddr;	 /* local address */
+	char		bogus_pad[16]; 	 //[sizeof(sockaddr_in6) - sizeof(sockaddr_in)]
 } SVCXPRT;
 
 /*
diff -urpN src.org/lib/rpc/svc_udp.c src.udp/lib/rpc/svc_udp.c
--- src.org/lib/rpc/svc_udp.c	Tue Sep 21 21:20:16 2004
+++ src.udp/lib/rpc/svc_udp.c	Mon Dec 19 00:26:16 2005
@@ -176,12 +176,153 @@ svcudp_stat(SVCXPRT *xprt)
 	return (XPRT_IDLE); 
 }
 
+//vda
+static int
+recv_from_to(int s, void *buf, size_t len, int flags,
+	struct sockaddr *from, socklen_t *fromlen,
+	struct sockaddr *to, socklen_t *tolen)
+{
+#ifndef IP_PKTINFO
+    return recvfrom(s, buf, len, flags, from, fromlen);
+#else
+    int r;
+
+    struct iovec iov[1];
+    struct sockaddr_in remote;
+    char cmsg[CMSG_SPACE(
+		sizeof(struct in6_pktinfo) > sizeof(struct in_pktinfo) ?
+		sizeof(struct in6_pktinfo) : sizeof(struct in_pktinfo)
+	)];
+    struct cmsghdr *cmsgptr;
+    struct msghdr msg;
+
+    if(!to || !tolen)
+	return recvfrom(s, buf, len, flags, from, fromlen);
+
+    iov[0].iov_base = buf;
+    iov[0].iov_len = len;
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = from;
+    msg.msg_namelen = *fromlen;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = &cmsg;
+    msg.msg_controllen = sizeof(cmsg);
+
+    r = recvmsg(s, &msg, flags);
+    if (r < 0)
+	return r;
+
+    /* Here we try to retrieve destination IP and memorize it */
+    cmsgptr = CMSG_FIRSTHDR(&msg);
+    if (*tolen >= sizeof(struct sockaddr_in)) {
+	while (cmsgptr) {
+	    if (cmsgptr->cmsg_level == IPPROTO_IP
+	    && cmsgptr->cmsg_type == IP_PKTINFO) {
+		struct in_pktinfo* pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsgptr);
+		to->sa_family = AF_INET;
+		((struct sockaddr_in*)to)->sin_addr = pktinfo->ipi_addr;
+		*tolen = sizeof(struct sockaddr_in);
+		return r;
+	    }
+#ifdef IPV6_PKTINFO
+	    if (cmsgptr->cmsg_level == IPPROTO_IPV6
+	    && cmsgptr->cmsg_type == IPV6_PKTINFO
+	    && *tolen >= sizeof(struct sockaddr_in6)) {
+		struct in6_pktinfo* pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsgptr);
+		to->sa_family = AF_INET6;
+		((struct sockaddr_in6*)to)->sin6_addr = pktinfo->ipi6_addr;
+		*tolen = sizeof(struct sockaddr_in6);
+		return r;
+	    }
+#endif
+	    cmsgptr = CMSG_NXTHDR(&msg, cmsgptr);
+	}
+    }
+    /* No info about dst addr was available */
+    *tolen = 0;
+    return r;
+#endif
+}
+
+static int
+send_to_from(int fd, void *buf, size_t len, int flags,
+	const struct sockaddr *to, socklen_t tolen,
+	const struct sockaddr *from, socklen_t fromlen)
+{
+#ifndef IP_PKTINFO
+    return sendto(fd, buf, len, flags, to, tolen);
+#else
+    struct iovec iov[1];
+    struct msghdr msg;
+    struct cmsghdr* cmsgptr;
+    char cbuf[CMSG_SPACE(
+		sizeof(struct in6_pktinfo) > sizeof(struct in_pktinfo) ?
+		sizeof(struct in6_pktinfo) : sizeof(struct in_pktinfo)
+	)];
+
+    if(!from || !fromlen)
+	return sendto(fd, buf, len, flags, to, tolen);
+
+    if(from->sa_family != AF_INET && from->sa_family != AF_INET6)
+	return sendto(fd, buf, len, flags, to, tolen);
+
+    /* read man recvmsg and man cmsg to make sense of the code below */
+
+    iov[0].iov_base = buf;
+    iov[0].iov_len = len;
+    memset(cbuf, 0, sizeof(cbuf));
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_name = (void *)to; /* or compiler will annoy us */
+    msg.msg_namelen = tolen;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = cbuf;
+    /* msg.msg_flags = 0;   -- already done by memset(0) */
+
+    /* Or else CMSG_FIRSTHDR will return NULL */
+    msg.msg_controllen = sizeof(cbuf);
+    cmsgptr = CMSG_FIRSTHDR(&msg);
+    msg.msg_controllen = 0;
+
+    if (from->sa_family == AF_INET) {
+	if (to->sa_family == AF_INET) {
+	    struct in_pktinfo *pktinfo;
+	    cmsgptr->cmsg_level = IPPROTO_IP;
+	    cmsgptr->cmsg_type = IP_PKTINFO;
+	    cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+	    pktinfo = ((struct in_pktinfo *)(CMSG_DATA(cmsgptr)));
+	    /* pktinfo->ipi_ifindex = 0;   -- already done by memset(0) */
+	    pktinfo->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
+	    msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+	}
+    }
+#ifdef IPV6_PKTINFO
+    else { /* from->sa_family == AF_INET6 */
+	if (to->sa_family == AF_INET6) {
+	    struct in6_pktinfo *pktinfo;
+	    cmsgptr->cmsg_level = IPPROTO_IPV6;
+	    cmsgptr->cmsg_type = IPV6_PKTINFO;
+	    cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+	    pktinfo = ((struct in6_pktinfo *)(CMSG_DATA(cmsgptr)));
+	    /* pktinfo->ipi6_ifindex = 0;   -- already done by memset(0) */
+	    pktinfo->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr;
+	    msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+	}
+    }
+#endif
+    return sendmsg(fd, &msg, flags);
+#endif
+}
+
 static bool_t
 svcudp_recv(
 	register SVCXPRT *xprt,
 	struct rpc_msg *msg)
 {
-        struct msghdr dummy;
+	struct msghdr dummy;
 	struct iovec dummy_iov[1];
 	register struct svcudp_data *su = su_data(xprt);
 	register XDR *xdrs = &(su->su_xdrs);
@@ -206,8 +347,10 @@ svcudp_recv(
 	}
 	
 	xprt->xp_addrlen = sizeof(struct sockaddr_in);
-	rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz,
-	    0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen));
+	xprt->xp_laddrlen = sizeof(xprt->xp_laddr);
+	rlen = recv_from_to(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz,
+	    0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen),
+	    (struct sockaddr *)&(xprt->xp_laddr), &(xprt->xp_laddrlen));
 	if (rlen == -1 && errno == EINTR)
 		goto again;
 	if (rlen < (int) 4*sizeof(uint32_t))
@@ -219,8 +362,9 @@ svcudp_recv(
 	su->su_xid = msg->rm_xid;
 	if (su->su_cache != NULL) {
 		if (cache_get(xprt, msg, &reply, &replylen)) {
-			(void) sendto(xprt->xp_sock, reply, (int) replylen, 0,
-			  (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen);
+			(void) send_to_from(xprt->xp_sock, reply, (int) replylen, 0,
+			  (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen,
+			  (struct sockaddr *) &xprt->xp_laddr, xprt->xp_laddrlen);
 			return (TRUE);
 		}
 	}
@@ -258,6 +402,7 @@ static bool_t svcudp_reply(
 	 (!has_args ||
 	  (SVCAUTH_WRAP(xprt->xp_auth, xdrs, xdr_results, xdr_location)))) {
 	  slen = (int)XDR_GETPOS(xdrs);
+//vda: use send_to_from instead?
 	  if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,
 		     (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen)
 	      == slen) {
