diff --git a/misc/ss.c b/misc/ss.c
index f563710..a760cf9 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -105,6 +105,7 @@ int show_sock_ctx = 0;
 int user_ent_hash_build_init = 0;
 int follow_events = 0;
 int json_output = 0;
+int json_first_elem = 1;
 
 int netid_width;
 int state_width;
@@ -113,6 +114,8 @@ int addr_width;
 int serv_width;
 int screen_width;
 
+enum out_fmt_type fmt_type = FMT_HR;
+
 static const char *TCP_PROTO = "tcp";
 static const char *UDP_PROTO = "udp";
 static const char *RAW_PROTO = "raw";
@@ -346,6 +349,16 @@ static FILE *ephemeral_ports_open(void)
 #define USER_ENT_HASH_SIZE	256
 struct user_ent *user_ent_hash[USER_ENT_HASH_SIZE];
 
+static void json_print_opening(void)
+{
+	if (json_output && json_first_elem) {
+		json_first_elem = 0;
+		printf("{\n");
+	} else if (json_output) {
+		printf(",\n{\n");
+	}
+}
+
 static int user_ent_hashfn(unsigned int ino)
 {
 	int val = (ino >> 24) ^ (ino >> 16) ^ (ino >> 8) ^ ino;
@@ -791,7 +804,8 @@ do_numeric:
 	return buf;
 }
 
-static void inet_addr_print(const inet_prefix *a, int port, unsigned int ifindex)
+static void inet_addr_print(const inet_prefix * a, int port,
+			    unsigned int ifindex, char *peer_kind)
 {
 	char buf[1024];
 	const char *ap = buf;
@@ -819,8 +833,8 @@ static void inet_addr_print(const inet_prefix *a, int port, unsigned int ifindex
 		est_len -= strlen(ifname) + 1;  /* +1 for percent char */
 	}
 
-	sock_addr_print_width(est_len, ap, ":", serv_width, resolve_service(port),
-			ifname);
+	sock_addr_fmt(ap, est_len, ":", serv_width, resolve_service(port),
+			ifname, peer_kind);
 }
 
 static int inet2_addr_match(const inet_prefix *a, const inet_prefix *p,
@@ -1352,21 +1366,28 @@ static void inet_stats_print(struct sockstat *s, int protocol)
 {
 	char *buf = NULL;
 
-	sock_state_print(s, proto_name(protocol));
+	sock_state_fmt(s, sstate_name, proto_name(protocol),
+		       	netid_width, state_width);
 
-	inet_addr_print(&s->local, s->lport, s->iface);
-	inet_addr_print(&s->remote, s->rport, 0);
+	if (json_output) {
+		printf("\t,\"peers\": {\n");
+	}
+	inet_addr_print(&s->local, s->lport, s->iface, "local");
+	inet_addr_print(&s->remote, s->rport, 0, "remote");
+	if (json_output) {
+		printf("}");
+	}
 
 	if (show_proc_ctx || show_sock_ctx) {
 		if (find_entry(s->ino, &buf,
-				(show_proc_ctx & show_sock_ctx) ?
-				PROC_SOCK_CTX : PROC_CTX) > 0) {
-			printf(" users:(%s)", buf);
+			       (show_proc_ctx & show_sock_ctx) ?
+			       PROC_SOCK_CTX : PROC_CTX) > 0) {
+			sock_users_fmt(buf);
 			free(buf);
 		}
 	} else if (show_users) {
 		if (find_entry(s->ino, &buf, USERS) > 0) {
-			printf(" users:(%s)", buf);
+			sock_users_fmt(buf);
 			free(buf);
 		}
 	}
@@ -1470,16 +1491,16 @@ static int tcp_show_line(char *line, const struct filter *f, int family)
 	inet_stats_print(&s.ss, IPPROTO_TCP);
 
 	if (show_options)
-		tcp_timer_print(&s);
+		tcp_timer_fmt(&s);
 
 	if (show_details) {
-		sock_details_print(&s.ss);
+		sock_details_fmt(&s.ss, GENERIC_DETAIL, 0, 0);
 		if (opt[0])
-			printf(" opt:\"%s\"", opt);
+			opt_fmt(opt);
 	}
 
 	if (show_tcpinfo)
-		tcp_stats_print(&s);
+		tcp_stats_fmt(&s);
 
 	printf("\n");
 	return 0;
@@ -1523,31 +1544,14 @@ static void print_skmeminfo(struct rtattr *tb[], int attrtype)
 			const struct inet_diag_meminfo *minfo =
 				RTA_DATA(tb[INET_DIAG_MEMINFO]);
 
-			printf(" mem:(r%u,w%u,f%u,t%u)",
-					minfo->idiag_rmem,
-					minfo->idiag_wmem,
-					minfo->idiag_fmem,
-					minfo->idiag_tmem);
+			mem_fmt(minfo);
 		}
 		return;
 	}
 
 	skmeminfo = RTA_DATA(tb[attrtype]);
 
-	printf(" skmem:(r%u,rb%u,t%u,tb%u,f%u,w%u,o%u",
-	       skmeminfo[SK_MEMINFO_RMEM_ALLOC],
-	       skmeminfo[SK_MEMINFO_RCVBUF],
-	       skmeminfo[SK_MEMINFO_WMEM_ALLOC],
-	       skmeminfo[SK_MEMINFO_SNDBUF],
-	       skmeminfo[SK_MEMINFO_FWD_ALLOC],
-	       skmeminfo[SK_MEMINFO_WMEM_QUEUED],
-	       skmeminfo[SK_MEMINFO_OPTMEM]);
-
-	if (RTA_PAYLOAD(tb[attrtype]) >=
-		(SK_MEMINFO_BACKLOG + 1) * sizeof(__u32))
-		printf(",bl%u", skmeminfo[SK_MEMINFO_BACKLOG]);
-
-	printf(")");
+	skmem_fmt(skmeminfo, tb, attrtype);
 }
 
 #define TCPI_HAS_OPT(info, opt) !!(info->tcpi_options & (opt))
@@ -1660,8 +1664,11 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
 		s.bytes_received = info->tcpi_bytes_received;
 		s.segs_out = info->tcpi_segs_out;
 		s.segs_in = info->tcpi_segs_in;
-		tcp_stats_print(&s);
-		free(s.dctcp);
+		tcp_stats_fmt(&s);
+		if (s.dctcp)
+			free(s.dctcp);
+		if (s.cong_alg)
+			free(s.cong_alg);
 	}
 }
 
@@ -1685,6 +1692,8 @@ static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f, int protocol)
 	s.iface		= r->id.idiag_if;
 	s.sk		= cookie_sk_get(&r->id.idiag_cookie[0]);
 
+	json_print_opening();
+
 	if (s.local.family == AF_INET) {
 		s.local.bytelen = s.remote.bytelen = 4;
 	} else {
@@ -1708,29 +1717,29 @@ static int inet_show_sock(struct nlmsghdr *nlh, struct filter *f, int protocol)
 		t.timer = r->idiag_timer;
 		t.timeout = r->idiag_expires;
 		t.retrans = r->idiag_retrans;
-		tcp_timer_print(&t);
+		tcp_timer_fmt(&t);
 	}
 
 	if (show_details) {
-		sock_details_print(&s);
-		if (s.local.family == AF_INET6 && tb[INET_DIAG_SKV6ONLY]) {
-			unsigned char v6only;
-			v6only = *(__u8 *)RTA_DATA(tb[INET_DIAG_SKV6ONLY]);
-			printf(" v6only:%u", v6only);
-		}
+		sock_details_fmt(&s, GENERIC_DETAIL, 0, 0);
 		if (tb[INET_DIAG_SHUTDOWN]) {
 			unsigned char mask;
 			mask = *(__u8 *)RTA_DATA(tb[INET_DIAG_SHUTDOWN]);
-			printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>');
+			sock_conn_fmt(mask);
 		}
 	}
 
 	if (show_mem || show_tcpinfo) {
-		printf("\n\t");
+		if (!json_output)
+			printf("\n\t");
 		tcp_show_info(nlh, r, tb);
 	}
 
-	printf("\n");
+	if (json_output)
+		printf("}\n");
+	else
+		printf("\n");
+
 	return 0;
 }
 
@@ -2081,7 +2090,10 @@ static int dgram_show_line(char *line, const struct filter *f, int family)
 	inet_stats_print(&s, dg_proto == UDP_PROTO ? IPPROTO_UDP : 0);
 
 	if (show_details && opt[0])
-		printf(" opt:\"%s\"", opt);
+		opt_fmt(opt);
+
+	if (json_output)
+		printf("}");
 
 	printf("\n");
 	return 0;
@@ -2258,27 +2270,36 @@ static void unix_stats_print(struct sockstat *list, struct filter *f)
 				continue;
 		}
 
-		sock_state_print(s, unix_netid_name(s->type));
+		sock_state_fmt(s, sstate_name,
+				unix_netid_name(s->type), netid_width, state_width);
 
-		sock_addr_print(s->name ?: "*", " ",
-				int_to_str(s->lport, port_name), NULL);
-		sock_addr_print(peer, " ", int_to_str(s->rport, port_name),
-				NULL);
+		if (json_output) {
+			printf("\t,\"peers\": {\n");
+		}
+		sock_addr_fmt(s->name ?: "*", addr_width, " ", serv_width,
+				int_to_str(s->lport, port_name),
+				NULL, "local");
+		sock_addr_fmt(peer, addr_width, " ", serv_width,
+				int_to_str(s->rport, port_name),
+				NULL, "remote");
+		if (json_output)
+			printf("}\n");
 
 		if (show_proc_ctx || show_sock_ctx) {
 			if (find_entry(s->ino, &ctx_buf,
-					(show_proc_ctx & show_sock_ctx) ?
-					PROC_SOCK_CTX : PROC_CTX) > 0) {
-				printf(" users:(%s)", ctx_buf);
+				       (show_proc_ctx & show_sock_ctx) ?
+				       PROC_SOCK_CTX : PROC_CTX) > 0) {
+				sock_users_fmt(ctx_buf);
 				free(ctx_buf);
 			}
 		} else if (show_users) {
 			if (find_entry(s->ino, &ctx_buf, USERS) > 0) {
-				printf(" users:(%s)", ctx_buf);
+				sock_users_fmt(ctx_buf);
 				free(ctx_buf);
 			}
 		}
-		printf("\n");
+		if (!json_output)
+			printf("\n");
 	}
 }
 
@@ -2291,7 +2312,9 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh,
 	char name[128];
 	struct sockstat stat = { .name = "*", .peer_name = "*" };
 
-	parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr*)(r+1),
+	json_print_opening();
+
+	parse_rtattr(tb, UNIX_DIAG_MAX, (struct rtattr *)(r + 1),
 		     nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
 	stat.type  = r->udiag_type;
@@ -2324,21 +2347,28 @@ static int unix_show_sock(const struct sockaddr_nl *addr, struct nlmsghdr *nlh,
 		return 0;
 
 	unix_stats_print(&stat, f);
-
-	if (show_mem) {
-		printf("\t");
+if (show_mem) {
+		if (!json_output)
+			printf("\t");
 		print_skmeminfo(tb, UNIX_DIAG_MEMINFO);
 	}
 	if (show_details) {
 		if (tb[UNIX_DIAG_SHUTDOWN]) {
 			unsigned char mask;
 			mask = *(__u8 *)RTA_DATA(tb[UNIX_DIAG_SHUTDOWN]);
-			printf(" %c-%c", mask & 1 ? '-' : '<', mask & 2 ? '-' : '>');
+			sock_conn_fmt(mask);
 		}
 	}
-	if (show_mem || show_details)
-		printf("\n");
+		if (show_mem || show_details)
+		if (!json_output)
+			printf("\n");
 
+	if (json_output) {
+		printf("}\n");
+	}
+
+	if (name)
+		free(name);
 	return 0;
 }
 
@@ -2480,7 +2510,8 @@ static int packet_stats_print(struct sockstat *s, const struct filter *f)
 			return 1;
 	}
 
-	sock_state_print(s, s->type == SOCK_RAW ? "p_raw" : "p_dgr");
+	sock_state_fmt(s, sstate_name, s->type == SOCK_RAW ? "p_raw" : "p_dgr",
+		       	netid_width, state_width);
 
 	if (s->prot == 3)
 		addr = "*";
@@ -2492,25 +2523,35 @@ static int packet_stats_print(struct sockstat *s, const struct filter *f)
 	else
 		port = xll_index_to_name(s->iface);
 
-	sock_addr_print(addr, ":", port, NULL);
-	sock_addr_print("", "*", "", NULL);
+	if (json_output)
+		printf("\t,\"peers\": {\n");
+
+	sock_addr_fmt(addr, addr_width, ":",
+			serv_width, port,
+			NULL, "local");
+	sock_addr_fmt("", addr_width, "*",
+			serv_width, "",
+			NULL, "remote");
+
+	if (json_output)
+		printf("}\n");
 
 	if (show_proc_ctx || show_sock_ctx) {
 		if (find_entry(s->ino, &buf,
-					(show_proc_ctx & show_sock_ctx) ?
-					PROC_SOCK_CTX : PROC_CTX) > 0) {
-			printf(" users:(%s)", buf);
+			       (show_proc_ctx & show_sock_ctx) ?
+			       PROC_SOCK_CTX : PROC_CTX) > 0) {
+			sock_users_fmt(buf);
 			free(buf);
 		}
 	} else if (show_users) {
 		if (find_entry(s->ino, &buf, USERS) > 0) {
-			printf(" users:(%s)", buf);
+			sock_users_fmt(buf);
 			free(buf);
 		}
 	}
 
 	if (show_details)
-		sock_details_print(s);
+		sock_details_fmt(s, GENERIC_DETAIL, 0, 0);
 
 	return 0;
 }
@@ -2527,7 +2568,9 @@ static int packet_show_sock(const struct sockaddr_nl *addr,
 	uint32_t fanout = 0;
 	bool has_fanout = false;
 
-	parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr*)(r+1),
+	json_print_opening();
+
+	parse_rtattr(tb, PACKET_DIAG_MAX, (struct rtattr *)(r + 1),
 		     nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
 	/* use /proc/net/packet if all info are not available */
@@ -2567,60 +2610,9 @@ static int packet_show_sock(const struct sockaddr_nl *addr,
 	if (packet_stats_print(&stat, f))
 		return 0;
 
-	if (show_details) {
-		if (pinfo) {
-			printf("\n\tver:%d", pinfo->pdi_version);
-			printf(" cpy_thresh:%d", pinfo->pdi_copy_thresh);
-			printf(" flags( ");
-			if (pinfo->pdi_flags & PDI_RUNNING)
-				printf("running");
-			if (pinfo->pdi_flags & PDI_AUXDATA)
-				printf(" auxdata");
-			if (pinfo->pdi_flags & PDI_ORIGDEV)
-				printf(" origdev");
-			if (pinfo->pdi_flags & PDI_VNETHDR)
-				printf(" vnethdr");
-			if (pinfo->pdi_flags & PDI_LOSS)
-				printf(" loss");
-			if (!pinfo->pdi_flags)
-				printf("0");
-			printf(" )");
-		}
-		if (ring_rx) {
-			printf("\n\tring_rx(");
-			packet_show_ring(ring_rx);
-			printf(")");
-		}
-		if (ring_tx) {
-			printf("\n\tring_tx(");
-			packet_show_ring(ring_tx);
-			printf(")");
-		}
-		if (has_fanout) {
-			uint16_t type = (fanout >> 16) & 0xffff;
-
-			printf("\n\tfanout(");
-			printf("id:%d,", fanout & 0xffff);
-			printf("type:");
-
-			if (type == 0)
-				printf("hash");
-			else if (type == 1)
-				printf("lb");
-			else if (type == 2)
-				printf("cpu");
-			else if (type == 3)
-				printf("roll");
-			else if (type == 4)
-				printf("random");
-			else if (type == 5)
-				printf("qm");
-			else
-				printf("0x%x", type);
-
-			printf(")");
-		}
-	}
+	if (show_details)
+		packet_details_fmt(pinfo,
+				ring_rx, ring_tx, fanout, has_fanout);
 
 	if (show_bpf && tb[PACKET_DIAG_FILTER]) {
 		struct sock_filter *fil =
@@ -2628,15 +2620,15 @@ static int packet_show_sock(const struct sockaddr_nl *addr,
 		int num = RTA_PAYLOAD(tb[PACKET_DIAG_FILTER]) /
 			  sizeof(struct sock_filter);
 
-		printf("\n\tbpf filter (%d): ", num);
-		while (num) {
-			printf(" 0x%02x %u %u %u,",
-			      fil->code, fil->jt, fil->jf, fil->k);
-			num--;
-			fil++;
-		}
+		bpf_filter_fmt(fil, num);
 	}
-	printf("\n");
+
+	if (json_output)
+		printf("}\n");
+	else
+		printf("\n");
+
+
 	return 0;
 }
 
@@ -2712,6 +2704,7 @@ static int netlink_show_one(struct filter *f,
 	SPRINT_BUF(prot_buf) = {};
 	const char *prot_name;
 	char procname[64] = {};
+	char *rem = "remote";
 
 	st.state = SS_CLOSE;
 	st.rq	 = rq;
@@ -2727,7 +2720,7 @@ static int netlink_show_one(struct filter *f,
 			return 1;
 	}
 
-	sock_state_print(&st, "nl");
+	sock_state_fmt(&st, sstate_name,"nl", netid_width, state_width);
 
 	if (resolve_services)
 		prot_name = nl_proto_n2a(prot, prot_buf, sizeof(prot_buf));
@@ -2759,17 +2752,24 @@ static int netlink_show_one(struct filter *f,
 		int_to_str(pid, procname);
 	}
 
-	sock_addr_print(prot_name, ":", procname, NULL);
+	if (json_output)
+		printf("\t,\"peers\": {\n");
+
+	sock_addr_fmt(prot_name, addr_width, ":", serv_width,
+			procname, NULL, "local");
 
 	if (state == NETLINK_CONNECTED) {
 		char dst_group_buf[30];
 		char dst_pid_buf[30];
-		sock_addr_print(int_to_str(dst_group, dst_group_buf), ":",
-				int_to_str(dst_pid, dst_pid_buf), NULL);
+		sock_addr_fmt(int_to_str(dst_group, dst_group_buf), addr_width, ":",
+				serv_width, int_to_str(dst_pid, dst_pid_buf), NULL, rem);
 	} else {
-		sock_addr_print("", "*", "", NULL);
+		sock_addr_fmt("", addr_width, "*", serv_width, "", NULL, rem);
 	}
 
+	if (json_output)
+		printf("}\n");
+
 	char *pid_context = NULL;
 	if (show_proc_ctx) {
 		/* The pid value will either be:
@@ -2783,16 +2783,11 @@ static int netlink_show_one(struct filter *f,
 		else if (pid > 0)
 			getpidcon(pid, &pid_context);
 
-		if (pid_context != NULL) {
-			printf("proc_ctx=%-*s ", serv_width, pid_context);
-			free(pid_context);
-		} else {
-			printf("proc_ctx=%-*s ", serv_width, "unavailable");
-		}
+		proc_fmt(serv_width, pid_context);
 	}
 
 	if (show_details) {
-		printf(" sk=%llx cb=%llx groups=0x%08x", sk, cb, groups);
+		sock_details_fmt(&st, NETLINK_DETAIL, groups, cb);
 	}
 	printf("\n");
 
@@ -2808,7 +2803,9 @@ static int netlink_show_sock(const struct sockaddr_nl *addr,
 	int rq = 0, wq = 0;
 	unsigned long groups = 0;
 
-	parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr*)(r+1),
+	json_print_opening();
+
+	parse_rtattr(tb, NETLINK_DIAG_MAX, (struct rtattr *)(r + 1),
 		     nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
 
 	if (tb[NETLINK_DIAG_GROUPS] && RTA_PAYLOAD(tb[NETLINK_DIAG_GROUPS]))
@@ -2834,6 +2831,9 @@ static int netlink_show_sock(const struct sockaddr_nl *addr,
 		printf("\n");
 	}
 
+	if (json_output)
+		printf("}\n");
+
 	return 0;
 }
 
@@ -3028,7 +3028,7 @@ static int get_sockstat(struct ssummary *s)
 	return 0;
 }
 
-static int print_summary(void)
+static int print_summary(bool has_successor)
 {
 	struct ssummary s;
 	struct snmpstat sn;
@@ -3040,32 +3040,7 @@ static int print_summary(void)
 
 	get_slabstat(&slabstat);
 
-	printf("Total: %d (kernel %d)\n", s.socks, slabstat.socks);
-
-	printf("TCP:   %d (estab %d, closed %d, orphaned %d, synrecv %d, timewait %d/%d), ports %d\n",
-	       s.tcp_total + slabstat.tcp_syns + s.tcp_tws,
-	       sn.tcp_estab,
-	       s.tcp_total - (s.tcp4_hashed+s.tcp6_hashed-s.tcp_tws),
-	       s.tcp_orphans,
-	       slabstat.tcp_syns,
-	       s.tcp_tws, slabstat.tcp_tws,
-	       slabstat.tcp_ports
-	       );
-
-	printf("\n");
-	printf("Transport Total     IP        IPv6\n");
-	printf("*	  %-9d %-9s %-9s\n", slabstat.socks, "-", "-");
-	printf("RAW	  %-9d %-9d %-9d\n", s.raw4+s.raw6, s.raw4, s.raw6);
-	printf("UDP	  %-9d %-9d %-9d\n", s.udp4+s.udp6, s.udp4, s.udp6);
-	printf("TCP	  %-9d %-9d %-9d\n", s.tcp4_hashed+s.tcp6_hashed, s.tcp4_hashed, s.tcp6_hashed);
-	printf("INET	  %-9d %-9d %-9d\n",
-	       s.raw4+s.udp4+s.tcp4_hashed+
-	       s.raw6+s.udp6+s.tcp6_hashed,
-	       s.raw4+s.udp4+s.tcp4_hashed,
-	       s.raw6+s.udp6+s.tcp6_hashed);
-	printf("FRAG	  %-9d %-9d %-9d\n", s.frag4+s.frag6, s.frag4, s.frag6);
-
-	printf("\n");
+	sock_summary_fmt(&s, &sn, &slabstat, has_successor);
 
 	if (json_output && has_successor)
 		printf(",\n");
@@ -3406,11 +3381,10 @@ int main(int argc, char *argv[])
 	argc -= optind;
 	argv += optind;
 
-	if (do_summary) {
-		print_summary();
-		if (do_default && argc == 0)
-			exit(0);
-	}
+	if (json_output)
+		printf("{\n");
+
+
 
 	/* Now parse filter... */
 	if (argc == 0 && filter_fp) {
