Adding inet_count/2 and inet_window/3 functions

Similar to existing process functions, but for open TCP, UDP, and SCTP
connections.
diff --git a/src/recon.erl b/src/recon.erl
index a7d45ab..796bf22 100644
--- a/src/recon.erl
+++ b/src/recon.erl
@@ -6,7 +6,8 @@
 -export([get_state/1]).
 -export([remote_load/1, remote_load/2,
          source/1]).
--export([tcp/0, udp/0, sctp/0, files/0, port_types/0]).
+-export([tcp/0, udp/0, sctp/0, files/0, port_types/0,
+         inet_count/2, inet_window/3]).
 -export([rpc/1, rpc/2, rpc/3,
          named_rpc/1, named_rpc/2, named_rpc/3]).
 
@@ -18,8 +19,11 @@
                        [Name::atom()
                        |{current_function, mfa()}
                        |{initial_call, mfa()}, ...]}.
+-type inet_attrs() :: {port(),
+                       Attr::_,
+                       [{atom(), term()}]}.
 
--export_type([proc_attrs/0]).
+-export_type([proc_attrs/0, inet_attrs/0]).
 %%%%%%%%%%%%%%%%%%
 %%% PUBLIC API %%%
 %%%%%%%%%%%%%%%%%%
@@ -303,6 +307,54 @@
         recon_lib:count([Name || {_, Name} <- recon_lib:port_list(name)])
     ).
 
+%% @doc Fetches a given attribute from all inet ports (TCP, UDP, SCTP)
+%% and returns the biggest `Num' consumers.
+%%
+%% The values to be used can be the number of octets (bytes) sent, received,
+%% or both (`send_oct', `recv_oct', `oct', respectively), or the number
+%% of packets sent, received, or both (`send_cnt', `recv_cnt', `cnt',
+%% respectively). Individual absolute values for each metric will be returned
+%% in the 3rd position of the resulting tuple.
+%%
+%% @todo Implement this function so it only stores `Num' entries in
+%% memory at any given time, instead of as many as there are
+%% processes.
+-spec inet_count(AttributeName, Num) -> [inet_attrs()] when
+      AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct'
+                     | 'cnt' | 'oct',
+      Num :: non_neg_integer().
+inet_count(Attr, Num) ->
+    lists:sublist(lists:usort(
+        fun({_,A,_},{_,B,_}) -> A > B end,
+        recon_lib:inet_attrs(Attr)
+    ), Num).
+
+%% @doc Fetches a given attribute from all inet ports (TCP, UDP, SCTP)
+%% and returns the biggest entries, over a sliding time window.
+%%
+%% Warning: this function depends on data gathered at two snapshots, and then
+%% building a dictionary with entries to differentiate them. This can take a
+%% heavy toll on memory when you have many dozens of thousands of ports open.
+%%
+%% The values to be used can be the number of octets (bytes) sent, received,
+%% or both (`send_oct', `recv_oct', `oct', respectively), or the number
+%% of packets sent, received, or both (`send_cnt', `recv_cnt', `cnt',
+%% respectively). Individual absolute values for each metric will be returned
+%% in the 3rd position of the resulting tuple.
+-spec inet_window(AttributeName, Num, Milliseconds) -> [inet_attrs()] when
+      AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct'
+                     | 'cnt' | 'oct',
+      Num :: non_neg_integer(),
+      Milliseconds :: pos_integer().
+inet_window(Attr, Num, Time) when is_atom(Attr) ->
+    Sample = fun() -> recon_lib:inet_attrs(Attr) end,
+    {First,Last} = recon_lib:sample(Time, Sample),
+    lists:sublist(lists:usort(
+        fun({_,A,_},{_,B,_}) -> A > B end,
+        recon_lib:sliding_window(First, Last)
+    ), Num).
+
+
 %%% RPC Utils %%%
 
 %% @doc Shorthand for `rpc([node()|nodes()], Fun)'.
diff --git a/src/recon_lib.erl b/src/recon_lib.erl
index 9b25762..394e7c6 100644
--- a/src/recon_lib.erl
+++ b/src/recon_lib.erl
@@ -2,10 +2,11 @@
 -export([sliding_window/2, sample/2, count/1,
          port_list/1, port_list/2,
          proc_attrs/1, proc_attrs/2,
+         inet_attrs/1, inet_attrs/2,
          triple_to_pid/3,
          time_map/5, time_fold/6]).
 
--type diff() :: [{Key::term(), Diff::number(), Other::term()}].
+-type diff() :: [recon:proc_attrs() | recon:inet_attrs()].
 
 %% @doc Compare two samples and return a list based on some key. The type mentioned
 %% for the structure is `diff()' (`{Key,Val,Other}'), which is compatible with
@@ -77,6 +78,35 @@
                            current_function, initial_call]),
     {Pid, Attr, [Name || is_atom(Name)]++[Init, Cur]}.
 
+%% @doc Returns the attributes ({@link recon:inet_attrs()}) of
+%% all inet ports (UDP, SCTP, TCP) of the node.
+-spec inet_attrs(term()) -> [recon:inet_attrs()].
+inet_attrs(AttrName) ->
+    Ports = [Port || Port <- erlang:ports(),
+                     {_, Name} <- [erlang:port_info(Port, name)],
+                     Name =:= "tcp_inet" orelse
+                     Name =:= "udp_inet" orelse
+                     Name =:= "sctp_inet"],
+    [Attrs || Port <- Ports,
+              Attrs <- [inet_attrs(AttrName, Port)]].
+
+%% @doc Returns the attributes required for a given inet port (UDP,
+%% SCTP, TCP). This form of attributes is standard for most comparison
+%% functions for processes in recon.
+-spec inet_attrs(AttributeName, port()) -> recon:inet_attrs() when
+      AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct'
+                     | 'cnt' | 'oct'.
+inet_attrs(Attr, Port) ->
+    Attrs = case Attr of
+        cnt -> [recv_cnt, send_cnt];
+        oct -> [recv_oct, send_oct];
+        _ -> [Attr]
+    end,
+    {ok, Props} = inet:getstat(Port, Attrs),
+    ValSum = lists:foldl(fun({_,X},Y) -> X+Y end, 0, Props),
+    {Port,ValSum,Props}.
+
+
 %% @doc Equivalent of `pid(X,Y,Z)' in the Erlang shell.
 -spec triple_to_pid(N,N,N) -> pid() when
     N :: non_neg_integer().
@@ -85,7 +115,7 @@
                        integer_to_list(Y) ++ "." ++
                        integer_to_list(Z) ++ ">").
 
-%% @doc Calls a given function every N milliseconds and supports
+%% @doc Calls a given function every `Interval' milliseconds and supports
 %% a map-like interface (each result is modified and returned)
 -spec time_map(N, Interval, Fun, State, MapFun) -> [term()] when
     N :: non_neg_integer(),
@@ -100,7 +130,7 @@
     timer:sleep(Interval),
     [MapFun(Res) | time_map(N-1,Interval,Fun,NewState,MapFun)].
 
-%% @doc Calls a given function every N milliseconds and supports
+%% @doc Calls a given function every `Interval' milliseconds and supports
 %% a fold-like interface (each result is modified and accumulated)
 -spec time_fold(N, Interval, Fun, State, FoldFun, Init) -> [term()] when
     N :: non_neg_integer(),