diff --git a/.gitmodules b/.gitmodules
index 36caa59f81ea0c42ec319b5bb06aea224797a278..ac0df914d8272fa5a5cc4f200efeff140449eb75 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -25,3 +25,6 @@
 [submodule "fmt"]
     path = externals/fmt
     url = https://github.com/fmtlib/fmt.git
+[submodule "externals/enet"]
+    path = externals/enet
+    url = https://github.com/lsalzman/enet
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 02e02350c814ff4671ebb21abe1b4511c00bbcb9..cc47166fc2c1987fabcb0bb390156a8bbdef3cb1 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -48,3 +48,7 @@ if (ARCHITECTURE_x86_64)
     target_include_directories(xbyak INTERFACE ./xbyak/xbyak)
     target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
 endif()
+
+# ENet
+add_subdirectory(enet)
+target_include_directories(enet INTERFACE ./enet/include)
diff --git a/externals/enet b/externals/enet
new file mode 160000
index 0000000000000000000000000000000000000000..9d9ba122d4818f7ae1aef2197933ac696edb2331
--- /dev/null
+++ b/externals/enet
@@ -0,0 +1 @@
+Subproject commit 9d9ba122d4818f7ae1aef2197933ac696edb2331
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a45439481c828da106938d3c42a41efa1f4f95cc..655bd83aaf6383775cee97c2a8d5714909f2d19e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(common)
 add_subdirectory(core)
 add_subdirectory(video_core)
 add_subdirectory(audio_core)
+add_subdirectory(network)
 add_subdirectory(input_common)
 add_subdirectory(tests)
 if (ENABLE_SDL2)
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 4841cbf05eab48b766b9ed56067d6372bb1e8a76..9572d3e2888354ad129330d39dae67ae5a36dca2 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -91,7 +91,7 @@ if (APPLE)
 else()
     add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
 endif()
-target_link_libraries(citra-qt PRIVATE audio_core common core input_common video_core)
+target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
 target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets)
 target_link_libraries(citra-qt PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
 
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index a8a4aed8b35046b5c9c47c9166d091cc8af422d3..30554890f2cfa4cced091ded0f31ac87665456c2 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -17,6 +17,7 @@
 #include "core/settings.h"
 #include "input_common/keyboard.h"
 #include "input_common/main.h"
+#include "network/network.h"
 
 EmuThread::EmuThread(GRenderWindow* render_window)
     : exec_step(false), running(false), stop_run(false), render_window(render_window) {}
@@ -110,10 +111,12 @@ GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread)
     setWindowTitle(QString::fromStdString(window_title));
 
     InputCommon::Init();
+    Network::Init();
 }
 
 GRenderWindow::~GRenderWindow() {
     InputCommon::Shutdown();
+    Network::Shutdown();
 }
 
 void GRenderWindow::moveContext() {
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 42f6a99188739b08a7d59f5843fed50c10c50d52..0e4b85a7633eec6089f221e76e616e4a9621f9b8 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -72,6 +72,7 @@ namespace Log {
     SUB(Audio, DSP)                                                                                \
     SUB(Audio, Sink)                                                                               \
     CLS(Input)                                                                                     \
+    CLS(Network)                                                                                   \
     CLS(Loader)
 
 // GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 1b905f66c50cc5e2ced3984a69596fac26f1ef60..8f13b80b3e85520558987a42963a67194398ebbe 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -90,6 +90,7 @@ enum class Class : ClassType {
     Audio_Sink,        ///< Emulator audio output backend
     Loader,            ///< ROM loader
     Input,             ///< Input emulation
+    Network,           ///< Network emulation
     Count              ///< Total number of logging classes
 };
 
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..aeabe430eabc444b36a8681171505ca8c17a70eb
--- /dev/null
+++ b/src/network/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SRCS
+            network.cpp
+            room.cpp
+            room_member.cpp
+            )
+
+set(HEADERS
+            network.h
+            room.h
+            room_member.h
+            )
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(network STATIC ${SRCS} ${HEADERS})
+target_link_libraries(network PRIVATE common enet)
diff --git a/src/network/network.cpp b/src/network/network.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51b5d6a9fab7eb336bdeba881ae5d7d62125957b
--- /dev/null
+++ b/src/network/network.cpp
@@ -0,0 +1,50 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "enet/enet.h"
+#include "network/network.h"
+
+namespace Network {
+
+static std::shared_ptr<RoomMember> g_room_member; ///< RoomMember (Client) for network games
+static std::shared_ptr<Room> g_room;              ///< Room (Server) for network games
+// TODO(B3N30): Put these globals into a networking class
+
+bool Init() {
+    if (enet_initialize() != 0) {
+        LOG_ERROR(Network, "Error initalizing ENet");
+        return false;
+    }
+    g_room = std::make_shared<Room>();
+    g_room_member = std::make_shared<RoomMember>();
+    LOG_DEBUG(Network, "initialized OK");
+    return true;
+}
+
+std::weak_ptr<Room> GetRoom() {
+    return g_room;
+}
+
+std::weak_ptr<RoomMember> GetRoomMember() {
+    return g_room_member;
+}
+
+void Shutdown() {
+    if (g_room_member) {
+        if (g_room_member->IsConnected())
+            g_room_member->Leave();
+        g_room_member.reset();
+    }
+    if (g_room) {
+        if (g_room->GetState() == Room::State::Open)
+            g_room->Destroy();
+        g_room.reset();
+    }
+    enet_deinitialize();
+    LOG_DEBUG(Network, "shutdown OK");
+}
+
+} // namespace Network
diff --git a/src/network/network.h b/src/network/network.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d002d693f656bd692e82b1dcbb3a1889f23a9dc
--- /dev/null
+++ b/src/network/network.h
@@ -0,0 +1,25 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "network/room.h"
+#include "network/room_member.h"
+
+namespace Network {
+
+/// Initializes and registers the network device, the room, and the room member.
+bool Init();
+
+/// Returns a pointer to the room handle
+std::weak_ptr<Room> GetRoom();
+
+/// Returns a pointer to the room member handle
+std::weak_ptr<RoomMember> GetRoomMember();
+
+/// Unregisters the network device, the room, and the room member and shut them down.
+void Shutdown();
+
+} // namespace Network
diff --git a/src/network/room.cpp b/src/network/room.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..48de2f5cb8f9da36e60f848a7edb3dccc020b982
--- /dev/null
+++ b/src/network/room.cpp
@@ -0,0 +1,60 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "enet/enet.h"
+#include "network/room.h"
+
+namespace Network {
+
+/// Maximum number of concurrent connections allowed to this room.
+static constexpr u32 MaxConcurrentConnections = 10;
+
+class Room::RoomImpl {
+public:
+    ENetHost* server = nullptr; ///< Network interface.
+
+    std::atomic<State> state{State::Closed}; ///< Current state of the room.
+    RoomInformation room_information;        ///< Information about this room.
+};
+
+Room::Room() : room_impl{std::make_unique<RoomImpl>()} {}
+
+Room::~Room() = default;
+
+void Room::Create(const std::string& name, const std::string& server_address, u16 server_port) {
+    ENetAddress address;
+    address.host = ENET_HOST_ANY;
+    enet_address_set_host(&address, server_address.c_str());
+    address.port = server_port;
+
+    room_impl->server = enet_host_create(&address, MaxConcurrentConnections, NumChannels, 0, 0);
+    // TODO(B3N30): Allow specifying the maximum number of concurrent connections.
+    room_impl->state = State::Open;
+
+    room_impl->room_information.name = name;
+    room_impl->room_information.member_slots = MaxConcurrentConnections;
+
+    // TODO(B3N30): Start the receiving thread
+}
+
+Room::State Room::GetState() const {
+    return room_impl->state;
+}
+
+const RoomInformation& Room::GetRoomInformation() const {
+    return room_impl->room_information;
+}
+
+void Room::Destroy() {
+    room_impl->state = State::Closed;
+    // TODO(B3n30): Join the receiving thread
+
+    if (room_impl->server) {
+        enet_host_destroy(room_impl->server);
+    }
+    room_impl->room_information = {};
+    room_impl->server = nullptr;
+}
+
+} // namespace Network
diff --git a/src/network/room.h b/src/network/room.h
new file mode 100644
index 0000000000000000000000000000000000000000..70c64d5f17f4c3c1681ba28c58bf3308f99d4d4b
--- /dev/null
+++ b/src/network/room.h
@@ -0,0 +1,60 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+
+namespace Network {
+
+constexpr u16 DefaultRoomPort = 1234;
+constexpr size_t NumChannels = 1; // Number of channels used for the connection
+
+struct RoomInformation {
+    std::string name; ///< Name of the server
+    u32 member_slots; ///< Maximum number of members in this room
+};
+
+/// This is what a server [person creating a server] would use.
+class Room final {
+public:
+    enum class State : u8 {
+        Open,   ///< The room is open and ready to accept connections.
+        Closed, ///< The room is not opened and can not accept connections.
+    };
+
+    Room();
+    ~Room();
+
+    /**
+     * Gets the current state of the room.
+     */
+    State GetState() const;
+
+    /**
+     * Gets the room information of the room.
+     */
+    const RoomInformation& GetRoomInformation() const;
+
+    /**
+     * Creates the socket for this room. Will bind to default address if
+     * server is empty string.
+     */
+    void Create(const std::string& name, const std::string& server = "",
+                u16 server_port = DefaultRoomPort);
+
+    /**
+     * Destroys the socket
+     */
+    void Destroy();
+
+private:
+    class RoomImpl;
+    std::unique_ptr<RoomImpl> room_impl;
+};
+
+} // namespace Network
diff --git a/src/network/room_member.cpp b/src/network/room_member.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c87f009f432f47c7461da774103bf2bdef10cefc
--- /dev/null
+++ b/src/network/room_member.cpp
@@ -0,0 +1,74 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "enet/enet.h"
+#include "network/room_member.h"
+
+namespace Network {
+
+constexpr u32 ConnectionTimeoutMs = 5000;
+
+class RoomMember::RoomMemberImpl {
+public:
+    ENetHost* client = nullptr; ///< ENet network interface.
+    ENetPeer* server = nullptr; ///< The server peer the client is connected to
+
+    std::atomic<State> state{State::Idle}; ///< Current state of the RoomMember.
+
+    std::string nickname; ///< The nickname of this member.
+};
+
+RoomMember::RoomMember() : room_member_impl{std::make_unique<RoomMemberImpl>()} {
+    room_member_impl->client = enet_host_create(nullptr, 1, NumChannels, 0, 0);
+    ASSERT_MSG(room_member_impl->client != nullptr, "Could not create client");
+}
+
+RoomMember::~RoomMember() {
+    ASSERT_MSG(!IsConnected(), "RoomMember is being destroyed while connected");
+    enet_host_destroy(room_member_impl->client);
+}
+
+RoomMember::State RoomMember::GetState() const {
+    return room_member_impl->state;
+}
+
+void RoomMember::Join(const std::string& nick, const char* server_addr, u16 server_port,
+                      u16 client_port) {
+    ENetAddress address{};
+    enet_address_set_host(&address, server_addr);
+    address.port = server_port;
+
+    room_member_impl->server =
+        enet_host_connect(room_member_impl->client, &address, NumChannels, 0);
+
+    if (!room_member_impl->server) {
+        room_member_impl->state = State::Error;
+        return;
+    }
+
+    ENetEvent event{};
+    int net = enet_host_service(room_member_impl->client, &event, ConnectionTimeoutMs);
+    if (net > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
+        room_member_impl->nickname = nick;
+        room_member_impl->state = State::Joining;
+        // TODO(B3N30): Send a join request with the nickname to the server
+        // TODO(B3N30): Start the receive thread
+    } else {
+        room_member_impl->state = State::CouldNotConnect;
+    }
+}
+
+bool RoomMember::IsConnected() const {
+    return room_member_impl->state == State::Joining || room_member_impl->state == State::Joined;
+}
+
+void RoomMember::Leave() {
+    enet_peer_disconnect(room_member_impl->server, 0);
+    room_member_impl->state = State::Idle;
+    // TODO(B3N30): Close the receive thread
+    enet_peer_reset(room_member_impl->server);
+}
+
+} // namespace Network
diff --git a/src/network/room_member.h b/src/network/room_member.h
new file mode 100644
index 0000000000000000000000000000000000000000..177622b690b1bb2754803e5ebcda5f75aa54c5e7
--- /dev/null
+++ b/src/network/room_member.h
@@ -0,0 +1,65 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+#include "network/room.h"
+
+namespace Network {
+
+/**
+ * This is what a client [person joining a server] would use.
+ * It also has to be used if you host a game yourself (You'd create both, a Room and a
+ * RoomMembership for yourself)
+ */
+class RoomMember final {
+public:
+    enum class State : u8 {
+        Idle,    ///< Default state
+        Error,   ///< Some error [permissions to network device missing or something]
+        Joining, ///< The client is attempting to join a room.
+        Joined,  ///< The client is connected to the room and is ready to send/receive packets.
+        LostConnection, ///< Connection closed
+
+        // Reasons why connection was rejected
+        NameCollision,  ///< Somebody is already using this name
+        MacCollision,   ///< Somebody is already using that mac-address
+        CouldNotConnect ///< The room is not responding to a connection attempt
+    };
+
+    RoomMember();
+    ~RoomMember();
+
+    /**
+     * Returns the status of our connection to the room.
+     */
+    State GetState() const;
+
+    /**
+     * Returns whether we're connected to a server or not.
+     */
+    bool IsConnected() const;
+
+    /**
+     * Attempts to join a room at the specified address and port, using the specified nickname.
+     * This may fail if the username is already taken.
+     */
+    void Join(const std::string& nickname, const char* server_addr = "127.0.0.1",
+              const u16 serverPort = DefaultRoomPort, const u16 clientPort = 0);
+
+    /**
+     * Leaves the current room.
+     */
+    void Leave();
+
+private:
+    class RoomMemberImpl;
+    std::unique_ptr<RoomMemberImpl> room_member_impl;
+};
+
+} // namespace Network