aboutsummaryrefslogtreecommitdiff
path: root/shard/lib/net
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2018-10-11 14:22:00 +0200
committerAlex Auvolat <alex@adnab.me>2018-10-11 14:22:00 +0200
commiteb8c949551ffb8b3600357d7ff2bebe750af96e5 (patch)
tree36a0b46219ae595313b569c5885c470a0ddaca0e /shard/lib/net
parent7b6042205e7c6135fae4e0d21dbf7a5975e8491b (diff)
downloadshard-eb8c949551ffb8b3600357d7ff2bebe750af96e5.tar.gz
shard-eb8c949551ffb8b3600357d7ff2bebe750af96e5.zip
Address, broadcast group management
Diffstat (limited to 'shard/lib/net')
-rw-r--r--shard/lib/net/addr.ex27
-rw-r--r--shard/lib/net/group.ex98
2 files changed, 125 insertions, 0 deletions
diff --git a/shard/lib/net/addr.ex b/shard/lib/net/addr.ex
new file mode 100644
index 0000000..645e109
--- /dev/null
+++ b/shard/lib/net/addr.ex
@@ -0,0 +1,27 @@
+defmodule SNet.Addr do
+
+ def get_if_inet4 do
+ {:ok, ifs} = :inet.getifaddrs
+ for {_, opts} <- ifs,
+ {:addr, addr} <- opts,
+ tuple_size(addr) == 4,
+ addr != {127,0,0,1}
+ do
+ addr
+ end
+ end
+
+ def get_pub_inet4 do
+ Application.ensure_all_started(:inets)
+ Application.ensure_all_started(:ssl)
+ {:ok, {_, _, body}} = :httpc.request('http://api.ipify.org')
+ {:ok, addr} = :inet.parse_address body
+ addr
+ end
+
+ def get_all_inet4 do
+ addrset = for x <- get_if_inet4(), into: %MapSet{}, do: x
+ addrset = MapSet.put(addrset, get_pub_inet4())
+ MapSet.to_list addrset
+ end
+end
diff --git a/shard/lib/net/group.ex b/shard/lib/net/group.ex
new file mode 100644
index 0000000..d2c2537
--- /dev/null
+++ b/shard/lib/net/group.ex
@@ -0,0 +1,98 @@
+defprotocol SNet.Group do
+ @moduledoc"""
+ A group is a specification of a bunch of peers we want and accept to talk to
+ about some things. It supports a number of abstract operations for finding peers,
+ broadcasting/gossiping, authenticating, etc.
+ """
+
+ @doc"""
+ Find new peers for this group, open connections and notify us when connections are open.
+
+ Launches background processes if necessary, returns immediately.
+ """
+ def init_lookup(group, notify_to)
+
+ @doc"""
+ Get all currently open connections to peers in this group.
+ """
+ def get_connections(group)
+
+ @doc"""
+ Broadcast a message to peers of the group.
+ Will send to at most nmax peers, so this is a good primitive for gossip.
+ """
+ def broadcast(group, msg, nmax \\ 10)
+
+ @doc"""
+ Check if a peer is allowed to participate in this group.
+ """
+ def in_group?(group, conn_pid, auth)
+end
+
+defmodule SNet.PubShardGroup do
+ defstruct [:id]
+
+ defimpl SNet.Group do
+ def init_lookup(%SNet.PubShardGroup{id: id}, _notify_to) do
+ # For now: ask all currently connected peers and connect to new peers we know of
+ spawn fn ->
+ for {_, pid, _} <- Shard.Manager.list_connections do
+ GenServer.cast(pid, {:send_msg, {:interested, [id]}})
+ end
+ for peer_info <- Shard.Manager.get_shard_peers id do
+ if Shard.Manager.get_connections_to peer_info == [] do
+ Shard.Manager.add_peer(peer_info) # TODO callback when connected
+ end
+ end
+ end
+ # TODO: use a DHT to find peers
+ end
+
+ def get_connections(%SNet.PubShardGroup{id: id}) do
+ for peer_info <- Shard.Manager.get_shard_peers(id),
+ [{pid, _auth}|_] = Shard.Manager.get_connections_to(peer_info),
+ do: pid
+ end
+
+ def broadcast(group, msg, nmax) do
+ %SNet.PubShardGroup{id: id} = group
+ nsent = get_connections(group)
+ |> Enum.shuffle
+ |> Enum.take(nmax)
+ |> Enum.map(&(GenServer.cast(&1, {:send_msg, msg})))
+ |> Enum.count
+ if nmax - nsent > 0 do
+ Shard.Manager.get_shard_peers(id)
+ |> Enum.filter(&(Shard.Manager.get_connections_to(&1) == []))
+ |> Enum.shuffle
+ |> Enum.take(nmax - nsent)
+ |> Enum.map(&(Shard.Manager.send(&1, msg)))
+ end
+ end
+
+ def in_group?(%SNet.PubShardGroup{id: _id}, _peer_pid, _auth) do
+ true # No access control
+ end
+ end
+end
+
+defmodule SNet.PrivGroup do
+ defstruct [:pk_list]
+
+ defimpl SNet.Group do
+ def init_lookup(%SNet.PubShardGroup{id: id}, notify_to) do
+ # TODO
+ end
+
+ def get_connections(%SNet.PubShardGroup{id: id}) do
+ # TODO
+ end
+
+ def broadcast(group, msg, nmax) do
+ end
+
+ def in_group?(%SNet.PubShardGroup{id: _id}, peer_pid, auth) do
+ # TODO
+ end
+ end
+end