diff --git a/misc/ss.c b/misc/ss.c
index 1b3ef90..f563710 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -34,6 +34,9 @@
 #include "libnetlink.h"
 #include "namespace.h"
 #include "SNAPSHOT.h"
+#include "ss_out_fmt.h"
+#include "ss_json_fmt.h"
+#include "ss_types.h"
 
 #include <linux/tcp.h>
 #include <linux/sock_diag.h>
@@ -101,6 +104,7 @@ int show_sock_ctx = 0;
 /* If show_users & show_proc_ctx only do user_ent_hash_build() once */
 int user_ent_hash_build_init = 0;
 int follow_events = 0;
+int json_output = 0;
 
 int netid_width;
 int state_width;
@@ -714,7 +718,6 @@ static int is_ephemeral(int port)
 	return (port >= ip_local_port_min && port<= ip_local_port_max);
 }
 
-
 static const char *__resolve_service(int port)
 {
 	struct scache *c;
@@ -3064,6 +3067,9 @@ static int print_summary(void)
 
 	printf("\n");
 
+	if (json_output && has_successor)
+		printf(",\n");
+
 	return 0;
 }
 
@@ -3090,6 +3096,7 @@ static void _usage(FILE *dest)
 "   -z, --contexts      display process and socket SELinux security contexts\n"
 "   -N, --net           switch to the specified network namespace name\n"
 "\n"
+"   -j, --json          format output in JSON\n"
 "   -4, --ipv4          display only IP version 4 sockets\n"
 "   -6, --ipv6          display only IP version 6 sockets\n"
 "   -0, --packet        display PACKET sockets\n"
@@ -3189,6 +3196,7 @@ static const struct option long_opts[] = {
 	{ "help", 0, 0, 'h' },
 	{ "context", 0, 0, 'Z' },
 	{ "contexts", 0, 0, 'z' },
+	{ "json", 0, 0, 'j' },
 	{ "net", 1, 0, 'N' },
 	{ 0 }
 
@@ -3204,7 +3212,7 @@ int main(int argc, char *argv[])
 	int ch;
 	int state_filter = 0;
 
-	while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:",
+	while ((ch = getopt_long(argc, argv, "dhaletuwxnro460spbEf:miA:D:F:vVzZN:j",
 				 long_opts, NULL)) != EOF) {
 		switch(ch) {
 		case 'n':
@@ -3383,6 +3391,10 @@ int main(int argc, char *argv[])
 			if (netns_switch(optarg))
 				exit(1);
 			break;
+		case 'j':
+			fmt_type = FMT_JSON;
+			json_output = 1;
+			break;
 		case 'h':
 		case '?':
 			help();
@@ -3464,11 +3476,34 @@ int main(int argc, char *argv[])
 				exit(-1);
 			}
 		}
+		printf("\"TCP\": [\n");
 		inet_show_netlink(&current_filter, dump_fp, IPPROTO_TCP);
+		res_json_fmt_branch(current_filter.dbs & (1<<NETLINK_DB) ||
+				current_filter.dbs & PACKET_DBM ||
+				current_filter.dbs & UNIX_DBM ||
+				current_filter.dbs & (1<<RAW_DB) ||
+				current_filter.dbs & (1<<UDP_DB) ||
+				current_filter.dbs & (1<<TCP_DB) ||
+				current_filter.dbs & (1<<DCCP_DB), ']');
 		fflush(dump_fp);
 		exit(0);
 	}
 
+	if (do_summary) {
+		print_summary(current_filter.dbs & PACKET_DBM ||
+				current_filter.dbs & UNIX_DBM ||
+				current_filter.dbs & (1<<RAW_DB) ||
+				current_filter.dbs & (1<<UDP_DB) ||
+				current_filter.dbs & (1<<TCP_DB) ||
+				current_filter.dbs & (1<<DCCP_DB));
+		if (do_default && argc == 0) {
+			if (json_output) {
+				printf("}\n");
+			}
+			exit(0);
+		}
+	}
+
 	if (ssfilter_parse(&current_filter.f, argc, argv, filter_fp))
 		usage();
 
@@ -3490,62 +3525,141 @@ int main(int argc, char *argv[])
 		}
 	}
 
-	addrp_width = screen_width;
-	addrp_width -= netid_width+1;
-	addrp_width -= state_width+1;
-	addrp_width -= 14;
+	if (!json_output) {
+
+		addrp_width = screen_width;
+		addrp_width -= netid_width + 1;
+		addrp_width -= state_width + 1;
+		addrp_width -= 14;
+
+		if (addrp_width & 1) {
+			if (netid_width)
+				netid_width++;
+			else if (state_width)
+				state_width++;
+		}
+
+		addrp_width /= 2;
+		addrp_width--;
+
+		serv_width = resolve_services ? 7 : 5;
+
+		if (addrp_width < 15 + serv_width + 1)
+			addrp_width = 15 + serv_width + 1;
 
-	if (addrp_width&1) {
+		addr_width = addrp_width - serv_width - 1;
 		if (netid_width)
-			netid_width++;
-		else if (state_width)
-			state_width++;
-	}
+			printf("%-*s ", netid_width, "Netid");
+		if (state_width)
+			printf("%-*s ", state_width, "State");
+		printf("%-6s %-6s ", "Recv-Q", "Send-Q");
+
+		/* Make enough space for the local/remote port field */
+		addr_width -= 13;
+		serv_width += 13;
 
-	addrp_width /= 2;
-	addrp_width--;
+		printf("%*s:%-*s %*s:%-*s\n",
+				addr_width, "Local Address", serv_width, "Port",
+				addr_width, "Peer Address", serv_width, "Port");
+	}
 
-	serv_width = resolve_services ? 7 : 5;
+	fflush(stdout);
 
-	if (addrp_width < 15+serv_width+1)
-		addrp_width = 15+serv_width+1;
+	if (current_filter.dbs & (1<<NETLINK_DB)) {
+		if (json_output) {
+			printf("\"NETLINK\": [\n");
+			netlink_show(&current_filter);
+			json_first_elem = 1;
+			res_json_fmt_branch(current_filter.dbs & PACKET_DBM ||
+					current_filter.dbs & UNIX_DBM ||
+					current_filter.dbs & (1<<RAW_DB) ||
+					current_filter.dbs & (1<<UDP_DB) ||
+					current_filter.dbs & (1<<TCP_DB) ||
+					current_filter.dbs & (1<<DCCP_DB), ']');
+		} else
+			netlink_show(&current_filter);
+	}
+	if (current_filter.dbs & PACKET_DBM) {
+		if (json_output) {
+			printf("\"PACKET\": [\n");
+			packet_show(&current_filter);
+			json_first_elem = 1;
+			res_json_fmt_branch(
+					current_filter.dbs & UNIX_DBM ||
+					current_filter.dbs & (1<<RAW_DB) ||
+					current_filter.dbs & (1<<UDP_DB) ||
+					current_filter.dbs & (1<<TCP_DB) ||
+					current_filter.dbs & (1<<DCCP_DB), ']');
+		} else
+			packet_show(&current_filter);
+	}
+	if (current_filter.dbs & UNIX_DBM) {
+		if (json_output) {
+			printf("\"UNIX\": [\n");
+			unix_show(&current_filter);
+			json_first_elem = 1;
+			res_json_fmt_branch(
+					current_filter.dbs & (1<<RAW_DB) ||
+					current_filter.dbs & (1<<UDP_DB) ||
+					current_filter.dbs & (1<<TCP_DB) ||
+					current_filter.dbs & (1<<DCCP_DB), ']');
 
-	addr_width = addrp_width - serv_width - 1;
+		} else
+			unix_show(&current_filter);
+	}
+	if (current_filter.dbs & (1<<RAW_DB)) {
+		if (json_output) {
+			printf("\"RAW\": [\n");
+			raw_show(&current_filter);
+			json_first_elem = 1;
+			res_json_fmt_branch(
+					current_filter.dbs & (1<<UDP_DB) ||
+					current_filter.dbs & (1<<TCP_DB) ||
+					current_filter.dbs & (1<<DCCP_DB), ']');
 
-	if (netid_width)
-		printf("%-*s ", netid_width, "Netid");
-	if (state_width)
-		printf("%-*s ", state_width, "State");
-	printf("%-6s %-6s ", "Recv-Q", "Send-Q");
+		} else
+			raw_show(&current_filter);
+	}
+	if (current_filter.dbs & (1<<UDP_DB)) {
+		if (json_output) {
+			printf("\"UDP\": [\n");
+			udp_show(&current_filter);
+			json_first_elem = 1;
+			res_json_fmt_branch(
+					current_filter.dbs & (1<<TCP_DB) ||
+					current_filter.dbs & (1<<DCCP_DB), ']');
 
-	/* Make enough space for the local/remote port field */
-	addr_width -= 13;
-	serv_width += 13;
+		} else
+			udp_show(&current_filter);
+	}
+	if (current_filter.dbs & (1<<TCP_DB)) {
+		if (json_output) {
+			printf("\"TCP\": [\n");
+			tcp_show(&current_filter, IPPROTO_TCP);
+			json_first_elem = 1;
+			res_json_fmt_branch(
+					current_filter.dbs & (1<<DCCP_DB), ']');
+		} else
+			tcp_show(&current_filter, IPPROTO_TCP);
+	}
+	if (current_filter.dbs & (1<<DCCP_DB)) {
+		if (json_output) {
+			printf("\"DCCP\": [\n");
+			tcp_show(&current_filter, IPPROTO_DCCP);
+			printf("]\n");
+		} else
+			tcp_show(&current_filter, IPPROTO_DCCP);
+	}
 
-	printf("%*s:%-*s %*s:%-*s\n",
-	       addr_width, "Local Address", serv_width, "Port",
-	       addr_width, "Peer Address", serv_width, "Port");
+	if (json_output) {
+		printf("}\n");
+	}
 
 	fflush(stdout);
 
 	if (follow_events)
 		exit(handle_follow_request(&current_filter));
 
-	if (current_filter.dbs & (1<<NETLINK_DB))
-		netlink_show(&current_filter);
-	if (current_filter.dbs & PACKET_DBM)
-		packet_show(&current_filter);
-	if (current_filter.dbs & UNIX_DBM)
-		unix_show(&current_filter);
-	if (current_filter.dbs & (1<<RAW_DB))
-		raw_show(&current_filter);
-	if (current_filter.dbs & (1<<UDP_DB))
-		udp_show(&current_filter);
-	if (current_filter.dbs & (1<<TCP_DB))
-		tcp_show(&current_filter, IPPROTO_TCP);
-	if (current_filter.dbs & (1<<DCCP_DB))
-		tcp_show(&current_filter, IPPROTO_DCCP);
-
 	if (show_users || show_proc_ctx || show_sock_ctx)
 		user_ent_destroy();
 
