Add -o,--sort option to perform flows sorting by rate/bytes/pkts.
Add -d,--dir option to choose the direction to sort (default is dst).

is_visible is used only to show sorted flows to do not
show newer appeared flows with rate 0 at the head of list.

Signed-off-by: Vadim Kochan <[email protected]>
---
 flowtop.8 |  20 ++++++++++++
 flowtop.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 120 insertions(+), 9 deletions(-)

diff --git a/flowtop.8 b/flowtop.8
index 1367e6e..bd636d1 100644
--- a/flowtop.8
+++ b/flowtop.8
@@ -117,6 +117,26 @@ Also show source information of the flow, not only 
destination information.
 .SS -b, --bits
 Show flow rates in bits per second instead of bytes per second.
 .PP
+.SS -o, --sort <type> (default no sorting)
+Sort flows by type (see -d,--dir option):
+.in +4
+.sp
+r,rate - sort by rate
+.sp
+b,bytes - sort by number of bytes
+.sp
+p,pkts - sort by number of packets
+.in -4
+.PP
+.SS -d, --dir <dir> (default dst)
+Specify flows direction (sorting, etc):
+.in +4
+.sp
+s,src - use source direction
+.sp
+d,dst - use destination direction
+.in -4
+.PP
 .SS -u, --update
 The built-in database update mechanism will be invoked to get Maxmind's
 latest database. To configure search locations for databases, the file
diff --git a/flowtop.c b/flowtop.c
index 6496e44..c67b683 100644
--- a/flowtop.c
+++ b/flowtop.c
@@ -117,6 +117,13 @@ enum rate_units {
        RATE_BYTES
 };
 
+enum sort_type {
+       SORT_NONE,
+       SORT_RATE,
+       SORT_BYTES,
+       SORT_PKTS
+};
+
 static volatile bool is_flow_collecting;
 static volatile sig_atomic_t sigint = 0;
 static int what = INCLUDE_IPV4 | INCLUDE_IPV6 | INCLUDE_TCP;
@@ -128,8 +135,10 @@ static bool show_src = false;
 static bool resolve_dns = true;
 static bool resolve_geoip = true;
 static enum rate_units rate_type = RATE_BYTES;
+static enum flow_direction flow_dir = FLOW_DIR_DST;
+static enum sort_type sort_by = SORT_NONE;
 
-static const char *short_options = "vhTUsDIS46ut:nGb";
+static const char *short_options = "vhTUsDIS46ut:nGbo:d:";
 static const struct option long_options[] = {
        {"ipv4",        no_argument,            NULL, '4'},
        {"ipv6",        no_argument,            NULL, '6'},
@@ -142,6 +151,8 @@ static const struct option long_options[] = {
        {"no-geoip",    no_argument,            NULL, 'G'},
        {"show-src",    no_argument,            NULL, 's'},
        {"bits",        no_argument,            NULL, 'b'},
+       {"sort",        required_argument,      NULL, 'o'},
+       {"dir",         required_argument,      NULL, 'd'},
        {"update",      no_argument,            NULL, 'u'},
        {"interval",    required_argument,      NULL, 't'},
        {"version",     no_argument,            NULL, 'v'},
@@ -278,6 +289,13 @@ static void help(void)
             "  -G|--no-geoip          Don't perform GeoIP lookup\n"
             "  -s|--show-src          Also show source, not only dest\n"
             "  -b|--bits              Show rates in bits/s instead of 
bytes/s\n"
+            "  -o|--sort <type>       Sort flows by type (default no 
sorting):\n"
+            "                             r,rate - sort by rate\n"
+            "                             b,bytes - sort by number of bytes\n"
+            "                             p,pkts - sort by number of packets\n"
+            "  -d|--dir <dir>         Specify flows direction (default dst):\n"
+            "                             s,src - source direction\n"
+            "                             d,dst - destination direction\n"
             "  -u|--update            Update GeoIP databases\n"
             "  -t|--interval <time>   Refresh time in seconds (default 1s)\n"
             "  -v|--version           Print version and exit\n"
@@ -1126,7 +1144,8 @@ static void presenter_screen_update(WINDOW *screen, 
struct flow_list *fl,
                          "Is netfilter running?)");
 
        for (; n; n = rcu_dereference(n->next)) {
-               n->is_visible = false;
+               if (!n->is_visible)
+                       continue;
 
                if (presenter_flow_wrong_state(n))
                        continue;
@@ -1142,8 +1161,6 @@ static void presenter_screen_update(WINDOW *screen, 
struct flow_list *fl,
                        continue;
                }
 
-               n->is_visible = true;
-
                presenter_screen_do_line(screen, n, &line);
 
                line++;
@@ -1295,6 +1312,62 @@ static void conntrack_tstamp_enable(void)
        }
 }
 
+static void flow_list_sort(struct flow_list *fl, struct flow_entry *n)
+{
+       struct flow_entry *head;
+
+       if (sort_by == SORT_NONE)
+               return;
+
+       synchronize_rcu();
+       spinlock_lock(&flow_list.lock);
+
+       head = rcu_dereference(fl->head);
+       if (!head)
+               return;
+       if (!rcu_dereference(head->next))
+               return;
+
+       flow_list_remove_entry(fl, n);
+
+       for (; head; head = rcu_dereference(head->next)) {
+               if (sort_by == SORT_RATE) {
+                       if (flow_dir == FLOW_DIR_SRC) {
+                               if (n->rate_bytes_src > head->rate_bytes_src)
+                                       break;
+                       } else if (flow_dir == FLOW_DIR_DST) {
+                               if (n->rate_bytes_dst > head->rate_bytes_dst)
+                                       break;
+                       }
+               } else if (sort_by == SORT_BYTES) {
+                       if (flow_dir == FLOW_DIR_SRC) {
+                               if (n->bytes_src > head->bytes_src)
+                                       break;
+                       } else if (flow_dir == FLOW_DIR_DST) {
+                               if (n->bytes_dst > head->bytes_dst)
+                                       break;
+                       }
+               } else if (sort_by == SORT_PKTS) {
+                       if (flow_dir == FLOW_DIR_SRC) {
+                               if (n->pkts_src > head->pkts_src)
+                                       break;
+                       } else if (flow_dir == FLOW_DIR_DST) {
+                               if (n->pkts_dst > head->pkts_dst)
+                                       break;
+                       }
+               }
+       }
+
+       if (!head)
+               head = rcu_dereference(n->prev);
+       else
+               head = rcu_dereference(head->prev);
+
+       flow_list_insert_entry(fl, head, n);
+
+       spinlock_unlock(&flow_list.lock);
+}
+
 static int flow_update_cb(enum nf_conntrack_msg_type type,
                          struct nf_conntrack *ct, void *data __maybe_unused)
 {
@@ -1314,6 +1387,10 @@ static int flow_update_cb(enum nf_conntrack_msg_type 
type,
        flow_entry_update_time(n);
        flow_entry_from_ct(n, ct);
 
+       flow_list_sort(&flow_list, n);
+
+       n->is_visible = true;
+
        return NFCT_CB_CONTINUE;
 }
 
@@ -1322,12 +1399,8 @@ static void collector_refresh_flows(struct nfct_handle 
*handle)
        struct flow_entry *n;
 
        n = rcu_dereference(flow_list.head);
-       for (; n; n = rcu_dereference(n->next)) {
-               if (!n->is_visible)
-                       continue;
-
+       for (; n; n = rcu_dereference(n->next))
                nfct_query(handle, NFCT_Q_GET, n->ct);
-       }
 }
 
 static void collector_create_filter(struct nfct_handle *nfct)
@@ -1586,6 +1659,24 @@ int main(int argc, char **argv)
                case 'G':
                        resolve_geoip = false;
                        break;
+               case 'o':
+                       if (*optarg == 'r' || strcmp(optarg, "rate") == 0)
+                               sort_by = SORT_RATE;
+                       else if (*optarg == 'b' || strcmp(optarg, "bytes") == 0)
+                               sort_by = SORT_BYTES;
+                       else if (*optarg == 'p' || strcmp(optarg, "pkts") == 0)
+                               sort_by = SORT_PKTS;
+                       else
+                               help();
+                       break;
+               case 'd':
+                       if (*optarg == 's' || strcmp(optarg, "src") == 0)
+                               flow_dir = FLOW_DIR_SRC;
+                       else if (*optarg == 'd' || strcmp(optarg, "dst") == 0)
+                               flow_dir = FLOW_DIR_DST;
+                       else
+                               help();
+                       break;
                case 'h':
                        help();
                        break;
-- 
2.6.1

-- 
You received this message because you are subscribed to the Google Groups 
"netsniff-ng" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to