diff --git a/common/Makefile b/common/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..fbedc829a6cccf227edfff2ee111397a416060fc
--- /dev/null
+++ b/common/Makefile
@@ -0,0 +1,8 @@
+CFLAGS= -Wall -Werror -Wno-error=unused-variable
+
+all: utils.o
+
+clean:
+	-rm -f *.o
+
+.PHONY: all clean
diff --git a/common/include/utils.h b/common/include/utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..b276371e8e40bda5531547e1b3361fce8cd70426
--- /dev/null
+++ b/common/include/utils.h
@@ -0,0 +1,18 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void hex_dump(const void * addr, size_t size);
+
+#define DIE(condition, message, ...) \
+do { \
+	if ((condition)) { \
+		fprintf(stderr, "[(%s:%d)]: " # message "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
+		perror(""); \
+		exit(1); \
+	} \
+} while (0)
+
+#endif /* UTILS_H */
diff --git a/common/link_emulator/.clang-format b/common/link_emulator/.clang-format
new file mode 100644
index 0000000000000000000000000000000000000000..fa959436bcfd0fdc41d69e15496289bc998a07e6
--- /dev/null
+++ b/common/link_emulator/.clang-format
@@ -0,0 +1,560 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# clang-format configuration file. Intended for clang-format >= 4.
+#
+# For more information, see:
+#
+#   Documentation/process/clang-format.rst
+#   https://clang.llvm.org/docs/ClangFormat.html
+#   https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+#
+---
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
+AlignOperands: true
+AlignTrailingComments: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+  AfterClass: false
+  AfterControlStatement: false
+  AfterEnum: false
+  AfterFunction: true
+  AfterNamespace: true
+  AfterObjCDeclaration: false
+  AfterStruct: false
+  AfterUnion: false
+  #AfterExternBlock: false # Unknown to clang-format-5.0
+  BeforeCatch: false
+  BeforeElse: false
+  IndentBraces: false
+  #SplitEmptyFunction: true # Unknown to clang-format-4.0
+  #SplitEmptyRecord: true # Unknown to clang-format-4.0
+  #SplitEmptyNamespace: true # Unknown to clang-format-4.0
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
+BreakBeforeTernaryOperators: false
+BreakConstructorInitializersBeforeComma: false
+#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: false
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+#CompactNamespaces: false # Unknown to clang-format-4.0
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 8
+ContinuationIndentWidth: 8
+Cpp11BracedListStyle: false
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+#FixNamespaceComments: false # Unknown to clang-format-4.0
+
+# Taken from:
+#   git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
+#   | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$,  - '\1'," \
+#   | sort | uniq
+ForEachMacros:
+  - 'apei_estatus_for_each_section'
+  - 'ata_for_each_dev'
+  - 'ata_for_each_link'
+  - '__ata_qc_for_each'
+  - 'ata_qc_for_each'
+  - 'ata_qc_for_each_raw'
+  - 'ata_qc_for_each_with_internal'
+  - 'ax25_for_each'
+  - 'ax25_uid_for_each'
+  - '__bio_for_each_bvec'
+  - 'bio_for_each_bvec'
+  - 'bio_for_each_bvec_all'
+  - 'bio_for_each_integrity_vec'
+  - '__bio_for_each_segment'
+  - 'bio_for_each_segment'
+  - 'bio_for_each_segment_all'
+  - 'bio_list_for_each'
+  - 'bip_for_each_vec'
+  - 'bitmap_for_each_clear_region'
+  - 'bitmap_for_each_set_region'
+  - 'blkg_for_each_descendant_post'
+  - 'blkg_for_each_descendant_pre'
+  - 'blk_queue_for_each_rl'
+  - 'bond_for_each_slave'
+  - 'bond_for_each_slave_rcu'
+  - 'bpf_for_each_spilled_reg'
+  - 'btree_for_each_safe128'
+  - 'btree_for_each_safe32'
+  - 'btree_for_each_safe64'
+  - 'btree_for_each_safel'
+  - 'card_for_each_dev'
+  - 'cgroup_taskset_for_each'
+  - 'cgroup_taskset_for_each_leader'
+  - 'cpufreq_for_each_entry'
+  - 'cpufreq_for_each_entry_idx'
+  - 'cpufreq_for_each_valid_entry'
+  - 'cpufreq_for_each_valid_entry_idx'
+  - 'css_for_each_child'
+  - 'css_for_each_descendant_post'
+  - 'css_for_each_descendant_pre'
+  - 'device_for_each_child_node'
+  - 'displayid_iter_for_each'
+  - 'dma_fence_chain_for_each'
+  - 'do_for_each_ftrace_op'
+  - 'drm_atomic_crtc_for_each_plane'
+  - 'drm_atomic_crtc_state_for_each_plane'
+  - 'drm_atomic_crtc_state_for_each_plane_state'
+  - 'drm_atomic_for_each_plane_damage'
+  - 'drm_client_for_each_connector_iter'
+  - 'drm_client_for_each_modeset'
+  - 'drm_connector_for_each_possible_encoder'
+  - 'drm_for_each_bridge_in_chain'
+  - 'drm_for_each_connector_iter'
+  - 'drm_for_each_crtc'
+  - 'drm_for_each_crtc_reverse'
+  - 'drm_for_each_encoder'
+  - 'drm_for_each_encoder_mask'
+  - 'drm_for_each_fb'
+  - 'drm_for_each_legacy_plane'
+  - 'drm_for_each_plane'
+  - 'drm_for_each_plane_mask'
+  - 'drm_for_each_privobj'
+  - 'drm_mm_for_each_hole'
+  - 'drm_mm_for_each_node'
+  - 'drm_mm_for_each_node_in_range'
+  - 'drm_mm_for_each_node_safe'
+  - 'flow_action_for_each'
+  - 'for_each_acpi_dev_match'
+  - 'for_each_active_dev_scope'
+  - 'for_each_active_drhd_unit'
+  - 'for_each_active_iommu'
+  - 'for_each_aggr_pgid'
+  - 'for_each_available_child_of_node'
+  - 'for_each_bio'
+  - 'for_each_board_func_rsrc'
+  - 'for_each_bvec'
+  - 'for_each_card_auxs'
+  - 'for_each_card_auxs_safe'
+  - 'for_each_card_components'
+  - 'for_each_card_dapms'
+  - 'for_each_card_pre_auxs'
+  - 'for_each_card_prelinks'
+  - 'for_each_card_rtds'
+  - 'for_each_card_rtds_safe'
+  - 'for_each_card_widgets'
+  - 'for_each_card_widgets_safe'
+  - 'for_each_cgroup_storage_type'
+  - 'for_each_child_of_node'
+  - 'for_each_clear_bit'
+  - 'for_each_clear_bit_from'
+  - 'for_each_cmsghdr'
+  - 'for_each_compatible_node'
+  - 'for_each_component_dais'
+  - 'for_each_component_dais_safe'
+  - 'for_each_comp_order'
+  - 'for_each_console'
+  - 'for_each_cpu'
+  - 'for_each_cpu_and'
+  - 'for_each_cpu_not'
+  - 'for_each_cpu_wrap'
+  - 'for_each_dapm_widgets'
+  - 'for_each_dev_addr'
+  - 'for_each_dev_scope'
+  - 'for_each_dma_cap_mask'
+  - 'for_each_dpcm_be'
+  - 'for_each_dpcm_be_rollback'
+  - 'for_each_dpcm_be_safe'
+  - 'for_each_dpcm_fe'
+  - 'for_each_drhd_unit'
+  - 'for_each_dss_dev'
+  - 'for_each_dtpm_table'
+  - 'for_each_efi_memory_desc'
+  - 'for_each_efi_memory_desc_in_map'
+  - 'for_each_element'
+  - 'for_each_element_extid'
+  - 'for_each_element_id'
+  - 'for_each_endpoint_of_node'
+  - 'for_each_evictable_lru'
+  - 'for_each_fib6_node_rt_rcu'
+  - 'for_each_fib6_walker_rt'
+  - 'for_each_free_mem_pfn_range_in_zone'
+  - 'for_each_free_mem_pfn_range_in_zone_from'
+  - 'for_each_free_mem_range'
+  - 'for_each_free_mem_range_reverse'
+  - 'for_each_func_rsrc'
+  - 'for_each_hstate'
+  - 'for_each_if'
+  - 'for_each_iommu'
+  - 'for_each_ip_tunnel_rcu'
+  - 'for_each_irq_nr'
+  - 'for_each_link_codecs'
+  - 'for_each_link_cpus'
+  - 'for_each_link_platforms'
+  - 'for_each_lru'
+  - 'for_each_matching_node'
+  - 'for_each_matching_node_and_match'
+  - 'for_each_member'
+  - 'for_each_memcg_cache_index'
+  - 'for_each_mem_pfn_range'
+  - '__for_each_mem_range'
+  - 'for_each_mem_range'
+  - '__for_each_mem_range_rev'
+  - 'for_each_mem_range_rev'
+  - 'for_each_mem_region'
+  - 'for_each_migratetype_order'
+  - 'for_each_msi_entry'
+  - 'for_each_msi_entry_safe'
+  - 'for_each_net'
+  - 'for_each_net_continue_reverse'
+  - 'for_each_netdev'
+  - 'for_each_netdev_continue'
+  - 'for_each_netdev_continue_rcu'
+  - 'for_each_netdev_continue_reverse'
+  - 'for_each_netdev_feature'
+  - 'for_each_netdev_in_bond_rcu'
+  - 'for_each_netdev_rcu'
+  - 'for_each_netdev_reverse'
+  - 'for_each_netdev_safe'
+  - 'for_each_net_rcu'
+  - 'for_each_new_connector_in_state'
+  - 'for_each_new_crtc_in_state'
+  - 'for_each_new_mst_mgr_in_state'
+  - 'for_each_new_plane_in_state'
+  - 'for_each_new_private_obj_in_state'
+  - 'for_each_node'
+  - 'for_each_node_by_name'
+  - 'for_each_node_by_type'
+  - 'for_each_node_mask'
+  - 'for_each_node_state'
+  - 'for_each_node_with_cpus'
+  - 'for_each_node_with_property'
+  - 'for_each_nonreserved_multicast_dest_pgid'
+  - 'for_each_of_allnodes'
+  - 'for_each_of_allnodes_from'
+  - 'for_each_of_cpu_node'
+  - 'for_each_of_pci_range'
+  - 'for_each_old_connector_in_state'
+  - 'for_each_old_crtc_in_state'
+  - 'for_each_old_mst_mgr_in_state'
+  - 'for_each_oldnew_connector_in_state'
+  - 'for_each_oldnew_crtc_in_state'
+  - 'for_each_oldnew_mst_mgr_in_state'
+  - 'for_each_oldnew_plane_in_state'
+  - 'for_each_oldnew_plane_in_state_reverse'
+  - 'for_each_oldnew_private_obj_in_state'
+  - 'for_each_old_plane_in_state'
+  - 'for_each_old_private_obj_in_state'
+  - 'for_each_online_cpu'
+  - 'for_each_online_node'
+  - 'for_each_online_pgdat'
+  - 'for_each_pci_bridge'
+  - 'for_each_pci_dev'
+  - 'for_each_pci_msi_entry'
+  - 'for_each_pcm_streams'
+  - 'for_each_physmem_range'
+  - 'for_each_populated_zone'
+  - 'for_each_possible_cpu'
+  - 'for_each_present_cpu'
+  - 'for_each_prime_number'
+  - 'for_each_prime_number_from'
+  - 'for_each_process'
+  - 'for_each_process_thread'
+  - 'for_each_prop_codec_conf'
+  - 'for_each_prop_dai_codec'
+  - 'for_each_prop_dai_cpu'
+  - 'for_each_prop_dlc_codecs'
+  - 'for_each_prop_dlc_cpus'
+  - 'for_each_prop_dlc_platforms'
+  - 'for_each_property_of_node'
+  - 'for_each_registered_fb'
+  - 'for_each_requested_gpio'
+  - 'for_each_requested_gpio_in_range'
+  - 'for_each_reserved_mem_range'
+  - 'for_each_reserved_mem_region'
+  - 'for_each_rtd_codec_dais'
+  - 'for_each_rtd_components'
+  - 'for_each_rtd_cpu_dais'
+  - 'for_each_rtd_dais'
+  - 'for_each_set_bit'
+  - 'for_each_set_bit_from'
+  - 'for_each_set_clump8'
+  - 'for_each_sg'
+  - 'for_each_sg_dma_page'
+  - 'for_each_sg_page'
+  - 'for_each_sgtable_dma_page'
+  - 'for_each_sgtable_dma_sg'
+  - 'for_each_sgtable_page'
+  - 'for_each_sgtable_sg'
+  - 'for_each_sibling_event'
+  - 'for_each_subelement'
+  - 'for_each_subelement_extid'
+  - 'for_each_subelement_id'
+  - '__for_each_thread'
+  - 'for_each_thread'
+  - 'for_each_unicast_dest_pgid'
+  - 'for_each_vsi'
+  - 'for_each_wakeup_source'
+  - 'for_each_zone'
+  - 'for_each_zone_zonelist'
+  - 'for_each_zone_zonelist_nodemask'
+  - 'fwnode_for_each_available_child_node'
+  - 'fwnode_for_each_child_node'
+  - 'fwnode_graph_for_each_endpoint'
+  - 'gadget_for_each_ep'
+  - 'genradix_for_each'
+  - 'genradix_for_each_from'
+  - 'hash_for_each'
+  - 'hash_for_each_possible'
+  - 'hash_for_each_possible_rcu'
+  - 'hash_for_each_possible_rcu_notrace'
+  - 'hash_for_each_possible_safe'
+  - 'hash_for_each_rcu'
+  - 'hash_for_each_safe'
+  - 'hctx_for_each_ctx'
+  - 'hlist_bl_for_each_entry'
+  - 'hlist_bl_for_each_entry_rcu'
+  - 'hlist_bl_for_each_entry_safe'
+  - 'hlist_for_each'
+  - 'hlist_for_each_entry'
+  - 'hlist_for_each_entry_continue'
+  - 'hlist_for_each_entry_continue_rcu'
+  - 'hlist_for_each_entry_continue_rcu_bh'
+  - 'hlist_for_each_entry_from'
+  - 'hlist_for_each_entry_from_rcu'
+  - 'hlist_for_each_entry_rcu'
+  - 'hlist_for_each_entry_rcu_bh'
+  - 'hlist_for_each_entry_rcu_notrace'
+  - 'hlist_for_each_entry_safe'
+  - 'hlist_for_each_entry_srcu'
+  - '__hlist_for_each_rcu'
+  - 'hlist_for_each_safe'
+  - 'hlist_nulls_for_each_entry'
+  - 'hlist_nulls_for_each_entry_from'
+  - 'hlist_nulls_for_each_entry_rcu'
+  - 'hlist_nulls_for_each_entry_safe'
+  - 'i3c_bus_for_each_i2cdev'
+  - 'i3c_bus_for_each_i3cdev'
+  - 'ide_host_for_each_port'
+  - 'ide_port_for_each_dev'
+  - 'ide_port_for_each_present_dev'
+  - 'idr_for_each_entry'
+  - 'idr_for_each_entry_continue'
+  - 'idr_for_each_entry_continue_ul'
+  - 'idr_for_each_entry_ul'
+  - 'in_dev_for_each_ifa_rcu'
+  - 'in_dev_for_each_ifa_rtnl'
+  - 'inet_bind_bucket_for_each'
+  - 'inet_lhash2_for_each_icsk_rcu'
+  - 'key_for_each'
+  - 'key_for_each_safe'
+  - 'klp_for_each_func'
+  - 'klp_for_each_func_safe'
+  - 'klp_for_each_func_static'
+  - 'klp_for_each_object'
+  - 'klp_for_each_object_safe'
+  - 'klp_for_each_object_static'
+  - 'kunit_suite_for_each_test_case'
+  - 'kvm_for_each_memslot'
+  - 'kvm_for_each_vcpu'
+  - 'list_for_each'
+  - 'list_for_each_codec'
+  - 'list_for_each_codec_safe'
+  - 'list_for_each_continue'
+  - 'list_for_each_entry'
+  - 'list_for_each_entry_continue'
+  - 'list_for_each_entry_continue_rcu'
+  - 'list_for_each_entry_continue_reverse'
+  - 'list_for_each_entry_from'
+  - 'list_for_each_entry_from_rcu'
+  - 'list_for_each_entry_from_reverse'
+  - 'list_for_each_entry_lockless'
+  - 'list_for_each_entry_rcu'
+  - 'list_for_each_entry_reverse'
+  - 'list_for_each_entry_safe'
+  - 'list_for_each_entry_safe_continue'
+  - 'list_for_each_entry_safe_from'
+  - 'list_for_each_entry_safe_reverse'
+  - 'list_for_each_entry_srcu'
+  - 'list_for_each_prev'
+  - 'list_for_each_prev_safe'
+  - 'list_for_each_safe'
+  - 'llist_for_each'
+  - 'llist_for_each_entry'
+  - 'llist_for_each_entry_safe'
+  - 'llist_for_each_safe'
+  - 'mci_for_each_dimm'
+  - 'media_device_for_each_entity'
+  - 'media_device_for_each_intf'
+  - 'media_device_for_each_link'
+  - 'media_device_for_each_pad'
+  - 'nanddev_io_for_each_page'
+  - 'netdev_for_each_lower_dev'
+  - 'netdev_for_each_lower_private'
+  - 'netdev_for_each_lower_private_rcu'
+  - 'netdev_for_each_mc_addr'
+  - 'netdev_for_each_uc_addr'
+  - 'netdev_for_each_upper_dev_rcu'
+  - 'netdev_hw_addr_list_for_each'
+  - 'nft_rule_for_each_expr'
+  - 'nla_for_each_attr'
+  - 'nla_for_each_nested'
+  - 'nlmsg_for_each_attr'
+  - 'nlmsg_for_each_msg'
+  - 'nr_neigh_for_each'
+  - 'nr_neigh_for_each_safe'
+  - 'nr_node_for_each'
+  - 'nr_node_for_each_safe'
+  - 'of_for_each_phandle'
+  - 'of_property_for_each_string'
+  - 'of_property_for_each_u32'
+  - 'pci_bus_for_each_resource'
+  - 'pcl_for_each_chunk'
+  - 'pcl_for_each_segment'
+  - 'pcm_for_each_format'
+  - 'ping_portaddr_for_each_entry'
+  - 'plist_for_each'
+  - 'plist_for_each_continue'
+  - 'plist_for_each_entry'
+  - 'plist_for_each_entry_continue'
+  - 'plist_for_each_entry_safe'
+  - 'plist_for_each_safe'
+  - 'pnp_for_each_card'
+  - 'pnp_for_each_dev'
+  - 'protocol_for_each_card'
+  - 'protocol_for_each_dev'
+  - 'queue_for_each_hw_ctx'
+  - 'radix_tree_for_each_slot'
+  - 'radix_tree_for_each_tagged'
+  - 'rb_for_each'
+  - 'rbtree_postorder_for_each_entry_safe'
+  - 'rdma_for_each_block'
+  - 'rdma_for_each_port'
+  - 'rdma_umem_for_each_dma_block'
+  - 'resource_list_for_each_entry'
+  - 'resource_list_for_each_entry_safe'
+  - 'rhl_for_each_entry_rcu'
+  - 'rhl_for_each_rcu'
+  - 'rht_for_each'
+  - 'rht_for_each_entry'
+  - 'rht_for_each_entry_from'
+  - 'rht_for_each_entry_rcu'
+  - 'rht_for_each_entry_rcu_from'
+  - 'rht_for_each_entry_safe'
+  - 'rht_for_each_from'
+  - 'rht_for_each_rcu'
+  - 'rht_for_each_rcu_from'
+  - '__rq_for_each_bio'
+  - 'rq_for_each_bvec'
+  - 'rq_for_each_segment'
+  - 'scsi_for_each_prot_sg'
+  - 'scsi_for_each_sg'
+  - 'sctp_for_each_hentry'
+  - 'sctp_skb_for_each'
+  - 'shdma_for_each_chan'
+  - '__shost_for_each_device'
+  - 'shost_for_each_device'
+  - 'sk_for_each'
+  - 'sk_for_each_bound'
+  - 'sk_for_each_entry_offset_rcu'
+  - 'sk_for_each_from'
+  - 'sk_for_each_rcu'
+  - 'sk_for_each_safe'
+  - 'sk_nulls_for_each'
+  - 'sk_nulls_for_each_from'
+  - 'sk_nulls_for_each_rcu'
+  - 'snd_array_for_each'
+  - 'snd_pcm_group_for_each_entry'
+  - 'snd_soc_dapm_widget_for_each_path'
+  - 'snd_soc_dapm_widget_for_each_path_safe'
+  - 'snd_soc_dapm_widget_for_each_sink_path'
+  - 'snd_soc_dapm_widget_for_each_source_path'
+  - 'tb_property_for_each'
+  - 'tcf_exts_for_each_action'
+  - 'udp_portaddr_for_each_entry'
+  - 'udp_portaddr_for_each_entry_rcu'
+  - 'usb_hub_for_each_child'
+  - 'v4l2_device_for_each_subdev'
+  - 'v4l2_m2m_for_each_dst_buf'
+  - 'v4l2_m2m_for_each_dst_buf_safe'
+  - 'v4l2_m2m_for_each_src_buf'
+  - 'v4l2_m2m_for_each_src_buf_safe'
+  - 'virtio_device_for_each_vq'
+  - 'while_for_each_ftrace_op'
+  - 'xa_for_each'
+  - 'xa_for_each_marked'
+  - 'xa_for_each_range'
+  - 'xa_for_each_start'
+  - 'xas_for_each'
+  - 'xas_for_each_conflict'
+  - 'xas_for_each_marked'
+  - 'xbc_array_for_each_value'
+  - 'xbc_for_each_key_value'
+  - 'xbc_node_for_each_array_value'
+  - 'xbc_node_for_each_child'
+  - 'xbc_node_for_each_key_value'
+  - 'zorro_for_each_dev'
+
+#IncludeBlocks: Preserve # Unknown to clang-format-5.0
+IncludeCategories:
+  - Regex: '.*'
+    Priority: 1
+IncludeIsMainRegex: '(Test)?$'
+IndentCaseLabels: false
+#IndentPPDirectives: None # Unknown to clang-format-5.0
+IndentWidth: 8
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
+ObjCBlockIndentWidth: 8
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: true
+
+# Taken from git's rules
+#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
+PenaltyBreakBeforeFirstCallParameter: 30
+PenaltyBreakComment: 10
+PenaltyBreakFirstLessLess: 0
+PenaltyBreakString: 10
+PenaltyExcessCharacter: 100
+PenaltyReturnTypeOnItsOwnLine: 60
+
+PointerAlignment: Right
+ReflowComments: false
+SortIncludes: false
+#SortUsingDeclarations: false # Unknown to clang-format-4.0
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
+#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
+SpaceBeforeParens: ControlStatements
+#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp03
+TabWidth: 8
+UseTab: Always
+...
diff --git a/lab3/link_emulator/Makefile b/common/link_emulator/Makefile
similarity index 71%
rename from lab3/link_emulator/Makefile
rename to common/link_emulator/Makefile
index bc25fa35e005fafff429c72f9eb99c93d90f89be..9503a5ae8f4d15dd9d86512620d88096178702c2 100644
--- a/lab3/link_emulator/Makefile
+++ b/common/link_emulator/Makefile
@@ -3,8 +3,8 @@ all: link lib.o
 link: link.o queue.o
 	gcc -g link.o queue.o -o link -pthread
 
-.c.o: 
-	gcc -Wall -g -c $? -pthread
+.c.o:
+	gcc -Wall -g -c $? -pthread -I../
 
 clean:
 	rm -f *.o link
diff --git a/common/link_emulator/lib.c b/common/link_emulator/lib.c
new file mode 100644
index 0000000000000000000000000000000000000000..49181d2b1f5e1ac8c990d947d1999e1dd2e17f89
--- /dev/null
+++ b/common/link_emulator/lib.c
@@ -0,0 +1,117 @@
+#include "lib.h"
+#include "include/utils.h"
+#include <arpa/inet.h>
+#include <poll.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <string.h>
+
+struct sockaddr_in addr_local, addr_remote;
+int s;
+struct pollfd fds[1];
+
+void set_local_port(int port)
+{
+	memset((char *) &addr_local, 0, sizeof(addr_local));
+	addr_local.sin_family = AF_INET;
+	addr_local.sin_port = htons(port);
+	addr_local.sin_addr.s_addr = htonl(INADDR_ANY);
+}
+
+void set_remote(char* ip, int port)
+{
+	memset((char *) &addr_remote, 0, sizeof(addr_remote));
+	addr_remote.sin_family = AF_INET;
+	addr_remote.sin_port = htons(port);
+	int res = inet_aton(ip, &addr_remote.sin_addr);
+	DIE(res == 0, "inet_aton failed\n");
+}
+
+void init(char* remote, int REMOTE_PORT)
+{
+	s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	DIE(s == -1, "Error creating socket");
+
+	set_local_port(0);
+	set_remote(remote, REMOTE_PORT);
+
+	int res = bind(s, (struct sockaddr*)&addr_local, sizeof(addr_local));
+	DIE(res == -1, "Failed to bind");
+
+	fds[0].fd = s;
+	fds[0].events = POLLIN;
+
+	msg m;
+	send_message(&m);
+}
+
+int send_message(const msg *m)
+{
+	return sendto(s, m, sizeof(msg), 0,(struct sockaddr*) &addr_remote,
+			sizeof(addr_remote));
+}
+
+int link_send(void *buf, int len)
+{
+	msg m;
+
+	memcpy(m.payload, buf, len);
+	m.len = len;
+
+	return send_message(&m);
+}
+
+int recv_message(msg *ret)
+{
+	return recvfrom(s, ret, sizeof(msg), 0, NULL, NULL);
+}
+
+int link_recv(void *buf, int len)
+{
+	msg m;
+	recv_message(&m);
+	int r = m.len < len ? m.len : len;
+	memcpy(buf, m.payload, r);
+	return r;
+}
+
+//timeout in millis
+int recv_message_timeout(msg *m, int timeout)
+{
+	int ret = poll(fds, 1, timeout);
+
+	if (ret > 0) {
+		if (fds[0].revents & POLLIN)
+			return recv_message(m);
+	}
+
+	return -1;
+}
+
+int link_recv_timeout(void *buf, int len, int timeout)
+{
+	msg m;
+	recv_message_timeout(&m, timeout);
+	int r = m.len < len ? m.len : len;
+	memcpy(buf, m.payload, r);
+	return r;
+}
+
+int send_byte(uint8_t byte) {
+	printf("Sending byte %1$c (0x%1$02x)\n", byte);
+	return link_send(&byte, sizeof(byte));
+}
+
+uint8_t recv_byte() {
+	int ret;
+	uint8_t byte;
+	ret = link_recv_timeout(&byte, sizeof(byte), 100000);
+	if (ret < 0)
+		byte = rand() % 128;
+
+	return byte;
+}
diff --git a/common/link_emulator/lib.h b/common/link_emulator/lib.h
new file mode 100644
index 0000000000000000000000000000000000000000..5d4fca6411757a891aab2683f7bebc98e7f20124
--- /dev/null
+++ b/common/link_emulator/lib.h
@@ -0,0 +1,25 @@
+#ifndef _LIB_H_
+#define _LIB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define MAX_LEN 1400
+
+typedef struct {
+  int len;
+  char payload[MAX_LEN];
+} msg;
+
+void init(char* remote, int remote_port);
+void set_local_port(int port);
+void set_remote(char* ip, int port);
+int send_message(const msg *t);
+int recv_message(msg* r);
+int recv_message_timeout(msg *m, int timeout);
+int link_send(void *buf, int len);
+int link_recv(void *buf, int len);
+int send_byte(uint8_t b);
+uint8_t recv_byte(void);
+
+#endif /* _LIB_H_ */
diff --git a/common/link_emulator/link.c b/common/link_emulator/link.c
new file mode 100644
index 0000000000000000000000000000000000000000..a71417341912f00affd33a4d9ab782a045a1dd4c
--- /dev/null
+++ b/common/link_emulator/link.c
@@ -0,0 +1,465 @@
+#include <arpa/inet.h>
+#include <assert.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+//#include <asm/param.h>
+#include "link.h"
+#include "queue.h"
+#include "include/utils.h"
+
+#define DEBUG 0
+
+int BUFFER_SIZE = 1000;
+
+int serialization_delay = 1000;
+int delay = 1000;
+int loss = 0;
+int corrupt = 0;
+int corrupt2 = 0;
+int reorder = 0;
+
+#define CHANNEL_BUSY 1
+#define CHANNEL_IDLE 0
+
+#define LOCAL_PORT1 10000
+#define LOCAL_PORT2 10001
+
+pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t buffer_cond = PTHREAD_COND_INITIALIZER;
+queue *buffer;
+
+struct sockaddr_in local_addr1, remote_addr1;
+struct sockaddr_in local_addr2, remote_addr2;
+
+int s1, s2;
+socklen_t sz;
+
+// 1 if a message was received on this link
+int link_up1 = 0;
+int link_up2 = 0;
+
+void init_sockets()
+{
+	/*LOCAL ADDRESSES*/
+
+	memset((char *)&local_addr1, 0, sizeof(local_addr1));
+	local_addr1.sin_family = AF_INET;
+	local_addr1.sin_port = htons(LOCAL_PORT1);
+	local_addr1.sin_addr.s_addr = htonl(INADDR_ANY);
+
+	memset((char *)&local_addr2, 0, sizeof(local_addr2));
+	local_addr2.sin_family = AF_INET;
+	local_addr2.sin_port = htons(LOCAL_PORT2);
+	local_addr2.sin_addr.s_addr = htonl(INADDR_ANY);
+
+	s1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	DIE(s1 == -1, "Error creating socket");
+
+	// now bind
+	int res = bind(s1, (struct sockaddr *)&local_addr1, sizeof(local_addr1));
+	DIE(res == -1, "Failed to bind s1");
+
+	s2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	DIE(s2 == -1, "Error creating socket 2");
+
+	res = bind(s2, (struct sockaddr *)&local_addr2, sizeof(local_addr2));
+	DIE(res == -1, "Failed to bind s2");
+}
+
+int send_message1(const msg *m)
+{
+	if (!link_up1) {
+		printf("Trying to send a message but remote peer is not connected on my "
+		       "port %d\n",
+		       LOCAL_PORT1);
+	}
+	return sendto(s1, m, sizeof(msg), 0, (struct sockaddr *)&remote_addr1,
+		      sizeof(remote_addr1));
+}
+
+msg *receive_message1()
+{
+	msg *ret;
+
+a:
+	ret = (msg *)malloc(sizeof(msg));
+	DIE(!ret, "malloc");
+
+	if (!link_up1) {
+		sz = sizeof(remote_addr1);
+		if (recvfrom(s1, ret, sizeof(msg), 0,
+			     (struct sockaddr *)&remote_addr1, &sz) == -1) {
+			free(ret);
+			return NULL;
+		}
+
+		link_up1 = 1;
+
+#if DEBUG
+		printf("Link 1 is up, remote addr is %s port %d\n",
+		       inet_ntoa(remote_addr1.sin_addr),
+		       ntohs(remote_addr1.sin_port));
+#endif
+
+		free(ret);
+		goto a;
+	} else if (recvfrom(s1, ret, sizeof(msg), 0, NULL, NULL) == -1) {
+		free(ret);
+		return NULL;
+	}
+	return ret;
+}
+
+int send_message2(const msg *m)
+{
+	if (!link_up2) {
+		printf("Trying to send a message but remote peer is not connected on my "
+		       "port %d\n",
+		       LOCAL_PORT2);
+	}
+	return sendto(s2, m, sizeof(msg), 0, (struct sockaddr *)&remote_addr2,
+		      sizeof(remote_addr2));
+}
+
+msg *receive_message2()
+{
+	msg *ret;
+
+a:
+	ret = (msg *)malloc(sizeof(msg));
+	DIE(!ret, "malloc");
+
+	if (!link_up2) {
+		sz = sizeof(remote_addr2);
+		if (recvfrom(s2, ret, sizeof(msg), 0,
+			     (struct sockaddr *)&remote_addr2, &sz) == -1) {
+			free(ret);
+			return NULL;
+		}
+		link_up2 = 1;
+
+#if DEBUG
+		printf("Link 2 is up, remote addr is %s port %d\n",
+		       inet_ntoa(remote_addr2.sin_addr),
+		       ntohs(remote_addr2.sin_port));
+#endif
+
+		free(ret);
+		goto a;
+	} else if (recvfrom(s2, ret, sizeof(msg), 0, NULL, NULL) == -1) {
+		free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
+
+unsigned long long now()
+{
+	struct timeval b;
+	gettimeofday(&b, NULL);
+	return (unsigned long long)b.tv_sec * 1000000 + b.tv_usec;
+}
+
+void *link_scheduler(void *argument)
+{
+	msg_in_flight *mif;
+	queue *in_flight = create_queue();
+	long long idle_time = 0;
+	long long crt_time, wait_time_idle, wait_time_send;
+	int stuff;
+
+	while (1) {
+		crt_time = now();
+
+#if DEBUG
+		printf("In flight size %d at %lld\n", in_flight->size,
+		       crt_time);
+#endif
+
+		while (in_flight->size > 0) {
+			msg_in_flight *last =
+				(msg_in_flight *)in_flight->last->crt;
+			if (crt_time < last->finish_time) {
+				break;
+			}
+
+			// else send first packet on the wire
+			mif = (msg_in_flight *)dequeue(in_flight);
+			DIE(!mif, "Error in deque: expecting non null msg!");
+			if (send_message2(mif->m) <= 0)
+				perror("SNDMSG2");
+
+#if DEBUG
+			printf("Sending message\n");
+#endif
+
+			free(mif->m);
+			free(mif);
+			mif = NULL;
+		}
+
+		pthread_mutex_lock(&buffer_lock);
+		stuff = buffer->size > 0;
+		pthread_mutex_unlock(&buffer_lock);
+
+#if DEBUG
+		printf("Stuff is %d\n", stuff);
+#endif
+
+		wait_time_send = wait_time_idle = 0;
+
+		// crt_time = now();
+
+		// now see if we can put stuff in flight
+		if (stuff && crt_time >= idle_time) {
+			idle_time = crt_time + serialization_delay;
+
+			mif = (msg_in_flight *)malloc(sizeof(msg_in_flight));
+			assert(mif);
+
+			pthread_mutex_lock(&buffer_lock);
+
+			if (rand() % 100 < reorder && buffer->size > 1) {
+				msg *m = (msg *)dequeue(buffer);
+				enqueue(buffer,m);
+			}
+
+			mif->m = (msg *)dequeue(buffer);
+			pthread_mutex_unlock(&buffer_lock);
+
+			assert(mif->m);
+			mif->finish_time =
+				crt_time + serialization_delay + delay;
+
+			// send message here from buffer to link
+			enqueue(in_flight, mif);
+
+#if DEBUG
+			printf("Enquing message\n");
+#endif
+		}
+		wait_time_idle = idle_time - crt_time;
+
+		if (in_flight->size > 0) {
+			msg_in_flight *last =
+				(msg_in_flight *)in_flight->last->crt;
+			wait_time_send = last->finish_time - crt_time;
+		}
+
+		if (wait_time_idle > 0 || wait_time_send > 0) {
+			long long wait_time = 0;
+			if (wait_time_idle > 0)
+				wait_time = wait_time_idle;
+			if (wait_time_send > 0 && wait_time_send < wait_time)
+				wait_time = wait_time_send;
+
+			// printf("Sleeping %lld\n", wait_time);
+			usleep(wait_time);
+		} else {
+			pthread_mutex_lock(&buffer_lock);
+			assert(!buffer->size);
+#if DEBUG
+			printf("Waiting on cond\n");
+#endif
+			pthread_cond_wait(&buffer_cond, &buffer_lock);
+			pthread_mutex_unlock(&buffer_lock);
+		}
+	}
+
+	return NULL;
+}
+
+void *run_forwarding(void *param)
+{
+	msg *m;
+
+	while (1) {
+		int overflow;
+		m = receive_message1();
+		DIE(m == NULL, "Read error");
+
+		// check queue space
+		pthread_mutex_lock(&buffer_lock);
+		overflow = buffer->size >= BUFFER_SIZE;
+		pthread_mutex_unlock(&buffer_lock);
+
+		if (overflow || (rand() % 100) < loss) {
+			// just drop message
+			free(m);
+			printf("Dropped packet\n");
+		} else {
+			if (rand() % 100 < corrupt) {
+				// flip a random bit in a randomly chosen byte
+				// of the payload
+				int random_byte = rand() % m->len;
+				int random_bit = rand();
+				m->payload[random_byte] ^= 1
+							   << (random_bit % 8);
+
+				if (rand() % 100 < corrupt2) {
+					// flip a second bit in the same byte
+					int random_bit2;
+					do {
+						random_bit2 = rand();
+					} while (random_bit2 == random_bit);
+					m->payload[random_byte] ^=
+						1 << (random_bit2 % 8);
+				}
+				// printf("Enqueue 1.");
+				pthread_mutex_lock(&buffer_lock);
+				enqueue(buffer, m);
+				pthread_cond_signal(&buffer_cond);
+				pthread_mutex_unlock(&buffer_lock);
+				// printf("Done!\n");
+			}
+		}
+	}
+}
+
+void *run_reverse_forwarding(void *param)
+{
+	msg *m;
+
+	while (1) {
+		m = receive_message2();
+		DIE(m == NULL, "Read error");
+
+		send_message1(m);
+		free(m);
+	}
+}
+
+#define SPEED 1
+#define DELAY 2
+#define LOSS 3
+#define CORRUPT 4
+#define CORRUPT2 5
+#define REORDER 6
+
+int split_param(char *p, int *type, double *value)
+{
+	char c[100];
+	int crt = 0, t = 1;
+
+	for (; *p != 0; p++) {
+		if (t && *p == '=') {
+			t = 0;
+			c[crt] = 0;
+			crt = 0;
+
+			if (!strcasecmp(c, "speed"))
+				*type = SPEED;
+			else if (!strcasecmp(c, "delay"))
+				*type = DELAY;
+			else if (!strcasecmp(c, "loss"))
+				*type = LOSS;
+			else if (!strcasecmp(c, "corrupt"))
+				*type = CORRUPT;
+			else if (!strcasecmp(c, "corrupt2"))
+				*type = CORRUPT2;
+			else if (!strcasecmp(c, "reorder"))
+				*type = REORDER;
+			else {
+				printf("Unknown parameter %s\n", c);
+				return -1;
+			}
+		} else
+			c[crt++] = *p;
+	}
+	*value = atof(c);
+	return 0;
+}
+
+int guess_hz()
+{
+	long long a, b, diff = 0;
+	int i;
+
+	for (i = 0; i < 100; i++) {
+		a = now();
+		// 0.1ms
+		usleep(100);
+		b = now();
+		diff += (b - a);
+	}
+
+	int error = (int)(diff / i - 100);
+	printf("Average error  100 was %d\n", error);
+
+	diff = 0;
+	for (i = 0; i < 100; i++) {
+		a = now();
+		// 0.1ms
+		usleep(1000);
+		b = now();
+		diff += (b - a);
+	}
+
+	error = (int)(diff / i - 1000);
+	printf("Average error 1000 was %d\n", error);
+	return error;
+}
+
+int main(int argc, char **argv)
+{
+	pthread_t link_thread, fw_thread;
+	int i;
+	for (i = 1; i < argc; i++) {
+		int type;
+		double value;
+		if (split_param(argv[i], &type, &value) < 0) {
+			printf("Usage %s speed=[speed in mb/s] delay=[delay in ms] "
+					"loss=[percent of packets] corrupt=[percent of packets]\n",
+					argv[0]);
+			return -1;
+		}
+
+		switch (type) {
+		case SPEED:
+			printf("Setting speed to %f Mb/s\n", value);
+			serialization_delay = 2 * 11200 / value;
+			break;
+		case DELAY:
+			printf("Setting delay %f to ms\n", value);
+			delay = value * 1000;
+			break;
+		case LOSS:
+			printf("Setting loss rate to %f%%\n", value);
+			loss = value;
+			break;
+		case CORRUPT:
+			printf("Setting corruption rate to %f%%\n", value);
+			corrupt = value;
+			break;
+		case CORRUPT2:
+			printf("Setting double corruption to %f%%\n", value);
+			corrupt2 = value;
+			break;
+		case REORDER:
+			printf("Setting reorder rate to %f%%\n", value);
+			reorder = value;
+			break;
+		}
+	}
+
+#if DEBUG
+	guess_hz();
+#endif
+
+	init_sockets();
+	srand(time(NULL));
+	buffer = create_queue();
+	assert(!pthread_create(&link_thread, NULL, link_scheduler, NULL));
+	assert(!pthread_create(&fw_thread, NULL, run_forwarding, NULL));
+
+	run_reverse_forwarding(NULL);
+	return 0;
+}
diff --git a/lab2/link_emulator/link.h b/common/link_emulator/link.h
similarity index 56%
rename from lab2/link_emulator/link.h
rename to common/link_emulator/link.h
index 92b64db0b579489f735064e9be2428ef1e984846..ab2a462c1e719d60c78968bb4a58395a23ebb0d5 100644
--- a/lab2/link_emulator/link.h
+++ b/common/link_emulator/link.h
@@ -1,10 +1,11 @@
-#ifndef LINK
-#define LINK
+#ifndef _LINK_H_
+#define _LINK_H_
+
 #include "lib.h"
 
 typedef struct {
-  msg* m;
+  msg *m;
   unsigned long long finish_time;
 } msg_in_flight;
 
-#endif
+#endif /* _LINK_H_ */
diff --git a/common/link_emulator/queue.c b/common/link_emulator/queue.c
new file mode 100644
index 0000000000000000000000000000000000000000..88d3196c88e6971c8c2fd4035d8cc743cf09a8b9
--- /dev/null
+++ b/common/link_emulator/queue.c
@@ -0,0 +1,66 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "queue.h"
+#include "include/utils.h"
+
+void enqueue(queue * q, void *m)
+{
+	if (q->first == NULL) {
+		assert(q->last == NULL);
+
+		q->first = (queue_entry *) malloc(sizeof(queue_entry));
+		DIE(!q->first, "malloc");
+		q->last = q->first;
+	} else {
+		queue_entry *t = (queue_entry *) malloc(sizeof(queue_entry));
+		DIE(!t, "malloc");
+		q->first->prev = t;
+		q->first = t;
+	}
+
+	q->first->prev = NULL;
+	q->first->crt = m;
+
+	q->size++;
+}
+
+void *dequeue(queue * q)
+{
+	queue_entry *t;
+	void *m;
+
+	if (q->first == NULL) {
+		assert(q->size == 0);
+		return NULL;
+	} else if (q->first == q->last) {
+		t = q->last;
+		assert(q->first->prev == NULL);
+
+		q->first = q->last = NULL;
+	} else {
+		t = q->last;
+		q->last = q->last->prev;
+	}
+
+	m = t->crt;
+	free(t);
+	q->size--;
+
+	return m;
+}
+
+queue *create_queue()
+{
+	queue *q = (queue *) malloc(sizeof(queue));
+	DIE(!q, "malloc");
+	q->first = NULL;
+	q->last = NULL;
+	q->size = 0;
+	return q;
+}
+
+void destroy_queue(queue * q)
+{
+	assert(0);
+}
diff --git a/common/link_emulator/queue.h b/common/link_emulator/queue.h
new file mode 100644
index 0000000000000000000000000000000000000000..08c5a65d242cea51c96a8998d3cb3db923a68f9c
--- /dev/null
+++ b/common/link_emulator/queue.h
@@ -0,0 +1,25 @@
+#ifndef _QUEUE_H_
+#define _QUEUE_H_
+
+#include "lib.h"
+
+struct q {
+  void* crt;
+  struct q *prev;
+};
+
+typedef struct q queue_entry;
+
+typedef struct {
+  int size;
+  queue_entry *first;
+  queue_entry *last;
+} queue;
+
+void enqueue(queue *q,void *m);
+void *dequeue(queue *q);
+
+queue *create_queue();
+void destroy_queue(queue *q);
+
+#endif /* _QUEUE_H_ */
diff --git a/common/run_experiment.sh b/common/run_experiment.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f363f62065d61990b2c2738fc29e14a8079d3a31
--- /dev/null
+++ b/common/run_experiment.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+SPEED=1
+DELAY=1
+LOSS=0
+# Adjust the corruption
+CORRUPT=0
+
+# Second bit corruption rate
+CORRUPT2=0
+
+{
+    pkill -9 link
+    pkill -9 recv
+    pkill -9 send
+} &> /dev/null
+
+./link_emulator/link speed=$SPEED delay=$DELAY loss=$LOSS corrupt=$CORRUPT \
+    corrupt2=$CORRUPT2 > link_emulator.out 2> link_emulator.err &
+sleep 1
+./recv &
+sleep 1
+
+./send
diff --git a/common/utils.c b/common/utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..7714e53e08ea0781af5c155e6c0212e64b071289
--- /dev/null
+++ b/common/utils.c
@@ -0,0 +1,42 @@
+#include "include/utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define HEX_LINESIZE 16
+
+void hex_dump(const void * addr, size_t size)
+{
+    int i;
+    unsigned char buff[HEX_LINESIZE+1];
+    const unsigned char * pc = (const unsigned char *)addr;
+
+    // Process every byte in the data.
+    for (i = 0; i < size; i++) {
+	// Multiple of HEX_LINESIZE means new or first line (with line offset).
+	if ((i % HEX_LINESIZE) == 0) {
+	    // Only print previous-line ASCII buffer for lines beyond first.
+	    if (i != 0) printf("  %s\n", buff);
+	    printf("  %04x ", i);
+	}
+
+	// Now the hex code for the specific character.
+	printf(" %02x", pc[i]);
+
+	// And buffer a printable ASCII character for later.
+	if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better.
+	    buff[i % HEX_LINESIZE] = '.';
+	else
+	    buff[i % HEX_LINESIZE] = pc[i];
+	buff[(i % HEX_LINESIZE) + 1] = '\0';
+    }
+
+    // Pad out last line if not exactly HEX_LINESIZE characters.
+    while ((i % HEX_LINESIZE) != 0) {
+	printf("   ");
+	i++;
+    }
+
+    // And print the final ASCII buffer.
+    printf("  %s\n", buff);
+}
diff --git a/lab2/Makefile b/lab2/Makefile
index 7715dbc98d5b0351751df9a9e7bf12a799b8f81b..20341b2820b4a502de5d921230cc02118b24095f 100644
--- a/lab2/Makefile
+++ b/lab2/Makefile
@@ -1,19 +1,19 @@
-CFLAGS= -Wall -Werror -Wno-error=unused-variable
+CFLAGS= -Wall -Werror -Wno-error=unused-variable -I../common/
 
 all: send recv
 
+../common/utils.o:
+	$(MAKE) -C ../common
+
 link_emulator/lib.o:
 	$(MAKE) -C link_emulator
 
-send: send.o link_emulator/lib.o
-	gcc $(CFLAGS) -g send.o link_emulator/lib.o -o send
-
-recv: recv.o link_emulator/lib.o
-	gcc $(CFLAGS) -g recv.o link_emulator/lib.o -o recv
+send: send.o link_emulator/lib.o ../common/utils.o
 
-.c.o:
-	gcc $(CFLAGS) -g -c $?
+recv: recv.o link_emulator/lib.o ../common/utils.o
 
 clean:
 	$(MAKE) -C link_emulator clean
 	-rm -f *.o send recv
+
+.PHONY: all clean
diff --git a/lab2/common.h b/lab2/common.h
index 18344a61becf9d773a778bdd0f6d572f1b5c0b68..94b55bdd1aa83d2f630b3b47fdbcd84549fd0fc5 100644
--- a/lab2/common.h
+++ b/lab2/common.h
@@ -5,14 +5,9 @@
 /* Atributul este folosit pentru a anunta compilatorul sa nu alinieze structura */
 /* DELIM | DATE | DELIM */
 struct __attribute__((packed)) Frame {
-    char frame_delim_start[2]; /* DEL STX */
+    char frame_delim_start[2]; /* DLE STX */
+
     /* TODO 2: Add source and destination */
     char payload[30];  /* Datele pe care vrem sa le transmitem */
-    char frame_delim_end[2]; /* DEL ETX */
+    char frame_delim_end[2]; /* DLE ETX */
 };
-
-/* Sends one character to the other end via the Physical layer */
-int send_byte(char c);
-/* Receives one character from the other end, if nothing is sent by the other end, returns a random character */
-char recv_byte();
-
diff --git a/lab2/link_emulator b/lab2/link_emulator
new file mode 120000
index 0000000000000000000000000000000000000000..ce372f42c8092972e2e4b833e566b786f3d73cc8
--- /dev/null
+++ b/lab2/link_emulator
@@ -0,0 +1 @@
+../common/link_emulator
\ No newline at end of file
diff --git a/lab2/link_emulator.err b/lab2/link_emulator.err
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lab2/link_emulator.out b/lab2/link_emulator.out
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lab2/link_emulator/Makefile b/lab2/link_emulator/Makefile
deleted file mode 100644
index b2105de964c0cbc2b4c5832d70801110670a8740..0000000000000000000000000000000000000000
--- a/lab2/link_emulator/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-all: link lib.o
-
-link: link.o queue.o
-	gcc -g link.o queue.o -o link -lpthread
-
-.c.o: 
-	gcc -Wall -g -c $? -lpthread
-
-clean:
-	-rm *.o link
diff --git a/lab2/link_emulator/lib.c b/lab2/link_emulator/lib.c
deleted file mode 100644
index 1d9e87bc877c6dba8926f4a5175293e894586fe2..0000000000000000000000000000000000000000
--- a/lab2/link_emulator/lib.c
+++ /dev/null
@@ -1,105 +0,0 @@
-#include "lib.h"
-#include <arpa/inet.h>
-#include <poll.h>
-//#include <stropts.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <string.h>
-
-struct sockaddr_in addr_local, addr_remote;
-int s;
-struct pollfd fds[1];
-
-void set_local_port(int port)
-{
-  memset((char *) &addr_local, 0, sizeof(addr_local));
-  addr_local.sin_family = AF_INET;
-  addr_local.sin_port = htons(port);
-  addr_local.sin_addr.s_addr = htonl(INADDR_ANY);
-}
-
-void set_remote(char* ip, int port){
-  memset((char *) &addr_remote, 0, sizeof(addr_remote));
-  addr_remote.sin_family = AF_INET;
-  addr_remote.sin_port = htons(port);
-  if (inet_aton(ip, &addr_remote.sin_addr)==0) {
-    perror("inet_aton failed\n");
-    exit(1);
-  }
-}
-
-void init(char* remote, int REMOTE_PORT){
-  if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1){
-    perror("Error creating socket");
-    exit(1);
-  }
-
-  set_local_port(0);
-  set_remote(remote,REMOTE_PORT);
-
-  if (bind(s, (struct sockaddr*)&addr_local, sizeof(addr_local))==-1){
-    perror("Failed to bind");
-    exit(1);
-  }
-
-  fds[0].fd = s;
-  fds[0].events = POLLIN;
-
-  msg m;
-  send_message(&m);
-}
-
-int send_message(const msg* m){
-  return sendto(s, m, sizeof(msg), 0,(struct sockaddr*) &addr_remote, sizeof(addr_remote));
-}
-
-/*msg* receive_message(){
-  msg* ret = (msg*)malloc(sizeof(msg));
-  if (recvfrom(s, ret, sizeof(msg), 0, NULL, NULL)==-1){
-    free(ret);
-    return NULL;
-  }
-  return ret;
-  }*/
-
-int recv_message(msg* ret){
-  return recvfrom(s, ret, sizeof(msg), 0, NULL, NULL);
-}
-
-int send_byte(char c) {
-        msg m;
-        m.len = 1;
-        m.payload[0] = c;
-        printf("Sending byte %c \n", m.payload[0]);
-        return send_message(&m);
-}
-
-char recv_byte() {
-        msg m;
-        int ret;
-	usleep(100000);
-        ret =  recvfrom(s, &m, sizeof(msg), MSG_DONTWAIT, NULL, NULL);
-        if (ret < 0) {
-                char c = rand() % 128;
-                return c;
-                
-        } else {
-                return m.payload[0];
-        }
-}
-
-//timeout in millis
-/*msg* receive_message_timeout(int timeout){
-  int ret = poll(fds,1,timeout);
-
-  if (ret>0){
-    if (fds[0].revents & POLLIN)
-      return receive_message();
-  }
-  
-  return NULL;
-  }*/
diff --git a/lab2/link_emulator/lib.h b/lab2/link_emulator/lib.h
deleted file mode 100644
index b6f98b040fda0d8ee49efc3ea2faf949c2067d40..0000000000000000000000000000000000000000
--- a/lab2/link_emulator/lib.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef LIB
-#define LIB
-
-#define MAX_LEN 1400
-
-typedef struct {
-  // int type;
-  int len;
-  char payload[MAX_LEN];
-} msg;
-
-void init(char* remote,int remote_port);
-void set_local_port(int port);
-void set_remote(char* ip, int port);
-int send_message(const msg* m);
-int recv_message(msg* r);
-int send_byte(char c);
-char recv_byte();
-
-//msg* receive_message_timeout(int timeout);
-
-#endif
-
diff --git a/lab2/link_emulator/link.c b/lab2/link_emulator/link.c
deleted file mode 100644
index a6c1c73fa63dddae6b861c8e7e78648eb67a4634..0000000000000000000000000000000000000000
--- a/lab2/link_emulator/link.c
+++ /dev/null
@@ -1,415 +0,0 @@
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <string.h>
-//#include <asm/param.h>
-#include "queue.h"
-#include "link.h"
-
-#define DEBUG 0
-
-int BUFFER_SIZE=1000;
-
-int serialization_delay = 1000;
-int delay = 1000;
-int loss = 0;
-int corrupt = 0;
-
-#define CHANNEL_BUSY 1
-#define CHANNEL_IDLE 0
-
-#define LOCAL_PORT1 10000
-#define LOCAL_PORT2 10001
-
-pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER;
-pthread_cond_t buffer_cond = PTHREAD_COND_INITIALIZER; 
-queue* buffer;
-
-struct sockaddr_in local_addr1, remote_addr1;
-struct sockaddr_in local_addr2, remote_addr2;
-
-int s1,s2;
-socklen_t sz;
-
-//1 if a message was received on this link
-int link_up1 = 0;
-int link_up2 = 0;
-
-void init_sockets()
-{
-  /*LOCAL ADDRESSES*/
-
-  memset((char *) &local_addr1, 0, sizeof(local_addr1));
-  local_addr1.sin_family = AF_INET;
-  local_addr1.sin_port = htons(LOCAL_PORT1);
-  local_addr1.sin_addr.s_addr = htonl(INADDR_ANY);
-
-  memset((char *) &local_addr2, 0, sizeof(local_addr2));
-  local_addr2.sin_family = AF_INET;
-  local_addr2.sin_port = htons(LOCAL_PORT2);
-  local_addr2.sin_addr.s_addr = htonl(INADDR_ANY);
-
-  if ((s1=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1){
-    perror("Error creating socket");
-    exit(1);
-  }
-
-  //now bind
-  if (bind(s1, (struct sockaddr*)&local_addr1, sizeof(local_addr1))==-1){
-    perror("Failed to bind s1");
-    exit(1);
-  }
-
-  if ((s2=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1){
-    perror("Error creating socket 2");
-    exit(1);
-  }
-
-  if (bind(s2, (struct sockaddr*)&local_addr2, sizeof(local_addr2))==-1){
-    perror("Failed to bind s2");
-    exit(1);
-  }
-}
-
-int send_message1(const msg* m){
-  if (!link_up1){
-    printf("Trying to send a message but remote peer is not connected on my port %d\n",LOCAL_PORT1);
-  }
-  return sendto(s1, m, sizeof(msg), 0,(struct sockaddr*) &remote_addr1, sizeof(remote_addr1));
-}
-
-msg* receive_message1(){
-  msg* ret;
-
- a:
-  ret = (msg*)malloc(sizeof(msg));
-  
-  if (!link_up1){
-    sz = sizeof(remote_addr1);
-    if (recvfrom(s1, ret, sizeof(msg), 0, (struct sockaddr*)&remote_addr1, &sz)==-1){
-      free(ret);
-      return NULL;
-    }
-    
-    link_up1 = 1;
-
-#if DEBUG
-    printf("Link 1 is up, remote addr is %s port %d\n",inet_ntoa(remote_addr1.sin_addr),ntohs(remote_addr1.sin_port));
-#endif
-
-    free(ret);
-    goto a;
-  }
-  else if (recvfrom(s1, ret, sizeof(msg), 0, NULL,NULL)==-1){
-    free(ret);
-    return NULL;
-  }
-  return ret;
-}
-
-int send_message2(const msg* m){
-  if (!link_up2){
-    printf("Trying to send a message but remote peer is not connected on my port %d\n",LOCAL_PORT2);
-  }
-  return sendto(s2, m, sizeof(msg), 0,(struct sockaddr*) &remote_addr2, sizeof(remote_addr2));
-}
-
-msg* receive_message2(){
-  msg* ret;
-
- a:
-  ret = (msg*)malloc(sizeof(msg));
-  if (!link_up2){
-    sz = sizeof(remote_addr2);
-    if (recvfrom(s2, ret, sizeof(msg), 0, (struct sockaddr*)&remote_addr2, &sz)==-1){
-      free(ret);
-      return NULL;
-    }
-    link_up2 = 1;
-
-#if DEBUG
-    printf("Link 2 is up, remote addr is %s port %d\n",inet_ntoa(remote_addr2.sin_addr),ntohs(remote_addr2.sin_port));
-#endif
-
-    free(ret);
-    goto a;
-  }
-  else
-  if (recvfrom(s2, ret, sizeof(msg), 0, NULL, NULL)==-1){
-    free(ret);
-    return NULL;
-  }
-
-  return ret;
-}
-
-unsigned long long now(){ 
-  struct timeval b;
-  gettimeofday(&b,NULL);
-  return (unsigned long long)b.tv_sec*1000000+b.tv_usec;
-}
-
-void* link_scheduler(void *argument)
-{
-  msg_in_flight* mif;
-  queue* in_flight = create_queue();
-  long long idle_time = 0;
-  long long crt_time,wait_time_idle,wait_time_send;
-  int stuff;
-
-  while (1){
-    crt_time = now();
-
-#if DEBUG
-    printf("In flight size %d at %lld\n",in_flight->size, crt_time);
-#endif
-
-    while (in_flight->size>0){
-      msg_in_flight* last = (msg_in_flight*)in_flight->last->crt;
-      if (crt_time<last->finish_time){
-	break;
-      }
-
-      //else send first packet on the wire
-      mif = (msg_in_flight*)dequeue(in_flight);
-      if (!mif){
-	printf("Error in deque: expecting non null msg!\n");
-	exit(1);
-      }
-      if (send_message2(mif->m)<=0)
-	perror("SNDMSG2");
-      
-#if DEBUG
-      printf("Sending message\n");
-#endif
-
-      free(mif->m);
-      free(mif);
-      mif = NULL;
-    }
-
-    pthread_mutex_lock( &buffer_lock );
-    stuff = buffer->size>0;
-    pthread_mutex_unlock( &buffer_lock );          
-
-#if DEBUG
-    printf("Stuff is %d\n",stuff);
-#endif
-
-    wait_time_send = wait_time_idle = 0;
-
-    //crt_time = now();
-    
-    //now see if we can put stuff in flight
-    if (stuff && crt_time>=idle_time){
-      idle_time = crt_time + serialization_delay;
-      
-      mif = (msg_in_flight*)malloc(sizeof(msg_in_flight));
-      assert(mif);
-      
-      pthread_mutex_lock( &buffer_lock );
-      mif->m = (msg*)dequeue(buffer);
-      pthread_mutex_unlock( &buffer_lock );          
-      
-      assert(mif->m);
-      mif->finish_time = crt_time + serialization_delay + delay;
-      
-      //send message here from buffer to link
-      enqueue(in_flight,mif);
-
-#if DEBUG
-      printf("Enquing message\n");
-#endif
-    }
-    wait_time_idle = idle_time - crt_time;
-    
-    if (in_flight->size>0){
-      msg_in_flight* last = (msg_in_flight*)in_flight->last->crt;
-      wait_time_send = last->finish_time-crt_time;
-    }
-
-    if (wait_time_idle>0 || wait_time_send>0){
-      long long wait_time = 0;
-      if (wait_time_idle>0) wait_time = wait_time_idle;
-      if (wait_time_send>0&&wait_time_send<wait_time)
-	wait_time = wait_time_send;
-
-      //printf("Sleeping %lld\n", wait_time);
-      usleep(wait_time);
-    }
-    else {
-	pthread_mutex_lock( &buffer_lock );
-	assert(!buffer->size);
-#if DEBUG
-	printf("Waiting on cond\n");
-#endif
-	pthread_cond_wait(&buffer_cond,&buffer_lock);
-	pthread_mutex_unlock( &buffer_lock );          
-    }
-  }
- 
-  return NULL;
-}
-
-void* run_forwarding(void* param){
-  msg *m;
-
-  while (1){
-    int overflow;
-    m = receive_message1();
-    if (m==NULL){
-      perror("Read error");
-      exit(1);
-    }
-    
-    //check queue space
-    pthread_mutex_lock( &buffer_lock );
-    overflow = buffer->size>=BUFFER_SIZE;
-    pthread_mutex_unlock( &buffer_lock );    
-
-    if (overflow || (rand()%100)<loss) {
-      //just drop message
-      free(m);
-      printf("Dropped packet\n");
-    } 
-    else {
-      if (rand()%100 < corrupt){
-	m->payload[rand()%m->len] = rand()%128;
-      }
-      //printf("Enqueue 1.");
-      pthread_mutex_lock( &buffer_lock );
-      enqueue(buffer,m);
-      pthread_cond_signal(&buffer_cond);
-      pthread_mutex_unlock(&buffer_lock);      
-      //printf("Done!\n");
-    }
-  }
-}
-
-void* run_reverse_forwarding(void* param){
-  msg *m;
-
-  while (1){
-    m = receive_message2();
-    if (m==NULL){
-      perror("Read error");
-      exit(1);
-    }
-    
-    send_message1(m);
-    free(m);
-  }
-}
-
-#define SPEED 1
-#define DELAY 2
-#define LOSS 3
-#define CORRUPT 4
-
-int split_param(char* p,int * type, double* value){
-  char c[100];
-  int crt = 0, t=1;
-  
-  for (;*p!=0;p++){
-    if (t && *p=='='){
-      t = 0;
-      c[crt] = 0;
-      crt = 0;
-      
-      if (!strcasecmp(c,"speed"))
-	*type = SPEED;
-      else if (!strcasecmp(c,"delay"))
-	*type = DELAY;
-      else if (!strcasecmp(c,"loss"))
-	*type = LOSS;
-      else if (!strcasecmp(c,"corrupt"))
-	*type = CORRUPT;
-      else {
-	printf ("Unknown parameter %s\n",c);
-	return -1;
-      }
-    }
-    else c[crt++] = *p;
-  }
-  *value = atof(c);
-  return 0;
-}
-
-int guess_hz(){
-  long long a,b,diff = 0;
-  int i;
-  
-  for (i=0;i<100;i++){
-    a = now();
-    //0.1ms
-    usleep(100);
-    b = now();
-    diff += (b-a);
-  }
-
-  int error = (int)(diff/i-100);
-  printf("Average error  100 was %d\n",error);
-
-  diff = 0;
-  for (i=0;i<100;i++){
-    a = now();
-    //0.1ms
-    usleep(1000);
-    b = now();
-    diff += (b-a);
-  }
-
-  error = (int)(diff/i-1000);
-  printf("Average error 1000 was %d\n",error);
-  return  error;
-}
-
-int main(int argc, char** argv){
-  pthread_t link_thread, fw_thread;
-  int i;
-  for (i=1;i<argc;i++){
-    int type;
-    double value;
-    if (split_param(argv[i],&type,&value)<0){
-      printf("Usage %s speed=[speed in mb/s] delay=[delay in ms] loss=[percent of packets] corrupt=[percent of packets]\n",argv[0]);
-      return -1;
-    }
-    
-    switch(type){
-    case SPEED:
-      printf("Setting speed to %f Mb/s\n",value);
-      serialization_delay = 2*11200/value;break;
-    case DELAY:
-      printf("Setting delay %f to ms\n",value);
-      delay = value*1000; break;
-    case LOSS:
-      printf("Setting loss rate to %f%%\n",value);      
-      loss = value; break;
-    case CORRUPT:
-      printf("Setting corruption rate to %f%%\n",value);      
-      corrupt = value; break;
-    }
-  }
-
-#if DEBUG
-  guess_hz();
-#endif
-
-  init_sockets();
-  srand(time(NULL));
-  buffer = create_queue();
-  assert(!pthread_create(&link_thread, NULL, link_scheduler, NULL));
-  assert(!pthread_create(&fw_thread, NULL, run_forwarding, NULL));
-
-  run_reverse_forwarding(NULL);
-  return 0;
-}
diff --git a/lab2/link_emulator/queue.c b/lab2/link_emulator/queue.c
deleted file mode 100644
index daced7e77ff0a452492049c5164c98a38b9c9fe6..0000000000000000000000000000000000000000
--- a/lab2/link_emulator/queue.c
+++ /dev/null
@@ -1,61 +0,0 @@
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "queue.h"
-
-void enqueue(queue* q,void* m){
-  if (q->first==NULL){
-    assert(q->last==NULL);
-
-    q->first = (queue_entry*)malloc(sizeof(queue_entry));
-    q->last = q->first;
-  }
-  else {
-    queue_entry * t = (queue_entry*)malloc(sizeof(queue_entry));
-    q->first->prev = t;
-    q->first = t;
-  }
-
-  q->first->prev = NULL;
-  q->first->crt = m;
-
-  q->size++;
-}
-
-void* dequeue(queue* q){
-  queue_entry  * t;
-  void* m;
-
-  if (q->first==NULL){
-    assert(q->size==0);
-    return NULL;
-  }
-  else if (q->first==q->last){
-    t = q->last;
-    assert(q->first->prev==NULL);
-
-    q->first = q->last = NULL;
-  }
-  else {
-    t = q->last;
-    q->last = q->last->prev;
-  }
-
-  m = t->crt;
-  free(t);
-  q->size--;
-
-  return m;
-}
-
-queue* create_queue(){
-  queue* q = (queue*)malloc(sizeof(queue));
-  q->first = NULL;
-  q->last = NULL;
-  q->size = 0;
-  return q;
-}
-
-void destroy_queue(queue* q){
-  assert(0);
-}
diff --git a/lab2/link_emulator/queue.h b/lab2/link_emulator/queue.h
deleted file mode 100644
index 9b6a45b0b5a77a3875e8523a52ad61bd48a69c5d..0000000000000000000000000000000000000000
--- a/lab2/link_emulator/queue.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef QUEUE
-#define QUEUE
-#include "lib.h"
-
-
-struct q {
-  void* crt;
-  struct q* prev;
-};
-
-typedef struct q queue_entry;
-
-typedef struct {
-  int size;
-  queue_entry* first;
-  queue_entry* last;
-} queue;
-
-void enqueue(queue* q,void* m);
-void* dequeue(queue* q);
-
-queue* create_queue();
-void destroy_queue(queue* q);
-
-#endif
diff --git a/lab2/recv b/lab2/recv
new file mode 100755
index 0000000000000000000000000000000000000000..ec239882d193b863ef0f6be1622bfcd711d80f54
Binary files /dev/null and b/lab2/recv differ
diff --git a/lab2/recv.c b/lab2/recv.c
index 99dba5d27133051a8898435ceef64bb876e95dfc..3e6d72d6da2f7223ea8e616d2a54b039c27af001 100644
--- a/lab2/recv.c
+++ b/lab2/recv.c
@@ -4,14 +4,20 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include "link_emulator/lib.h"
-
-/* Do not touch these two */
+#include "include/utils.h"
+
+/**
+ * You can change these to communicate with another colleague.
+ * There are several factors that could stop this from working over the
+ * internet, but if you're on the same network it should work.
+ * Just fill in their IP here and make sure that you use the same port.
+ */
 #define HOST "127.0.0.1"
 #define PORT 10001
 
 #include "common.h"
 
-/* Our unqiue layer 2 ID */
+/* Our unique layer 2 ID */
 static int ID = 123131;
 
 /* Function which our protocol implementation will provide to the upper layer. */
@@ -32,10 +38,11 @@ int recv_frame(char *buf, int size)
 }
 
 int main(int argc,char** argv){
-	/* Do not touch this */
+	/* Don't modify this */
 	init(HOST,PORT);
 
-
+        // TODO remove these recives, whih are hardcoded to receive a "Hello"
+	// message, and replace them with code that can receive any message.
 	char c;
 
 	/* Wait for the start of a frame */
diff --git a/lab2/run_experiment.sh b/lab2/run_experiment.sh
deleted file mode 100755
index fadeeb3a1182ae5cde6a7b5f4161b98039fcd7f2..0000000000000000000000000000000000000000
--- a/lab2/run_experiment.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-
-SPEED=1
-DELAY=1
-LOSS=0
-CORRUPT=0
-
-{
-    killall link
-    killall recv
-    killall send
-} &> /dev/null
-
-./link_emulator/link speed=$SPEED delay=$DELAY loss=$LOSS corrupt=$CORRUPT &> /dev/null &
-sleep 1
-./recv &
-sleep 1
-
-./send
-
-#sleep 2
-#echo "Finished transfer, checking files"
-#diff tema.txt recv_tema.txt
diff --git a/lab2/run_experiment.sh b/lab2/run_experiment.sh
new file mode 120000
index 0000000000000000000000000000000000000000..a204cff38ba9a5a5601351d3569f0c0bd8d05fde
--- /dev/null
+++ b/lab2/run_experiment.sh
@@ -0,0 +1 @@
+../common/run_experiment.sh
\ No newline at end of file
diff --git a/lab2/send b/lab2/send
new file mode 100755
index 0000000000000000000000000000000000000000..4599f0679206ff30a524decc8981d7142fcac658
Binary files /dev/null and b/lab2/send differ
diff --git a/lab2/send.c b/lab2/send.c
index 33fdd738409cf61c86049f8458720b23ddcd8c26..7e70a31960580501367f020bb8f9bf8eded35f11 100644
--- a/lab2/send.c
+++ b/lab2/send.c
@@ -6,8 +6,14 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include "link_emulator/lib.h"
-
-/* We don't touch this */
+#include "include/utils.h"
+
+/**
+ * You can change these to communicate with another colleague.
+ * There are several factors that could stop this from working over the
+ * internet, but if you're on the same network it should work.
+ * Just fill in their IP here and make sure that you use the same port.
+ */
 #define HOST "127.0.0.1"
 #define PORT 10000
 
@@ -38,6 +44,8 @@ int main(int argc,char** argv){
 	// Don't touch this
 	init(HOST,PORT);
 
+	// TODO remove these sends, whih are hardcoded to send a "Hello"
+	// message, and replace them with code that can send any message.
 	/* Send Hello */
 	send_byte(DLE);
 	send_byte(STX);
diff --git a/lab3/Makefile b/lab3/Makefile
index 364094868399f95f307bd0a48dc41add8a096111..c1f3d41dfe30c45bec5f6cccff1304307362eab1 100644
--- a/lab3/Makefile
+++ b/lab3/Makefile
@@ -1,21 +1,21 @@
-CFLAGS= -Wall -Werror -Wno-error=unused-variable
+CFLAGS= -Wall -Werror -Wno-error=unused-variable -I../common/
 
 all: send recv
 
+../common/utils.o:
+		$(MAKE) -C ../common
+
 link_emulator/lib.o:
 	$(MAKE) -C link_emulator
 
-send: send.o link_emulator/lib.o common.o
-	gcc $(CFLAGS) -g send.o link_emulator/lib.o common.o -o send
+send: send.o link_emulator/lib.o common.o ../common/utils.o
 
-recv: recv.o link_emulator/lib.o common.o
-	gcc $(CFLAGS) -g recv.o link_emulator/lib.o common.o -o recv
+recv: recv.o link_emulator/lib.o common.o ../common/utils.o
 
 common.o: common.c common.h
 
-.c.o:
-	gcc $(CFLAGS) -g -c $?
-
 clean:
 	$(MAKE) -C link_emulator clean
 	-rm -f *.o send recv common.h.gch
+
+.PHONY: all clean
diff --git a/lab3/link_emulator b/lab3/link_emulator
new file mode 120000
index 0000000000000000000000000000000000000000..ce372f42c8092972e2e4b833e566b786f3d73cc8
--- /dev/null
+++ b/lab3/link_emulator
@@ -0,0 +1 @@
+../common/link_emulator
\ No newline at end of file
diff --git a/lab3/link_emulator/lib.c b/lab3/link_emulator/lib.c
deleted file mode 100644
index 360ffbea389616bc83970414866f8c353fa02abe..0000000000000000000000000000000000000000
--- a/lab3/link_emulator/lib.c
+++ /dev/null
@@ -1,103 +0,0 @@
-#include "lib.h"
-#include <arpa/inet.h>
-#include <poll.h>
-//#include <stropts.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <string.h>
-
-struct sockaddr_in addr_local, addr_remote;
-int s;
-struct pollfd fds[1];
-
-static int send_message(const msg* m);
-
-void set_local_port(int port)
-{
-  memset((char *) &addr_local, 0, sizeof(addr_local));
-  addr_local.sin_family = AF_INET;
-  addr_local.sin_port = htons(port);
-  addr_local.sin_addr.s_addr = htonl(INADDR_ANY);
-}
-
-void set_remote(char* ip, int port){
-  memset((char *) &addr_remote, 0, sizeof(addr_remote));
-  addr_remote.sin_family = AF_INET;
-  addr_remote.sin_port = htons(port);
-  if (inet_aton(ip, &addr_remote.sin_addr)==0) {
-    perror("inet_aton failed\n");
-    exit(1);
-  }
-}
-
-void init(char* remote, int REMOTE_PORT){
-  if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1){
-    perror("Error creating socket");
-    exit(1);
-  }
-
-  set_local_port(0);
-  set_remote(remote,REMOTE_PORT);
-
-  if (bind(s, (struct sockaddr*)&addr_local, sizeof(addr_local))==-1){
-    perror("Failed to bind");
-    exit(1);
-  }
-
-  fds[0].fd = s;
-  fds[0].events = POLLIN;
-
-  msg m;
-  send_message(&m);
-}
-
-int send_message(const msg* m){
-  return sendto(s, m, sizeof(msg), 0,(struct sockaddr*) &addr_remote, sizeof(addr_remote));
-}
-
-void link_send(void *buf, int len) {
-	msg m;
-
-	memcpy(m.payload, buf, len);
-	m.len = len;
-
-	send_message(&m);
-}
-
-/*msg* receive_message(){
-  msg* ret = (msg*)malloc(sizeof(msg));
-  if (recvfrom(s, ret, sizeof(msg), 0, NULL, NULL)==-1){
-    free(ret);
-    return NULL;
-  }
-  return ret;
-  }*/
-
-int recv_message(msg* ret){
-  return recvfrom(s, ret, sizeof(msg), 0, NULL, NULL);
-}
-
-
-int link_recv(void *buf, int len) {
-	msg m;
-	recv_message(&m);
-	int r = m.len < len ? m.len : len;
-	memcpy(buf, m.payload, r);
-	return r;
-}
-
-//timeout in millis
-/*msg* receive_message_timeout(int timeout){
-  int ret = poll(fds,1,timeout);
-
-  if (ret>0){
-    if (fds[0].revents & POLLIN)
-      return receive_message();
-  }
-  
-  return NULL;
-  }*/
diff --git a/lab3/link_emulator/lib.h b/lab3/link_emulator/lib.h
deleted file mode 100644
index 5d8634c71f4ba58558423b65bd89e4d9f0b19f76..0000000000000000000000000000000000000000
--- a/lab3/link_emulator/lib.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef LIB
-#define LIB
-
-#include <stddef.h>
-
-#define MAX_LEN 1500
-
-typedef struct {
-  int len;
-  char payload[MAX_LEN];
-} msg;
-
-void init(char* remote,int remote_port);
-void set_local_port(int port);
-void set_remote(char* ip, int port);
-int link_recv(void *buf, int len);
-void link_send(void *buf, int len);
-
-#endif
diff --git a/lab3/link_emulator/link.c b/lab3/link_emulator/link.c
deleted file mode 100644
index 71b18ab3bbec1c6c2af24cf21656e414e3f01ef0..0000000000000000000000000000000000000000
--- a/lab3/link_emulator/link.c
+++ /dev/null
@@ -1,428 +0,0 @@
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#include <string.h>
-//#include <asm/param.h>
-#include "queue.h"
-#include "link.h"
-
-#define DEBUG 0
-
-int BUFFER_SIZE=1000;
-
-int serialization_delay = 1000;
-int delay = 1000;
-int loss = 0;
-int corrupt = 0;
-int corrupt2 = 0;
-
-#define CHANNEL_BUSY 1
-#define CHANNEL_IDLE 0
-
-#define LOCAL_PORT1 10000
-#define LOCAL_PORT2 10001
-
-pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER;
-pthread_cond_t buffer_cond = PTHREAD_COND_INITIALIZER; 
-queue* buffer;
-
-struct sockaddr_in local_addr1, remote_addr1;
-struct sockaddr_in local_addr2, remote_addr2;
-
-int s1,s2;
-socklen_t sz;
-
-//1 if a message was received on this link
-int link_up1 = 0;
-int link_up2 = 0;
-
-void init_sockets()
-{
-  /*LOCAL ADDRESSES*/
-
-  memset((char *) &local_addr1, 0, sizeof(local_addr1));
-  local_addr1.sin_family = AF_INET;
-  local_addr1.sin_port = htons(LOCAL_PORT1);
-  local_addr1.sin_addr.s_addr = htonl(INADDR_ANY);
-
-  memset((char *) &local_addr2, 0, sizeof(local_addr2));
-  local_addr2.sin_family = AF_INET;
-  local_addr2.sin_port = htons(LOCAL_PORT2);
-  local_addr2.sin_addr.s_addr = htonl(INADDR_ANY);
-
-  if ((s1=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1){
-    perror("Error creating socket");
-    exit(1);
-  }
-
-  //now bind
-  if (bind(s1, (struct sockaddr*)&local_addr1, sizeof(local_addr1))==-1){
-    perror("Failed to bind s1");
-    exit(1);
-  }
-
-  if ((s2=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1){
-    perror("Error creating socket 2");
-    exit(1);
-  }
-
-  if (bind(s2, (struct sockaddr*)&local_addr2, sizeof(local_addr2))==-1){
-    perror("Failed to bind s2");
-    exit(1);
-  }
-}
-
-int send_message1(const msg* m){
-  if (!link_up1){
-    printf("Trying to send a message but remote peer is not connected on my port %d\n",LOCAL_PORT1);
-  }
-  return sendto(s1, m, sizeof(msg), 0,(struct sockaddr*) &remote_addr1, sizeof(remote_addr1));
-}
-
-msg* receive_message1(){
-  msg* ret;
-
- a:
-  ret = (msg*)malloc(sizeof(msg));
-  
-  if (!link_up1){
-    sz = sizeof(remote_addr1);
-    if (recvfrom(s1, ret, sizeof(msg), 0, (struct sockaddr*)&remote_addr1, &sz)==-1){
-      free(ret);
-      return NULL;
-    }
-    
-    link_up1 = 1;
-
-#if DEBUG
-    printf("Link 1 is up, remote addr is %s port %d\n",inet_ntoa(remote_addr1.sin_addr),ntohs(remote_addr1.sin_port));
-#endif
-
-    free(ret);
-    goto a;
-  }
-  else if (recvfrom(s1, ret, sizeof(msg), 0, NULL,NULL)==-1){
-    free(ret);
-    return NULL;
-  }
-  return ret;
-}
-
-int send_message2(const msg* m){
-  if (!link_up2){
-    printf("Trying to send a message but remote peer is not connected on my port %d\n",LOCAL_PORT2);
-  }
-  return sendto(s2, m, sizeof(msg), 0,(struct sockaddr*) &remote_addr2, sizeof(remote_addr2));
-}
-
-msg* receive_message2(){
-  msg* ret;
-
- a:
-  ret = (msg*)malloc(sizeof(msg));
-  if (!link_up2){
-    sz = sizeof(remote_addr2);
-    if (recvfrom(s2, ret, sizeof(msg), 0, (struct sockaddr*)&remote_addr2, &sz)==-1){
-      free(ret);
-      return NULL;
-    }
-    link_up2 = 1;
-
-#if DEBUG
-    printf("Link 2 is up, remote addr is %s port %d\n",inet_ntoa(remote_addr2.sin_addr),ntohs(remote_addr2.sin_port));
-#endif
-
-    free(ret);
-    goto a;
-  }
-  else
-  if (recvfrom(s2, ret, sizeof(msg), 0, NULL, NULL)==-1){
-    free(ret);
-    return NULL;
-  }
-
-  return ret;
-}
-
-unsigned long long now(){ 
-  struct timeval b;
-  gettimeofday(&b,NULL);
-  return (unsigned long long)b.tv_sec*1000000+b.tv_usec;
-}
-
-void* link_scheduler(void *argument)
-{
-  msg_in_flight* mif;
-  queue* in_flight = create_queue();
-  long long idle_time = 0;
-  long long crt_time,wait_time_idle,wait_time_send;
-  int stuff;
-
-  while (1){
-    crt_time = now();
-
-#if DEBUG
-    printf("In flight size %d at %lld\n",in_flight->size, crt_time);
-#endif
-
-    while (in_flight->size>0){
-      msg_in_flight* last = (msg_in_flight*)in_flight->last->crt;
-      if (crt_time<last->finish_time){
-	break;
-      }
-
-      //else send first packet on the wire
-      mif = (msg_in_flight*)dequeue(in_flight);
-      if (!mif){
-	printf("Error in deque: expecting non null msg!\n");
-	exit(1);
-      }
-      if (send_message2(mif->m)<=0)
-	perror("SNDMSG2");
-      
-#if DEBUG
-      printf("Sending message\n");
-#endif
-
-      free(mif->m);
-      free(mif);
-      mif = NULL;
-    }
-
-    pthread_mutex_lock( &buffer_lock );
-    stuff = buffer->size>0;
-    pthread_mutex_unlock( &buffer_lock );          
-
-#if DEBUG
-    printf("Stuff is %d\n",stuff);
-#endif
-
-    wait_time_send = wait_time_idle = 0;
-
-    //crt_time = now();
-    
-    //now see if we can put stuff in flight
-    if (stuff && crt_time>=idle_time){
-      idle_time = crt_time + serialization_delay;
-      
-      mif = (msg_in_flight*)malloc(sizeof(msg_in_flight));
-      assert(mif);
-      
-      pthread_mutex_lock( &buffer_lock );
-      mif->m = (msg*)dequeue(buffer);
-      pthread_mutex_unlock( &buffer_lock );          
-      
-      assert(mif->m);
-      mif->finish_time = crt_time + serialization_delay + delay;
-      
-      //send message here from buffer to link
-      enqueue(in_flight,mif);
-
-#if DEBUG
-      printf("Enquing message\n");
-#endif
-    }
-    wait_time_idle = idle_time - crt_time;
-    
-    if (in_flight->size>0){
-      msg_in_flight* last = (msg_in_flight*)in_flight->last->crt;
-      wait_time_send = last->finish_time-crt_time;
-    }
-
-    if (wait_time_idle>0 || wait_time_send>0){
-      long long wait_time = 0;
-      if (wait_time_idle>0) wait_time = wait_time_idle;
-      if (wait_time_send>0&&wait_time_send<wait_time)
-	wait_time = wait_time_send;
-
-      //printf("Sleeping %lld\n", wait_time);
-      usleep(wait_time);
-    }
-    else {
-	pthread_mutex_lock( &buffer_lock );
-	assert(!buffer->size);
-#if DEBUG
-	printf("Waiting on cond\n");
-#endif
-	pthread_cond_wait(&buffer_cond,&buffer_lock);
-	pthread_mutex_unlock( &buffer_lock );          
-    }
-  }
- 
-  return NULL;
-}
-
-void* run_forwarding(void* param){
-  msg *m;
-
-  while (1){
-    int overflow;
-    m = receive_message1();
-    if (m==NULL){
-      perror("Read error");
-      exit(1);
-    }
-    
-    //check queue space
-    pthread_mutex_lock( &buffer_lock );
-    overflow = buffer->size>=BUFFER_SIZE;
-    pthread_mutex_unlock( &buffer_lock );    
-
-    if (overflow || (rand()%100)<loss) {
-      //just drop message
-      free(m);
-      printf("Dropped packet\n");
-    } 
-    else {
-      if (rand()%100 < corrupt){
-	//flip a random bit in a randomly chosen byte of the payload
-	int random_byte = rand() % m->len;
-	int random_bit = rand();
-	m->payload[random_byte] ^= 1 << (random_bit % 8);
-	if (corrupt2)
-		m->payload[random_byte] ^= 1 << (random_bit % 8);
-      }
-      //printf("Enqueue 1.");
-      pthread_mutex_lock( &buffer_lock );
-      enqueue(buffer,m);
-      pthread_cond_signal(&buffer_cond);
-      pthread_mutex_unlock(&buffer_lock);      
-      //printf("Done!\n");
-    }
-  }
-}
-
-void* run_reverse_forwarding(void* param){
-  msg *m;
-
-  while (1){
-    m = receive_message2();
-    if (m==NULL){
-      perror("Read error");
-      exit(1);
-    }
-    
-    send_message1(m);
-    free(m);
-  }
-}
-
-#define SPEED 1
-#define DELAY 2
-#define LOSS 3
-#define CORRUPT 4
-#define CORRUPT2 5
-
-int split_param(char* p,int * type, double* value){
-  char c[100];
-  int crt = 0, t=1;
-  
-  for (;*p!=0;p++){
-    if (t && *p=='='){
-      t = 0;
-      c[crt] = 0;
-      crt = 0;
-      
-      if (!strcasecmp(c,"speed"))
-	*type = SPEED;
-      else if (!strcasecmp(c,"delay"))
-	*type = DELAY;
-      else if (!strcasecmp(c,"loss"))
-	*type = LOSS;
-      else if (!strcasecmp(c,"corrupt"))
-	*type = CORRUPT;
-      else if (!strcasecmp(c,"corrupt2"))
-	*type = CORRUPT2;
-      else {
-	printf ("Unknown parameter %s\n",c);
-	return -1;
-      }
-    }
-    else c[crt++] = *p;
-  }
-  *value = atof(c);
-  return 0;
-}
-
-int guess_hz(){
-  long long a,b,diff = 0;
-  int i;
-  
-  for (i=0;i<100;i++){
-    a = now();
-    //0.1ms
-    usleep(100);
-    b = now();
-    diff += (b-a);
-  }
-
-  int error = (int)(diff/i-100);
-  printf("Average error  100 was %d\n",error);
-
-  diff = 0;
-  for (i=0;i<100;i++){
-    a = now();
-    //0.1ms
-    usleep(1000);
-    b = now();
-    diff += (b-a);
-  }
-
-  error = (int)(diff/i-1000);
-  printf("Average error 1000 was %d\n",error);
-  return  error;
-}
-
-int main(int argc, char** argv){
-  pthread_t link_thread, fw_thread;
-  int i;
-  for (i=1;i<argc;i++){
-    int type;
-    double value;
-    if (split_param(argv[i],&type,&value)<0){
-      printf("Usage %s speed=[speed in mb/s] delay=[delay in ms] loss=[percent of packets] corrupt=[percent of packets]\n",argv[0]);
-      return -1;
-    }
-    
-    switch(type){
-    case SPEED:
-      printf("Setting speed to %f Mb/s\n",value);
-      serialization_delay = 2*11200/value;break;
-    case DELAY:
-      printf("Setting delay %f to ms\n",value);
-      delay = value*1000; break;
-    case LOSS:
-      printf("Setting loss rate to %f%%\n",value);      
-      loss = value; break;
-    case CORRUPT:
-      printf("Setting corruption rate to %f%%\n",value);      
-      corrupt = value; break;
-
-    case CORRUPT2:
-      printf("Setting double corruption to %f\n", value);      
-      corrupt2 = value; break;
-    }
-  }
-
-#if DEBUG
-  guess_hz();
-#endif
-
-  init_sockets();
-  srand(time(NULL));
-  buffer = create_queue();
-  assert(!pthread_create(&link_thread, NULL, link_scheduler, NULL));
-  assert(!pthread_create(&fw_thread, NULL, run_forwarding, NULL));
-
-  run_reverse_forwarding(NULL);
-  return 0;
-}
diff --git a/lab3/link_emulator/link.h b/lab3/link_emulator/link.h
deleted file mode 100644
index 92b64db0b579489f735064e9be2428ef1e984846..0000000000000000000000000000000000000000
--- a/lab3/link_emulator/link.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef LINK
-#define LINK
-#include "lib.h"
-
-typedef struct {
-  msg* m;
-  unsigned long long finish_time;
-} msg_in_flight;
-
-#endif
diff --git a/lab3/link_emulator/queue.c b/lab3/link_emulator/queue.c
deleted file mode 100644
index daced7e77ff0a452492049c5164c98a38b9c9fe6..0000000000000000000000000000000000000000
--- a/lab3/link_emulator/queue.c
+++ /dev/null
@@ -1,61 +0,0 @@
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include "queue.h"
-
-void enqueue(queue* q,void* m){
-  if (q->first==NULL){
-    assert(q->last==NULL);
-
-    q->first = (queue_entry*)malloc(sizeof(queue_entry));
-    q->last = q->first;
-  }
-  else {
-    queue_entry * t = (queue_entry*)malloc(sizeof(queue_entry));
-    q->first->prev = t;
-    q->first = t;
-  }
-
-  q->first->prev = NULL;
-  q->first->crt = m;
-
-  q->size++;
-}
-
-void* dequeue(queue* q){
-  queue_entry  * t;
-  void* m;
-
-  if (q->first==NULL){
-    assert(q->size==0);
-    return NULL;
-  }
-  else if (q->first==q->last){
-    t = q->last;
-    assert(q->first->prev==NULL);
-
-    q->first = q->last = NULL;
-  }
-  else {
-    t = q->last;
-    q->last = q->last->prev;
-  }
-
-  m = t->crt;
-  free(t);
-  q->size--;
-
-  return m;
-}
-
-queue* create_queue(){
-  queue* q = (queue*)malloc(sizeof(queue));
-  q->first = NULL;
-  q->last = NULL;
-  q->size = 0;
-  return q;
-}
-
-void destroy_queue(queue* q){
-  assert(0);
-}
diff --git a/lab3/link_emulator/queue.h b/lab3/link_emulator/queue.h
deleted file mode 100644
index 9b6a45b0b5a77a3875e8523a52ad61bd48a69c5d..0000000000000000000000000000000000000000
--- a/lab3/link_emulator/queue.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef QUEUE
-#define QUEUE
-#include "lib.h"
-
-
-struct q {
-  void* crt;
-  struct q* prev;
-};
-
-typedef struct q queue_entry;
-
-typedef struct {
-  int size;
-  queue_entry* first;
-  queue_entry* last;
-} queue;
-
-void enqueue(queue* q,void* m);
-void* dequeue(queue* q);
-
-queue* create_queue();
-void destroy_queue(queue* q);
-
-#endif
diff --git a/lab3/recv.c b/lab3/recv.c
index 0372ee45ca230947628dee115ce8f1b4eb29aece..84f3e99bfe2aa8d2f7399955da3d1cbb810a49d9 100644
--- a/lab3/recv.c
+++ b/lab3/recv.c
@@ -6,7 +6,14 @@
 #include <arpa/inet.h>
 #include "common.h"
 #include "link_emulator/lib.h"
-
+#include "include/utils.h"
+
+/**
+ * You can change these to communicate with another colleague.
+ * There are several factors that could stop this from working over the
+ * internet, but if you're on the same network it should work.
+ * Just fill in their IP here and make sure that you use the same port.
+ */
 #define HOST "127.0.0.1"
 #define PORT 10001
 
@@ -19,10 +26,7 @@ int main(int argc,char** argv) {
 
 	/* Receive the frame from the link */
 	int len = link_recv(&t, sizeof(struct l3_msg));
-	if (len < 0){
-		perror("Receive message");
-		return -1;
-	}
+	DIE(len < 0, "Receive message");
 
 	/* We have to convert it to host order */
 	uint32_t recv_sum = ntohl(t.hdr.sum);
diff --git a/lab3/run_experiment.sh b/lab3/run_experiment.sh
deleted file mode 100755
index 73813130efa7cd0bc0449473ba287de6c007ad3a..0000000000000000000000000000000000000000
--- a/lab3/run_experiment.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-
-SPEED=1
-DELAY=1
-LOSS=0
-# Adjust the corruption
-CORRUPT=80
-
-# Second bit corruption rate
-CORRUPT2=0
-
-{
-    pkill -9 link
-    pkill -9 recv
-    pkill -9 send
-} &> /dev/null
-
-./link_emulator/link speed=$SPEED delay=$DELAY loss=$LOSS corrupt=$CORRUPT corrupt2=$CORRUPT2 &
-sleep 1
-./recv &
-sleep 1
-
-./send
diff --git a/lab3/run_experiment.sh b/lab3/run_experiment.sh
new file mode 120000
index 0000000000000000000000000000000000000000..a204cff38ba9a5a5601351d3569f0c0bd8d05fde
--- /dev/null
+++ b/lab3/run_experiment.sh
@@ -0,0 +1 @@
+../common/run_experiment.sh
\ No newline at end of file
diff --git a/lab3/send.c b/lab3/send.c
index 0c6e8d463a2aacbaef6150243938e64c53da2c13..c9f4e5b203d9838cf1b06a7e09ff81187bded35a 100644
--- a/lab3/send.c
+++ b/lab3/send.c
@@ -8,6 +8,7 @@
 #include "common.h"
 #include "link_emulator/lib.h"
 #include <arpa/inet.h>
+#include "include/utils.h"
 
 #define HOST "127.0.0.1"
 #define PORT 10000