diff --git a/daemon/media_socket.c b/daemon/media_socket.c
index cbb5a47a8..cde5ec55d 100644
--- a/daemon/media_socket.c
+++ b/daemon/media_socket.c
@@ -53,8 +53,6 @@
 		else								\
 			diff_ ## x ## _ ## io = (ke)->x - ks_val;		\
 		atomic64_add(&ps->stats_ ## io.x, diff_ ## x ## _ ## io);	\
-		if (ps->selected_sfd) \
-			atomic64_add_na(&ps->selected_sfd->local_intf->stats->io.x, diff_ ## x ## _ ## io); \
 		RTPE_STATS_ADD(x ## _kernel, diff_ ## x ## _ ## io);		\
 	} while (0)
 
@@ -1487,6 +1485,7 @@ static const char *kernelize_one(struct rtpengine_target_info *reti, GQueue *out
 	}
 
 	__re_address_translate_ep(&reti->local, &stream->selected_sfd->socket.local);
+	reti->iface_stats = stream->selected_sfd->local_intf->stats;
 	reti->rtcp_mux = MEDIA_ISSET(media, RTCP_MUX);
 	reti->rtcp = PS_ISSET(stream, RTCP);
 	reti->dtls = MEDIA_ISSET(media, DTLS);
@@ -1622,6 +1621,7 @@ output:
 
 	__re_address_translate_ep(&redi->output.dst_addr, &sink->endpoint);
 	__re_address_translate_ep(&redi->output.src_addr, &sink->selected_sfd->socket.local);
+	redi->output.iface_stats = sink->selected_sfd->local_intf->stats;
 
 	if (reti->track_ssrc) {
 		for (unsigned int u = 0; u < G_N_ELEMENTS(stream->ssrc_in); u++) {
diff --git a/include/kernel.h b/include/kernel.h
index 5d875b18b..0536ee1ed 100644
--- a/include/kernel.h
+++ b/include/kernel.h
@@ -6,6 +6,7 @@
 #include <netinet/in.h>
 
 #include "containers.h"
+#include "auxlib.h"
 
 #include "xt_RTPENGINE.h"
 
diff --git a/kernel-module/common_stats.h b/kernel-module/common_stats.h
index e0db7dd95..d4571de04 100644
--- a/kernel-module/common_stats.h
+++ b/kernel-module/common_stats.h
@@ -2,6 +2,12 @@
 #define _RTPE_COMMON_STATS_H_
 
 
+#ifdef __KERNEL__
+typedef atomic64_t atomic64;
+static_assert(sizeof(atomic64_t) == sizeof(int64_t), "atomic64_t != int64_t");
+#endif
+
+
 struct interface_counter_stats_dir {
 #define F(n) atomic64 n;
 #include "interface_counter_stats_fields_dir.inc"
diff --git a/kernel-module/xt_RTPENGINE.c b/kernel-module/xt_RTPENGINE.c
index 3a87ce4bc..3f5d2abf8 100644
--- a/kernel-module/xt_RTPENGINE.c
+++ b/kernel-module/xt_RTPENGINE.c
@@ -2008,6 +2008,26 @@ static int is_valid_address(const struct re_address *rea) {
 	return 1;
 }
 
+static void vm_mmap_close(struct vm_area_struct *vma) {
+}
+static const struct vm_operations_struct vm_mmap_ops = {
+	.close = vm_mmap_close,
+};
+
+static void *shm_map_resolve(void *p, size_t size) {
+	struct vm_area_struct *vma;
+	// XXX is there a better way to map this to the kernel address?
+	vma = vma_lookup(current->mm, (unsigned long) p);
+	if (!vma)
+		return NULL;
+	if (!vma->vm_private_data)
+		return NULL;
+	if ((unsigned long) p + size > vma->vm_end || (unsigned long) p + size < vma->vm_start)
+		return NULL;
+	if (vma->vm_ops != &vm_mmap_ops)
+		return NULL;
+	return vma->vm_private_data + ((unsigned long) p - (unsigned long) vma->vm_start);
+}
 
 
 
@@ -2404,6 +2424,7 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i
 	int err;
 	unsigned long flags;
 	unsigned int u;
+	struct interface_stats_block *iface_stats;
 
 	/* validation */
 
@@ -2424,6 +2445,10 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i
 	if (validate_srtp(&i->decrypt))
 		return -EINVAL;
 
+	iface_stats = shm_map_resolve(i->iface_stats, sizeof(*iface_stats));
+	if (!iface_stats)
+		return -EFAULT;
+
 	DBG("Creating new target\n");
 
 	/* initializing */
@@ -2444,6 +2469,7 @@ static int table_new_target(struct rtpengine_table *t, struct rtpengine_target_i
 	for (u = 0; u < RTPE_NUM_SSRC_TRACKING; u++)
 		g->ssrc_stats[u].lost_bits = -1;
 	rwlock_init(&g->outputs_lock);
+	g->target.iface_stats = iface_stats;
 
 	if (i->num_destinations) {
 		err = -ENOMEM;
@@ -2562,6 +2588,7 @@ static int table_add_destination(struct rtpengine_table *t, struct rtpengine_des
 	unsigned long flags;
 	int err;
 	struct rtpengine_target *g;
+	struct interface_stats_block *iface_stats;
 
 	// validate input
 
@@ -2574,6 +2601,10 @@ static int table_add_destination(struct rtpengine_table *t, struct rtpengine_des
 	if (validate_srtp(&i->output.encrypt))
 		return -EINVAL;
 
+	iface_stats = shm_map_resolve(i->output.iface_stats, sizeof(*iface_stats));
+	if (!iface_stats)
+		return -EFAULT;
+
 	g = get_target(t, &i->local);
 	if (!g)
 		return -ENOENT;
@@ -2597,6 +2628,7 @@ static int table_add_destination(struct rtpengine_table *t, struct rtpengine_des
 		goto out;
 
 	g->outputs[i->num].output = i->output;
+	g->outputs[i->num].output.iface_stats = iface_stats;
 
 	// init crypto stuff lock free: the "output" is already filled so we
 	// know it's there, but outputs_unfilled hasn't been decreased yet, so
@@ -2734,8 +2766,6 @@ static ssize_t proc_main_control_write(struct file *file, const char __user *buf
 }
 
 
-
-
 static int proc_control_mmap(struct file *file, struct vm_area_struct *vma) {
 	size_t size, order;
 	unsigned long pfn;
@@ -2801,6 +2831,7 @@ static int proc_control_mmap(struct file *file, struct vm_area_struct *vma) {
 
 	pfn = virt_to_phys(pages) >> PAGE_SHIFT;
 	vma->vm_private_data = pages; // remember kernel-space address
+	vma->vm_ops = &vm_mmap_ops;
 
 	ret = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
 
@@ -5458,6 +5489,7 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct sk_buff *oskb,
 			if (!skb2) {
 				log_err("out of memory while creating skb copy");
 				atomic64_inc(&g->stats_in.errors);
+				atomic64_inc(&g->target.iface_stats->in.errors);
 				continue;
 			}
 			skb_gso_reset(skb2);
@@ -5474,11 +5506,15 @@ static unsigned int rtpengine46(struct sk_buff *skb, struct sk_buff *oskb,
 		err = send_proxy_packet_output(skb2, g, rtp_pt_idx, o, &rtp2, ssrc_idx, par);
 		if (err) {
 			atomic64_inc(&g->stats_in.errors);
+			atomic64_inc(&g->target.iface_stats->in.errors);
 			atomic64_inc(&o->stats_out.errors);
+			atomic64_inc(&o->output.iface_stats->out.errors);
 		}
 		else {
 			atomic64_inc(&o->stats_out.packets);
 			atomic64_add(datalen_out, &o->stats_out.bytes);
+			atomic64_inc(&o->output.iface_stats->out.packets);
+			atomic64_add(datalen_out, &o->output.iface_stats->out.bytes);
 		}
 	}
 
@@ -5488,6 +5524,8 @@ do_stats:
 
 	atomic64_inc(&g->stats_in.packets);
 	atomic64_add(datalen, &g->stats_in.bytes);
+	atomic64_inc(&g->target.iface_stats->in.packets);
+	atomic64_add(datalen, &g->target.iface_stats->in.bytes);
 
 	if (rtp_pt_idx >= 0) {
 		atomic64_inc(&g->rtp_stats[rtp_pt_idx].packets);
@@ -5495,8 +5533,10 @@ do_stats:
 	}
 	else if (rtp_pt_idx == -2)
 		/* not RTP */ ;
-	else if (rtp_pt_idx == -1)
+	else if (rtp_pt_idx == -1) {
 		atomic64_inc(&g->stats_in.errors);
+		atomic64_inc(&g->target.iface_stats->in.errors);
+	}
 
 	target_put(g);
 	table_put(t);
@@ -5508,6 +5548,7 @@ do_stats:
 out_error:
 	log_err("x_tables action failed: %s", errstr);
 	atomic64_inc(&g->stats_in.errors);
+	atomic64_inc(&g->target.iface_stats->in.errors);
 out:
 	target_put(g);
 out_no_target:
diff --git a/kernel-module/xt_RTPENGINE.h b/kernel-module/xt_RTPENGINE.h
index 650a8ec14..9931c1384 100644
--- a/kernel-module/xt_RTPENGINE.h
+++ b/kernel-module/xt_RTPENGINE.h
@@ -2,6 +2,8 @@
 #define XT_RTPPROXY_H
 
 
+#include "common_stats.h"
+
 
 #define RTPE_NUM_PAYLOAD_TYPES 32
 #define RTPE_MAX_FORWARD_DESTINATIONS 32
@@ -116,6 +118,8 @@ struct rtpengine_target_info {
 	struct rtpengine_pt_input	pt_input[RTPE_NUM_PAYLOAD_TYPES]; /* must be sorted */
 	unsigned int			num_payload_types;
 
+	struct interface_stats_block	*iface_stats; // for ingress stats
+
 	unsigned int			rtcp_mux:1,
 					dtls:1,
 					stun:1,
@@ -141,6 +145,8 @@ struct rtpengine_output_info {
 	uint32_t			seq_offset[RTPE_NUM_SSRC_TRACKING]; // Rewrite output seq
 	struct rtpengine_pt_output	pt_output[RTPE_NUM_PAYLOAD_TYPES]; // same indexes as pt_input
 
+	struct interface_stats_block	*iface_stats; // for egress stats
+
 	unsigned char			tos;
 	unsigned int			ssrc_subst:1;
 };
diff --git a/lib/socket.c b/lib/socket.c
index 1efdaa4a5..382be607e 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -8,6 +8,7 @@
 #include <netinet/udp.h>
 #include <sys/socket.h>
 #include "str.h"
+#include "auxlib.h"
 #include "xt_RTPENGINE.h"
 #include "log.h"