diff --git a/infiniband-diags/man/iblinkinfo.8 b/infiniband-diags/man/iblinkinfo.8
index f184edf..b91afbd 100644
--- a/infiniband-diags/man/iblinkinfo.8
+++ b/infiniband-diags/man/iblinkinfo.8
@@ -50,6 +50,14 @@ fabrics or a previous state of a fabric.  Cannot be used if user
 specifies a directo route path.  See
 .B ibnetdiscover
 for information on caching ibnetdiscover output.
+.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 iblinkinfo
+output will be displayed showing differences between the old and current
+fabric links.  See
+.B ibnetdiscover
+for information on caching ibnetdiscover output.
 
 .SH AUTHOR
 .TP
diff --git a/infiniband-diags/src/iblinkinfo.c b/infiniband-diags/src/iblinkinfo.c
index 029573f..c6092f9 100644
--- a/infiniband-diags/src/iblinkinfo.c
+++ b/infiniband-diags/src/iblinkinfo.c
@@ -53,9 +53,16 @@
 
 #include "ibdiag_common.h"
 
+#define DIFF_FLAG_PORT_CONNECTION  0x01
+#define DIFF_FLAG_PORT_STATE       0x02
+
+#define DIFF_FLAG_DEFAULT (DIFF_FLAG_PORT_CONNECTION | DIFF_FLAG_PORT_STATE)
+
 static char *node_name_map_file = NULL;
 static nn_map_t *node_name_map = NULL;
 static char *load_cache_file = NULL;
+static char *diff_cache_file = NULL;
+static unsigned diffcheck_flags = DIFF_FLAG_DEFAULT;
 
 static uint64_t guid = 0;
 static char *guid_str = NULL;
@@ -107,7 +114,7 @@ void get_msg(char *width_msg, char *speed_msg, int msg_size, ibnd_port_t * port)
 				      buf, 64, &max_speed));
 }
 
-void print_port(ibnd_node_t * node, ibnd_port_t * port)
+void print_port(ibnd_node_t * node, ibnd_port_t * port, char *out_prefix)
 {
 	char width[64], speed[64], state[64], physstate[64];
 	char remote_guid_str[256];
@@ -204,21 +211,35 @@ void print_port(ibnd_node_t * node, ibnd_port_t * port)
 	if (line_mode) {
 		char *remap = remap_node_name(node_name_map, node->guid,
 					      node->nodedesc);
-		printf("0x%016" PRIx64 " \"%30s\" ", node->guid, remap);
+		printf("%s0x%016" PRIx64 " \"%30s\" ",
+		       out_prefix ? out_prefix : "",
+		       node->guid, remap);
 		free(remap);
 	} else
-		printf("      ");
+		printf("%s      ", out_prefix ? out_prefix : "");
 
 	printf("%6d %4d[%2s] ==%s==>  %s",
 	       node->smalid, port->portnum, ext_port_str, link_str, remote_str);
 }
 
+void print_switch_header(ibnd_node_t *node, int *out_header_flag, char *out_prefix)
+{
+	if (!(*out_header_flag) && !line_mode) {
+		char *remap =
+			remap_node_name(node_name_map, node->guid, node->nodedesc);
+		printf("%sSwitch 0x%016" PRIx64 " %s:\n",
+		       out_prefix ? out_prefix : "",
+		       node->guid, remap);
+		(*out_header_flag)++;
+		free(remap);
+	}
+}
+
 void print_switch(ibnd_node_t * node, void *user_data)
 {
 	int i = 0;
 	int head_print = 0;
-	char *remap =
-	    remap_node_name(node_name_map, node->guid, node->nodedesc);
+	char *out_prefix = (char *)user_data;
 
 	for (i = 1; i <= node->numports; i++) {
 		ibnd_port_t *port = node->ports[i];
@@ -227,15 +248,153 @@ void print_switch(ibnd_node_t * node, void *user_data)
 		if (!down_links_only ||
 		    mad_get_field(port->info, 0,
 				  IB_PORT_STATE_F) == IB_LINK_DOWN) {
-			if (!head_print && !line_mode) {
-				printf("Switch 0x%016" PRIx64 " %s:\n",
-				       node->guid, remap);
-				head_print = 1;
-			}
-			print_port(node, port);
+			print_switch_header(node, &head_print, out_prefix);
+			print_port(node, port, out_prefix);
+		}
+	}
+}
+
+struct iter_diff_data {
+        uint32_t diff_flags;
+        ibnd_fabric_t *fabric1;
+        ibnd_fabric_t *fabric2;
+        char *fabric1_prefix;
+        char *fabric2_prefix;
+};
+
+void diff_switch_ports(ibnd_node_t * fabric1_node, ibnd_node_t * fabric2_node,
+		       int *head_print, struct iter_diff_data *data)
+{
+	int i = 0;
+
+	for (i = 1; i <= fabric1_node->numports; i++) {
+		ibnd_port_t *fabric1_port, *fabric2_port;
+		int output_diff = 0;
+
+		fabric1_port = fabric1_node->ports[i];
+		fabric2_port = fabric2_node->ports[i];
+
+		if (!fabric1_port && !fabric2_port)
+			continue;
+
+		if (data->diff_flags & DIFF_FLAG_PORT_CONNECTION) {
+			if ((fabric1_port && !fabric2_port)
+			    || (!fabric1_port && fabric2_port)
+			    || (fabric1_port->remoteport
+				&& !fabric2_port->remoteport)
+			    || (!fabric1_port->remoteport
+				&& fabric2_port->remoteport)
+			    || (fabric1_port->remoteport
+				&& fabric2_port->remoteport
+				&& fabric1_port->remoteport->guid !=
+				fabric2_port->remoteport->guid))
+				output_diff++;
+		}
+
+		/* if either fabric1_port or fabric2_port NULL, should be
+		 * handled by port connection diff code
+		 */
+		if (data->diff_flags & DIFF_FLAG_PORT_STATE
+		    && fabric1_port
+		    && fabric2_port) {
+			int state1, state2;
+
+			state1 = mad_get_field(fabric1_port->info, 0,
+					       IB_PORT_STATE_F);
+			state2 = mad_get_field(fabric2_port->info, 0,
+					       IB_PORT_STATE_F);
+
+			if (state1 != state2)
+				output_diff++;
+		}
+
+		if (output_diff && fabric1_port) {
+			print_switch_header(fabric1_node,
+					    &head_print,
+					    NULL);
+			print_port(fabric1_node,
+				   fabric1_port,
+				   data->fabric1_prefix);
+		}
+
+		if (output_diff && fabric2_port) {
+			print_switch_header(fabric1_node,
+					    &head_print,
+					    NULL);
+			print_port(fabric2_node,
+				   fabric2_port,
+				   data->fabric2_prefix);
 		}
 	}
-	free(remap);
+}
+
+void diff_switch_iter(ibnd_node_t * fabric1_node, void *iter_user_data)
+{
+	struct iter_diff_data *data = iter_user_data;
+	ibnd_node_t *fabric2_node;
+	int head_print = 0;
+
+	DEBUG("DEBUG: fabric1_node %p\n", fabric1_node);
+
+	fabric2_node = ibnd_find_node_guid(data->fabric2, fabric1_node->guid);
+	if (!fabric2_node)
+		print_switch(fabric1_node, data->fabric1_prefix);
+	else if (data->diff_flags &
+		 (DIFF_FLAG_PORT_CONNECTION | DIFF_FLAG_PORT_STATE)) {
+
+		if (fabric1_node->numports != fabric2_node->numports) {
+			print_switch_header(fabric1_node,
+					    &head_print,
+					    NULL);
+			printf("%snumports = %d\n", data->fabric1_prefix,
+			       fabric1_node->numports);
+			printf("%snumports = %d\n", data->fabric2_prefix,
+			       fabric2_node->numports);
+			return;
+		}
+
+		diff_switch_ports(fabric1_node, fabric2_node,
+				  &head_print, data);
+	}
+}
+
+int diff_switch(ibnd_node_t * node, ibnd_fabric_t * orig_fabric,
+		ibnd_fabric_t * new_fabric)
+{
+	struct iter_diff_data iter_diff_data;
+
+	iter_diff_data.diff_flags = diffcheck_flags;
+	iter_diff_data.fabric1 = orig_fabric;
+	iter_diff_data.fabric2 = new_fabric;
+	iter_diff_data.fabric1_prefix = "< ";
+	iter_diff_data.fabric2_prefix = "> ";
+	if (node)
+		diff_switch_iter(node, &iter_diff_data);
+	else
+		ibnd_iter_nodes_type(orig_fabric, diff_switch_iter,
+				     IB_NODE_SWITCH, &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
+	 * or port state since it has already been done (i.e.
+	 * checks are only done when guid exists on both
+	 * orig and new).
+	 */
+	iter_diff_data.diff_flags = diffcheck_flags & ~DIFF_FLAG_PORT_CONNECTION;
+	iter_diff_data.diff_flags &= ~DIFF_FLAG_PORT_STATE;
+	iter_diff_data.fabric1 = new_fabric;
+	iter_diff_data.fabric2 = orig_fabric;
+	iter_diff_data.fabric1_prefix = "> ";
+	iter_diff_data.fabric2_prefix = "< ";
+	if (node)
+		diff_switch_iter(node, &iter_diff_data);
+	else
+		ibnd_iter_nodes_type(new_fabric, diff_switch_iter,
+				     IB_NODE_SWITCH, &iter_diff_data);
+	
+	return 0;
 }
 
 static int process_opt(void *context, int ch, char *optarg)
@@ -248,6 +407,9 @@ static int process_opt(void *context, int ch, char *optarg)
 	case 2:
 		load_cache_file = strdup(optarg);
 		break;
+	case 3:
+		diff_cache_file = strdup(optarg);
+		break;
 	case 'S':
 		guid_str = optarg;
 		guid = (uint64_t) strtoull(guid_str, 0, 0);
@@ -291,6 +453,7 @@ int main(int argc, char **argv)
 	int rc = 0;
 	int resolved = -1;
 	ibnd_fabric_t *fabric = NULL;
+	ibnd_fabric_t *diff_fabric = NULL;
 	struct ibmad_port *ibmad_port;
 	ib_portid_t port_id = { 0 };
 	int mgmt_classes[3] =
@@ -315,6 +478,8 @@ int main(int argc, char **argv)
 		 "print port guids instead of node guids"},
 		{"load-cache", 2, 1, "<file>",
 		 "filename of ibnetdiscover cache to load"},
+		{"diff", 3, 1, "<file>",
+		 "filename of ibnetdiscover cache to diff"},
 		{"outstanding_smps", 'o', 1, NULL,
 		 "specify the number of outstanding SMP's which should be "
 		 "issued during the scan"},
@@ -363,6 +528,10 @@ int main(int argc, char **argv)
 			       guid_str);
 	}
 
+	if (diff_cache_file &&
+	    !(diff_fabric = ibnd_load_fabric(diff_cache_file, 0)))
+		IBERROR("loading cached fabric for diff failed\n");
+
 	if (load_cache_file) {
 		if ((fabric = ibnd_load_fabric(load_cache_file, 0)) == NULL) {
 			fprintf(stderr, "loading cached fabric failed\n");
@@ -385,8 +554,12 @@ int main(int argc, char **argv)
 
 	if (!all && guid_str) {
 		ibnd_node_t *sw = ibnd_find_node_guid(fabric, guid);
-		if (sw)
-			print_switch(sw, NULL);
+		if (sw) {
+			if (diff_fabric)
+				diff_switch(sw, diff_fabric, fabric);
+			else
+				print_switch(sw, NULL);
+		}
 		else
 			fprintf(stderr, "Failed to find switch: %s\n",
 				guid_str);
@@ -400,15 +573,25 @@ int main(int argc, char **argv)
 		mad_decode_field(ni, IB_NODE_GUID_F, &(guid));
 
 		sw = ibnd_find_node_guid(fabric, guid);
-		if (sw)
-			print_switch(sw, NULL);
+		if (sw) {
+			if (diff_fabric)
+				diff_switch(sw, diff_fabric, fabric);
+			else
+				print_switch(sw, NULL);
+		}
 		else
 			fprintf(stderr, "Failed to find switch: %s\n", dr_path);
-	} else
-		ibnd_iter_nodes_type(fabric, print_switch, IB_NODE_SWITCH,
-				     NULL);
+	} else {
+		if (diff_fabric)
+			diff_switch(NULL, diff_fabric, fabric);
+		else
+			ibnd_iter_nodes_type(fabric, print_switch,
+					     IB_NODE_SWITCH, NULL);
+	}
 
 	ibnd_destroy_fabric(fabric);
+	if (diff_fabric)
+		ibnd_destroy_fabric(diff_fabric);
 
 close_port:
 	close_node_name_map(node_name_map);
