diff --git a/infiniband-diags/man/ibnetdiscover.8 b/infiniband-diags/man/ibnetdiscover.8
index 082a8e4..975b999 100644
--- a/infiniband-diags/man/ibnetdiscover.8
+++ b/infiniband-diags/man/ibnetdiscover.8
@@ -57,6 +57,13 @@ Load and use the cached ibnetdiscover data stored in the specified
 filename.  May be useful for outputting and learning about other
 fabrics or a previous state of a fabric.
 .TP
+\fB\-\-diff\fR <filename>
+Load cached ibnetdiscover data and do a diff comparison to the current
+network or another cache.  A special diff output for ibnetdiscover
+output will be displayed showing differences between the old and current
+fabric.  By default, the following are compared for differences: switches,
+channel adapters, routers, and port connections.
+.TP
 \fB\-p\fR, \fB\-\-ports\fR
 Obtain a ports report which is a
 list of connected ports with relevant information (like LID, portnum,
diff --git a/infiniband-diags/src/ibnetdiscover.c b/infiniband-diags/src/ibnetdiscover.c
index 651bafd..4da09ce 100644
--- a/infiniband-diags/src/ibnetdiscover.c
+++ b/infiniband-diags/src/ibnetdiscover.c
@@ -57,6 +57,16 @@
 #define LIST_SWITCH_NODE (1 << IB_NODE_SWITCH)
 #define LIST_ROUTER_NODE (1 << IB_NODE_ROUTER)
 
+#define DIFF_FLAG_SWITCH          0x00000001
+#define DIFF_FLAG_CA              0x00000002
+#define DIFF_FLAG_ROUTER          0x00000004
+#define DIFF_FLAG_PORT_CONNECTION 0x00000008
+
+#define DIFF_FLAG_DEFAULT      (DIFF_FLAG_SWITCH \
+				| DIFF_FLAG_CA \
+				| DIFF_FLAG_ROUTER \
+				| DIFF_FLAG_PORT_CONNECTION)
+
 struct ibmad_port *srcport;
 
 static FILE *f;
@@ -65,6 +75,7 @@ static char *node_name_map_file = NULL;
 static nn_map_t *node_name_map = NULL;
 static char *cache_file = NULL;
 static char *load_cache_file = NULL;
+static char *diff_cache_file = NULL;
 
 static int report_max_hops = 0;
 
@@ -183,16 +194,20 @@ void list_nodes(ibnd_fabric_t * fabric, int list)
 		ibnd_iter_nodes_type(fabric, list_node, IB_NODE_ROUTER, NULL);
 }
 
-void out_ids(ibnd_node_t * node, int group, char *chname)
+void out_ids(ibnd_node_t * node, int group, char *chname, char *out_prefix)
 {
 	uint64_t sysimgguid =
 	    mad_get_field64(node->info, 0, IB_NODE_SYSTEM_GUID_F);
 
-	fprintf(f, "\nvendid=0x%x\ndevid=0x%x\n",
-		mad_get_field(node->info, 0, IB_NODE_VENDORID_F),
+	fprintf(f, "\n%svendid=0x%x\n",
+		out_prefix ? out_prefix : "",
+		mad_get_field(node->info, 0, IB_NODE_VENDORID_F));
+	fprintf(f, "%sdevid=0x%x\n",
+		out_prefix ? out_prefix : "",
 		mad_get_field(node->info, 0, IB_NODE_DEVID_F));
 	if (sysimgguid)
-		fprintf(f, "sysimgguid=0x%" PRIx64, sysimgguid);
+		fprintf(f, "%ssysimgguid=0x%" PRIx64,
+			out_prefix ? out_prefix : "", sysimgguid);
 	if (group && node->chassis && node->chassis->chassisnum) {
 		fprintf(f, "\t\t# Chassis %d", node->chassis->chassisnum);
 		if (chname)
@@ -217,14 +232,15 @@ uint64_t out_chassis(ibnd_fabric_t * fabric, unsigned char chassisnum)
 	return guid;
 }
 
-void out_switch(ibnd_node_t * node, int group, char *chname)
+void out_switch(ibnd_node_t * node, int group, char *chname, char *out_prefix)
 {
 	char *str;
 	char str2[256];
 	char *nodename = NULL;
 
-	out_ids(node, group, chname);
-	fprintf(f, "switchguid=0x%" PRIx64, node->guid);
+	out_ids(node, group, chname, out_prefix);
+	fprintf(f, "%sswitchguid=0x%" PRIx64,
+		out_prefix ? out_prefix : "", node->guid);
 	fprintf(f, "(%" PRIx64 ")",
 		mad_get_field64(node->info, 0, IB_NODE_PORT_GUID_F));
 	if (group) {
@@ -239,7 +255,8 @@ void out_switch(ibnd_node_t * node, int group, char *chname)
 
 	nodename = remap_node_name(node_name_map, node->guid, node->nodedesc);
 
-	fprintf(f, "\nSwitch\t%d %s\t\t# \"%s\" %s port 0 lid %d lmc %d\n",
+	fprintf(f, "\n%sSwitch\t%d %s\t\t# \"%s\" %s port 0 lid %d lmc %d\n",
+		out_prefix ? out_prefix : "",
 		node->numports, node_name(node), nodename,
 		node->smaenhsp0 ? "enhanced" : "base",
 		node->smalid, node->smalmc);
@@ -247,12 +264,12 @@ void out_switch(ibnd_node_t * node, int group, char *chname)
 	free(nodename);
 }
 
-void out_ca(ibnd_node_t * node, int group, char *chname)
+void out_ca(ibnd_node_t * node, int group, char *chname, char *out_prefix)
 {
 	char *node_type;
 	char *node_type2;
 
-	out_ids(node, group, chname);
+	out_ids(node, group, chname, out_prefix);
 	switch (node->type) {
 	case IB_NODE_CA:
 		node_type = "ca";
@@ -268,8 +285,10 @@ void out_ca(ibnd_node_t * node, int group, char *chname)
 		break;
 	}
 
-	fprintf(f, "%sguid=0x%" PRIx64 "\n", node_type, node->guid);
-	fprintf(f, "%s\t%d %s\t\t# \"%s\"",
+	fprintf(f, "%s%sguid=0x%" PRIx64 "\n",
+		out_prefix ? out_prefix : "", node_type, node->guid);
+	fprintf(f, "%s%s\t%d %s\t\t# \"%s\"",
+		out_prefix ? out_prefix : "",
 		node_type2, node->numports, node_name(node),
 		clean_nodedesc(node->nodedesc));
 	if (group && ibnd_is_xsigo_hca(node->guid))
@@ -291,7 +310,7 @@ static char *out_ext_port(ibnd_port_t * port, int group)
 	return (NULL);
 }
 
-void out_switch_port(ibnd_port_t * port, int group)
+void out_switch_port(ibnd_port_t * port, int group, char *out_prefix)
 {
 	char *ext_port_str = NULL;
 	char *rem_nodename = NULL;
@@ -302,7 +321,7 @@ void out_switch_port(ibnd_port_t * port, int group)
 
 	DEBUG("port %p:%d remoteport %p\n", port, port->portnum,
 	      port->remoteport);
-	fprintf(f, "[%d]", port->portnum);
+	fprintf(f, "%s[%d]", out_prefix ? out_prefix : "", port->portnum);
 
 	ext_port_str = out_ext_port(port, group);
 	if (ext_port_str)
@@ -334,7 +353,7 @@ void out_switch_port(ibnd_port_t * port, int group)
 	free(rem_nodename);
 }
 
-void out_ca_port(ibnd_port_t * port, int group)
+void out_ca_port(ibnd_port_t * port, int group, char *out_prefix)
 {
 	char *str = NULL;
 	char *rem_nodename = NULL;
@@ -343,7 +362,7 @@ void out_ca_port(ibnd_port_t * port, int group)
 	uint32_t ispeed = mad_get_field(port->info, 0,
 					IB_PORT_LINK_SPEED_ACTIVE_F);
 
-	fprintf(f, "[%d]", port->portnum);
+	fprintf(f, "%s[%d]", out_prefix ? out_prefix : "", port->portnum);
 	if (port->node->type != IB_NODE_SWITCH)
 		fprintf(f, "(%" PRIx64 ") ", port->guid);
 	fprintf(f, "\t%s[%d]",
@@ -386,11 +405,11 @@ static void switch_iter_func(ibnd_node_t * node, void *iter_user_data)
 	    && node->chassis->chassisnum)
 		return;
 
-	out_switch(node, data->group, NULL);
+	out_switch(node, data->group, NULL, NULL);
 	for (p = 1; p <= node->numports; p++) {
 		port = node->ports[p];
 		if (port && port->remoteport)
-			out_switch_port(port, data->group);
+			out_switch_port(port, data->group, NULL);
 	}
 }
 
@@ -404,12 +423,12 @@ static void ca_iter_func(ibnd_node_t * node, void *iter_user_data)
 	/* Now, skip chassis based CAs */
 	if (data->group && node->chassis && node->chassis->chassisnum)
 		return;
-	out_ca(node, data->group, NULL);
+	out_ca(node, data->group, NULL, NULL);
 
 	for (p = 1; p <= node->numports; p++) {
 		port = node->ports[p];
 		if (port && port->remoteport)
-			out_ca_port(port, data->group);
+			out_ca_port(port, data->group, NULL);
 	}
 }
 
@@ -423,11 +442,11 @@ static void router_iter_func(ibnd_node_t * node, void *iter_user_data)
 	/* Now, skip chassis based RTs */
 	if (data->group && node->chassis && node->chassis->chassisnum)
 		return;
-	out_ca(node, data->group, NULL);
+	out_ca(node, data->group, NULL, NULL);
 	for (p = 1; p <= node->numports; p++) {
 		port = node->ports[p];
 		if (port && port->remoteport)
-			out_ca_port(port, data->group);
+			out_ca_port(port, data->group, NULL);
 	}
 }
 
@@ -482,7 +501,7 @@ int dump_topology(int group, ibnd_fabric_t * fabric)
 			for (n = 1; n <= SPINES_MAX_NUM; n++) {
 				if (ch->spinenode[n]) {
 					out_switch(ch->spinenode[n], group,
-						   chname);
+						   chname, NULL);
 					for (p = 1;
 					     p <= ch->spinenode[n]->numports;
 					     p++) {
@@ -490,7 +509,8 @@ int dump_topology(int group, ibnd_fabric_t * fabric)
 						    ch->spinenode[n]->ports[p];
 						if (port && port->remoteport)
 							out_switch_port(port,
-									group);
+									group,
+									NULL);
 					}
 				}
 			}
@@ -498,7 +518,7 @@ int dump_topology(int group, ibnd_fabric_t * fabric)
 			for (n = 1; n <= LINES_MAX_NUM; n++) {
 				if (ch->linenode[n]) {
 					out_switch(ch->linenode[n], group,
-						   chname);
+						   chname, NULL);
 					for (p = 1;
 					     p <= ch->linenode[n]->numports;
 					     p++) {
@@ -506,7 +526,8 @@ int dump_topology(int group, ibnd_fabric_t * fabric)
 						    ch->linenode[n]->ports[p];
 						if (port && port->remoteport)
 							out_switch_port(port,
-									group);
+									group,
+									NULL);
 					}
 				}
 			}
@@ -515,12 +536,13 @@ int dump_topology(int group, ibnd_fabric_t * fabric)
 			for (node = ch->nodes; node;
 			     node = node->next_chassis_node) {
 				if (node->type == IB_NODE_SWITCH) {
-					out_switch(node, group, chname);
+					out_switch(node, group, chname, NULL);
 					for (p = 1; p <= node->numports; p++) {
 						port = node->ports[p];
 						if (port && port->remoteport)
 							out_switch_port(port,
-									group);
+									group,
+									NULL);
 					}
 				}
 
@@ -530,12 +552,13 @@ int dump_topology(int group, ibnd_fabric_t * fabric)
 			for (node = ch->nodes; node;
 			     node = node->next_chassis_node) {
 				if (node->type == IB_NODE_CA) {
-					out_ca(node, group, chname);
+					out_ca(node, group, chname, NULL);
 					for (p = 1; p <= node->numports; p++) {
 						port = node->ports[p];
 						if (port && port->remoteport)
 							out_ca_port(port,
-								    group);
+								    group,
+								    NULL);
 					}
 				}
 			}
@@ -610,6 +633,155 @@ void dump_ports_report(ibnd_node_t * node, void *user_data)
 	}
 }
 
+struct iter_diff_data {
+	uint32_t diff_flags;
+	ibnd_fabric_t * fabric1;
+	ibnd_fabric_t * fabric2;
+	char *fabric1_prefix;
+	char *fabric2_prefix;
+	void (*out_header)(ibnd_node_t *, int, char *, char *);
+	void (*out_port)(ibnd_port_t *, int, char *);
+};
+
+static void diff_iter_out_header(ibnd_node_t * node,
+				 struct iter_diff_data *data,
+				 int *out_header_flag)
+{
+	if (!(*out_header_flag)) {
+		(*data->out_header)(node, 0, NULL, NULL);
+		(*out_header_flag)++;
+	}
+}
+
+static void diff_iter_func(ibnd_node_t * fabric1_node, void *iter_user_data)
+{
+	struct iter_diff_data *data = (struct iter_diff_data *)iter_user_data;
+	ibnd_node_t *fabric2_node;
+	ibnd_port_t *fabric1_port;
+	int p;
+
+	DEBUG("DEBUG: fabric1_node %p\n", fabric1_node);
+
+	fabric2_node = ibnd_find_node_guid (data->fabric2, fabric1_node->guid);
+
+	if (!fabric2_node) {
+		(*data->out_header)(fabric1_node, 0, NULL, data->fabric1_prefix);
+		for (p = 1; p <= fabric1_node->numports; p++) {
+			fabric1_port = fabric1_node->ports[p];
+			if (fabric1_port && fabric1_port->remoteport)
+				(*data->out_port)(fabric1_port, 0, data->fabric1_prefix);
+		}
+	}
+	else if (data->diff_flags & DIFF_FLAG_PORT_CONNECTION) {
+		ibnd_port_t *fabric2_port;
+		int out_header_flag = 0;
+
+		if (fabric1_node->numports != fabric2_node->numports) {
+			diff_iter_out_header(fabric1_node, data, &out_header_flag);
+			fprintf(f, "%snumports = %d\n",
+				data->fabric1_prefix, fabric1_node->numports);
+			fprintf(f, "%snumports = %d\n",
+				data->fabric2_prefix, fabric2_node->numports);
+			return;
+		}
+
+		for (p = 1; p <= fabric1_node->numports; p++) {
+			fabric1_port = fabric1_node->ports[p];
+			fabric2_port = fabric2_node->ports[p];
+			if ((fabric1_port && !fabric2_port)
+			    || ((fabric1_port && fabric2_port)
+				&& (fabric1_port->remoteport && !fabric2_port->remoteport))) {
+				diff_iter_out_header(fabric1_node, data, &out_header_flag);
+				(*data->out_port)(fabric1_port, 0, data->fabric1_prefix);
+			}
+			else if ((!fabric1_port && fabric2_port)
+				 || ((fabric1_port && fabric2_port)
+				     && (!fabric1_port->remoteport && fabric2_port->remoteport))) {
+				diff_iter_out_header(fabric1_node, data, &out_header_flag);
+				(*data->out_port)(fabric2_port, 0, data->fabric2_prefix);
+			}
+			else if ((fabric1_port && fabric2_port)
+				 && ((fabric1_port->guid != fabric2_port->guid)
+				     || ((fabric1_port->remoteport && fabric2_port->remoteport)
+					 && (fabric1_port->remoteport->guid != fabric2_port->remoteport->guid)))) {
+				diff_iter_out_header(fabric1_node, data, &out_header_flag);
+				(*data->out_port)(fabric1_port, 0, data->fabric1_prefix);
+				(*data->out_port)(fabric2_port, 0, data->fabric2_prefix);
+			}
+		}
+	}
+}
+
+static int diff_common(ibnd_fabric_t * orig_fabric,
+		       ibnd_fabric_t * new_fabric,
+		       int node_type,
+		       uint32_t diff_flags,
+		       void (*out_header)(ibnd_node_t *, int, char *, char *),
+		       void (*out_port)(ibnd_port_t *, int, char *))
+{
+	struct iter_diff_data iter_diff_data;
+
+	iter_diff_data.diff_flags = diff_flags;
+	iter_diff_data.fabric1 = orig_fabric;
+	iter_diff_data.fabric2 = new_fabric;
+	iter_diff_data.fabric1_prefix = "< ";
+	iter_diff_data.fabric2_prefix = "> ";
+	iter_diff_data.out_header = out_header;
+	iter_diff_data.out_port = out_port;
+	ibnd_iter_nodes_type(orig_fabric, diff_iter_func,
+			     node_type, &iter_diff_data);
+
+	/* Do opposite diff to find existence of node types
+	 * in new_fabric but not in orig_fabric.
+	 *
+	 * In this diff, we don't need to check port connections,
+	 * since it has already been done before.
+	 */
+	iter_diff_data.diff_flags = diff_flags & ~DIFF_FLAG_PORT_CONNECTION;
+	iter_diff_data.fabric1 = new_fabric;
+	iter_diff_data.fabric2 = orig_fabric;
+	iter_diff_data.fabric1_prefix = "> ";
+	iter_diff_data.fabric2_prefix = "< ";
+	iter_diff_data.out_header = out_header;
+	iter_diff_data.out_port = out_port;
+	ibnd_iter_nodes_type(new_fabric, diff_iter_func,
+			     node_type, &iter_diff_data);
+
+	return 0;
+}
+
+int diff(ibnd_fabric_t * orig_fabric, ibnd_fabric_t * new_fabric)
+{
+	uint32_t diff_flags = DIFF_FLAG_DEFAULT;
+
+	if (diff_flags & DIFF_FLAG_SWITCH)
+		diff_common(orig_fabric,
+			    new_fabric,
+			    IB_NODE_SWITCH,
+			    diff_flags,
+			    out_switch,
+			    out_switch_port);
+
+	if (diff_flags & DIFF_FLAG_CA)
+		diff_common(orig_fabric,
+			    new_fabric,
+			    IB_NODE_CA,
+			    diff_flags,
+			    out_ca,
+			    out_ca_port);
+
+	if (diff_flags & DIFF_FLAG_ROUTER)
+		diff_common(orig_fabric,
+			    new_fabric,
+			    IB_NODE_ROUTER,
+			    diff_flags,
+			    out_ca,
+			    out_ca_port);
+
+
+	return 0;
+}
+
 static int list, group, ports_report;
 
 static int process_opt(void *context, int ch, char *optarg)
@@ -624,6 +796,9 @@ static int process_opt(void *context, int ch, char *optarg)
 	case 3:
 		load_cache_file = strdup(optarg);
 		break;
+	case 4:
+		diff_cache_file = strdup(optarg);
+		break;
 	case 's':
 		ibnd_show_progress(1);
 		break;
@@ -658,6 +833,7 @@ static int process_opt(void *context, int ch, char *optarg)
 int main(int argc, char **argv)
 {
 	ibnd_fabric_t *fabric = NULL;
+	ibnd_fabric_t *diff_fabric = NULL;
 
 	struct ibmad_port *ibmad_port;
 	int mgmt_classes[2] = { IB_SMI_CLASS, IB_SMI_DIRECT_CLASS };
@@ -674,6 +850,8 @@ int main(int argc, char **argv)
 		 "filename to cache ibnetdiscover data to"},
 		{"load-cache", 3, 1, "<file>",
 		 "filename of ibnetdiscover cache to load"},
+		{"diff", 4, 1, "<file>",
+		 "filename of ibnetdiscover cache to diff"},
 		{"ports", 'p', 0, NULL, "obtain a ports report"},
 		{"max_hops", 'm', 0, NULL,
 		 "report max hops discovered by the library"},
@@ -704,6 +882,11 @@ int main(int argc, char **argv)
 
 	node_name_map = open_node_name_map(node_name_map_file);
 
+	if (diff_cache_file) {
+		if ((diff_fabric = ibnd_load_fabric(diff_cache_file, 0)) == NULL)
+			IBERROR("loading cached fabric for diff failed\n");
+	}
+
 	if (load_cache_file) {
 		if ((fabric = ibnd_load_fabric(load_cache_file, 0)) == NULL)
 			IBERROR("loading cached fabric failed\n");
@@ -717,6 +900,8 @@ int main(int argc, char **argv)
 		ibnd_iter_nodes(fabric, dump_ports_report, NULL);
 	else if (list)
 		list_nodes(fabric, list);
+	else if (diff_fabric)
+		diff(diff_fabric, fabric);
 	else
 		dump_topology(group, fabric);
 
@@ -725,6 +910,7 @@ int main(int argc, char **argv)
 			IBERROR("caching ibnetdiscover data failed\n");
 
 	ibnd_destroy_fabric(fabric);
+	ibnd_destroy_fabric(diff_fabric);
 	close_node_name_map(node_name_map);
 	mad_rpc_close_port(ibmad_port);
 	exit(0);
