Index: channels/chan_iax2.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_iax2.c,v
retrieving revision 1.231
diff -u -w -r1.231 chan_iax2.c
--- channels/chan_iax2.c	5 Jan 2005 23:05:49 -0000	1.231
+++ channels/chan_iax2.c	7 Jan 2005 16:17:03 -0000
@@ -342,6 +342,16 @@
 /* If we have less than this much excess real jitter buffer, enlarge it. */
 static int min_jitter_buffer = MIN_JITTER_BUFFER;
 
+struct iax_rr {
+	int jitter;
+	int losspct;
+	int losscnt;
+	int packets;
+	int delay;
+	int dropped;
+	int ooo;
+};
+
 struct chan_iax2_pvt {
 	/* Pipes for communication.  pipe[1] belongs to the
 	   network thread (write), and pipe[0] belongs to the individual 
@@ -485,6 +495,14 @@
 	int amaflags;
 	struct iax2_dpcache *dpentries;
 	struct ast_variable *vars;
+	/* last received remote rr */
+	struct iax_rr remote_rr;
+	/* Current base time: (just for stats) */
+	int min;
+	/* Dropped frame count: (just for stats) */
+	int frames_dropped;
+	/* received frame count: (just for stats) */
+	int frames_received;
 };
 
 static struct ast_iax2_queue {
@@ -1901,6 +1919,9 @@
 		iaxs[fr->callno]->jitterbuffer = max 
 			/* + ((float)iaxs[fr->callno]->jitter) * 0.1 */;
 
+	/* update "min", just for RRs and stats */
+	iaxs[fr->callno]->min = min; 
+
 	/* If the caller just wanted us to update, return now */
 	if (!reallydeliver)
 		return 0;
@@ -1938,6 +1959,7 @@
 		} else {
 			if (option_debug)
 				ast_log(LOG_DEBUG, "schedule_delivery: Dropping voice packet since %dms delay is too old\n", delay);
+			iaxs[fr->callno]->frames_dropped++;
 			/* Free our iax frame */
 			iax2_frame_free(fr);
 		}
@@ -3627,6 +3649,51 @@
 	return RESULT_SUCCESS;
 #undef FORMAT
 #undef FORMAT2
+#undef FORMATB
+}
+
+static int iax2_show_netstats(int fd, int argc, char *argv[])
+{
+	int x;
+	int numchans = 0;
+	char iabuf[INET_ADDRSTRLEN];
+	if (argc != 3)
+		return RESULT_SHOWUSAGE;
+	ast_cli(fd, "                                -------- LOCAL ---------------------  -------- REMOTE --------------------\n");
+	ast_cli(fd, "Channel                    RTT  Jit  Del  Lost   %%  Drop  OOO  Kpkts  Jit  Del  Lost   %%  Drop  OOO  Kpkts\n");
+	for (x=0;x<IAX_MAX_CALLS;x++) {
+		ast_mutex_lock(&iaxsl[x]);
+		if (iaxs[x]) {
+#ifdef BRIDGE_OPTIMIZATION
+			if (iaxs[x]->bridgecallno)
+				ast_cli(fd, "%-25.25s <NATIVE BRIDGED>",
+						iaxs[x]->owner ? iaxs[x]->owner->name : "(None)");
+			else
+#endif
+				ast_cli(fd, "%-25.25s %4d %4d %4d %5d %3d %5d %4d %6d %4d %4d %5d %3d %5d %4d %6d\n",
+						iaxs[x]->owner ? iaxs[x]->owner->name : "(None)",
+						iaxs[x]->pingtime,
+						iaxs[x]->jitter, 
+						ast_test_flag(iaxs[x], IAX_USEJITTERBUF) ? jitterbufsize(iaxs[x]) : 0,
+						-1,
+						-1,
+						-1,
+						-1,
+						iaxs[x]->frames_received/1000,
+						iaxs[x]->remote_rr.jitter,
+						iaxs[x]->remote_rr.delay,
+						iaxs[x]->remote_rr.losscnt,
+						iaxs[x]->remote_rr.losspct,
+						iaxs[x]->remote_rr.dropped,
+						iaxs[x]->remote_rr.ooo,
+						iaxs[x]->remote_rr.packets/1000
+				);
+			numchans++;
+		}
+		ast_mutex_unlock(&iaxsl[x]);
+	}
+	ast_cli(fd, "%d active IAX channel(s)\n", numchans);
+	return RESULT_SUCCESS;
 }
 
 static int iax2_do_trunk_debug(int fd, int argc, char *argv[])
@@ -3666,6 +3733,10 @@
 "Usage: iax2 show channels\n"
 "       Lists all currently active IAX channels.\n";
 
+static char show_netstats_usage[] = 
+"Usage: iax2 show netstats\n"
+"       Lists network status for all currently active IAX channels.\n";
+
 static char show_peers_usage[] = 
 "Usage: iax2 show peers\n"
 "       Lists all known IAX peers.\n";
@@ -3696,6 +3767,8 @@
 	{ { "iax2", "show", "firmware", NULL }, iax2_show_firmware, "Show available IAX firmwares", show_firmware_usage };
 static struct ast_cli_entry  cli_show_channels =
 	{ { "iax2", "show", "channels", NULL }, iax2_show_channels, "Show active IAX channels", show_channels_usage };
+static struct ast_cli_entry  cli_show_netstats =
+	{ { "iax2", "show", "netstats", NULL }, iax2_show_netstats, "Show active IAX channel netstats", show_netstats_usage };
 static struct ast_cli_entry  cli_show_peers =
 	{ { "iax2", "show", "peers", NULL }, iax2_show_peers, "Show defined IAX peers", show_peers_usage };
 static struct ast_cli_entry  cli_show_peers_include =
@@ -5264,6 +5337,30 @@
 	return 0;
 }
 
+static void construct_rr(struct chan_iax2_pvt *pvt, struct iax_ie_data *iep) {
+        memset(iep, 0, sizeof(*iep));
+	iax_ie_append_int(iep,IAX_IE_RR_JITTER, pvt->jitter);
+	iax_ie_append_int(iep,IAX_IE_RR_PKTS, pvt->frames_received);
+	if(!ast_test_flag(pvt, IAX_USEJITTERBUF)) 
+	    iax_ie_append_short(iep,IAX_IE_RR_DELAY, 0);
+	else
+	    iax_ie_append_short(iep,IAX_IE_RR_DELAY, pvt->jitterbuffer - pvt->min);
+	iax_ie_append_int(iep,IAX_IE_RR_DROPPED, pvt->frames_dropped);
+	/* don't know, don't send! iax_ie_append_int(&ied,IAX_IE_RR_OOO, 0); */
+	/* don't know, don't send! iax_ie_append_int(&ied,IAX_IE_RR_LOSS, 0); */
+
+}
+
+static void save_rr(struct iax_frame *fr, struct iax_ies *ies) {
+	iaxs[fr->callno]->remote_rr.jitter = ies->rr_jitter;
+	iaxs[fr->callno]->remote_rr.losspct = ies->rr_loss >> 24;
+	iaxs[fr->callno]->remote_rr.losscnt = ies->rr_loss & 0xffffff;
+	iaxs[fr->callno]->remote_rr.packets = ies->rr_pkts;
+	iaxs[fr->callno]->remote_rr.delay = ies->rr_delay;
+	iaxs[fr->callno]->remote_rr.dropped = ies->rr_dropped;
+	iaxs[fr->callno]->remote_rr.ooo = ies->rr_ooo;
+}
+
 static int socket_read(int *id, int fd, short events, void *cbdata)
 {
 	struct sockaddr_in sin;
@@ -5462,6 +5559,10 @@
 			iax_showframe(NULL, fh, 1, &sin, res - sizeof(struct ast_iax2_full_hdr));
 #endif
 	}
+
+	/* count this frame */
+	iaxs[fr.callno]->frames_received++;
+
 	if (!inaddrcmp(&sin, &iaxs[fr.callno]->addr) && !minivid &&
 		f.subclass != IAX_COMMAND_TXCNT &&		/* for attended transfer */
 		f.subclass != IAX_COMMAND_TXACC)		/* for attended transfer */
@@ -5922,12 +6023,18 @@
 					/* If we're in a bridged call, just forward this */
 					forward_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_PING, fr.ts, NULL, 0, -1);
 				} else {
+					struct iax_ie_data pingied;
+					construct_rr(iaxs[fr.callno], &pingied);
 					/* Send back a pong packet with the original timestamp */
-					send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr.ts, NULL, 0, -1);
+					send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr.ts, pingied.buf, pingied.pos, -1);
 				}
 #else				
+				{
+					struct iax_ie_data pingied;
+					construct_rr(iaxs[fr.callno], &pingied);
 				/* Send back a pong packet with the original timestamp */
-				send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr.ts, NULL, 0, -1);
+					send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_PONG, fr.ts, pingied.buf, pingied.pos, -1);
+				}
 #endif			
 				break;
 			case IAX_COMMAND_PONG:
@@ -5943,6 +6050,9 @@
 				/* Calculate ping time */
 				iaxs[fr.callno]->pingtime =  calc_timestamp(iaxs[fr.callno], 0, &f) - fr.ts;
 #endif
+				/* save RR info */
+				save_rr(&fr, &ies);
+
 				if (iaxs[fr.callno]->peerpoke) {
 					peer = iaxs[fr.callno]->peerpoke;
 					if ((peer->lastms < 0)  || (peer->lastms > peer->maxms)) {
@@ -7779,6 +7889,7 @@
 	ast_unregister_application(papp);
 	ast_cli_unregister(&cli_show_users);
 	ast_cli_unregister(&cli_show_channels);
+	ast_cli_unregister(&cli_show_netstats);
 	ast_cli_unregister(&cli_show_peers);
 	ast_cli_unregister(&cli_show_peers_include);
 	ast_cli_unregister(&cli_show_peers_exclude);
@@ -7857,6 +7968,7 @@
 
 	ast_cli_register(&cli_show_users);
 	ast_cli_register(&cli_show_channels);
+	ast_cli_register(&cli_show_netstats);
 	ast_cli_register(&cli_show_peers);
 	ast_cli_register(&cli_show_peers_include);
 	ast_cli_register(&cli_show_peers_exclude);
Index: channels/iax2-parser.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/iax2-parser.c,v
retrieving revision 1.33
diff -u -w -r1.33 iax2-parser.c
--- channels/iax2-parser.c	29 Dec 2004 23:02:21 -0000	1.33
+++ channels/iax2-parser.c	7 Jan 2005 16:17:03 -0000
@@ -761,6 +761,54 @@
 			} else
 				ies->calling_tns = ntohs(get_uint16(data + 2));	
 			break;
+               case IAX_IE_RR_JITTER:
+                       if (len != (int)sizeof(unsigned int)) {
+                               snprintf(tmp, (int)sizeof(tmp), "Expected jitter rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+                               errorf(tmp);
+                       } else {
+                               ies->rr_jitter = ntohl(get_uint32(data + 2));
+                       }
+                       break;
+               case IAX_IE_RR_LOSS:
+                       if (len != (int)sizeof(unsigned int)) {
+                               snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+                               errorf(tmp);
+                       } else {
+                               ies->rr_loss = ntohl(get_uint32(data + 2));
+                       }
+                       break;
+               case IAX_IE_RR_PKTS:
+                       if (len != (int)sizeof(unsigned int)) {
+                               snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+                               errorf(tmp);
+                       } else {
+                               ies->rr_pkts = ntohl(get_uint32(data + 2));
+                       }
+                       break;
+               case IAX_IE_RR_DELAY:
+                       if (len != (int)sizeof(unsigned short)) {
+                               snprintf(tmp, (int)sizeof(tmp), "Expected loss rr to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+                        errorf(tmp);
+                       } else {
+                               ies->rr_delay = ntohs(get_uint16(data + 2));
+                       }
+                       break;
+		case IAX_IE_RR_DROPPED:
+			if (len != (int)sizeof(unsigned int)) {
+				snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+				errorf(tmp);
+			} else {
+				ies->rr_dropped = ntohl(get_uint32(data + 2));
+			}
+			break;
+		case IAX_IE_RR_OOO:
+			if (len != (int)sizeof(unsigned int)) {
+				snprintf(tmp, (int)sizeof(tmp), "Expected packets rr to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+				errorf(tmp);
+			} else {
+				ies->rr_ooo = ntohl(get_uint32(data + 2));
+			}
+			break;
 		default:
 			snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len);
 			outputf(tmp);
Index: channels/iax2-parser.h
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/iax2-parser.h,v
retrieving revision 1.13
diff -u -w -r1.13 iax2-parser.h
--- channels/iax2-parser.h	29 Dec 2004 23:02:21 -0000	1.13
+++ channels/iax2-parser.h	7 Jan 2005 16:17:03 -0000
@@ -60,6 +60,12 @@
 	unsigned int provver;
 	unsigned short samprate;
 	int provverpres;
+	unsigned int rr_jitter;
+	unsigned int rr_loss;
+	unsigned int rr_pkts;
+	unsigned short rr_delay;
+	unsigned int rr_dropped;
+	unsigned int rr_ooo;
 };
 
 #define DIRECTION_INGRESS 1
Index: channels/iax2.h
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/iax2.h,v
retrieving revision 1.19
diff -u -w -r1.19 iax2.h
--- channels/iax2.h	29 Dec 2004 23:02:21 -0000	1.19
+++ channels/iax2.h	7 Jan 2005 16:17:03 -0000
@@ -120,6 +120,14 @@
 #define IAX_IE_ENCRYPTION			43		/* Encryption format (u16) */
 #define IAX_IE_ENCKEY				44		/* Encryption key (raw) */
 
+#define IAX_IE_RR_JITTER			64		/* Received jitter (as in RFC1889) u32 */
+#define IAX_IE_RR_LOSS				65		/* Received loss (high byte loss pct, low 24 bits loss count, as in rfc1889 */
+#define IAX_IE_RR_PKTS				66		/* Received frames (total frames received) u32 */
+#define IAX_IE_RR_DELAY				67		/* Max playout delay for received frames (in ms) u16 */
+#define IAX_IE_RR_DROPPED			68		/* Dropped frames (presumably by jitterbuf) u32 */
+#define IAX_IE_RR_OOO				69		/* Frames received Out of Order u32 */
+
+
 #define IAX_AUTH_PLAINTEXT			(1 << 0)
 #define IAX_AUTH_MD5				(1 << 1)
 #define IAX_AUTH_RSA				(1 << 2)
