diff --git a/doc/development/format_of_snmp_json_db_file.txt b/doc/development/format_of_snmp_json_db_file.txt
new file mode 100644
index 0000000000000000000000000000000000000000..59bb845d76e114ecd68025d8bf9648a06156a55b
--- /dev/null
+++ b/doc/development/format_of_snmp_json_db_file.txt
@@ -0,0 +1,76 @@
+
+== format of statistics table 
+
+stored as JSON
+
+configured with key 'SNMP_TEMP_FILE' in ./flowspy/settings.py, e.g., with file name './snmp_temp_data'
+
+
+=== 1. measurement entries per time (per rule id) in general:
+
+a measurement value
+per rule,
+for each measurement time slot,
+
+e.g.,
+
+{
+ "_per_rule": {
+     "243": [
+              {"ts": "2023-09-28T13:00:02.107721", "value": {"packets": 0, "bytes": 0 },
+              {"ts": "2023-09-28T13:00:07.107721", "value": {"packets": 10, "bytes": 4545 } },
+              {"ts": "2023-09-28T13:00:12.107721", "value": 1,
+              {"ts": "2023-09-28T13:00:17.107721", "value": {"packets": 30, "bytes": 8539 } },
+              {"ts": "2023-09-28T13:00:22.107721", "value": {"packets": 80, "bytes": 18234 } },
+              {"ts": "2023-09-28T13:00:27.107721", "value": {"packets": 80, "bytes": 18234 } },
+              {"ts": "2023-09-28T13:00:32.107721", "value": {"packets": 80, "bytes": 18234 } },
+              {"ts": "2023-09-28T13:00:33.107721", "value": 0 },
+              {"ts": "2023-09-30T14:00:11.107721", "value": {"packets": 0, "bytes": 0 },
+              {"ts": "2023-09-30T14:00:12.107721", "value": {"packets": 15, "bytes": 3691 } },
+              {"ts": "2023-09-30T14:00:17.107721", "value": {"packets": 25, "bytes": 18539 } },
+              {"ts": "2023-09-30T14:00:22.107721", "value": {"packets": 120, "bytes": 28934 } },
+              {"ts": "2023-09-30T14:00:27.107721", "value": {"packets": 120, "bytes": 28934 } },
+              {"ts": "2023-09-30T14:00:32.107721", "value": {"packets": 120, "bytes": 28934 } },
+              {"ts": "2023-09-30T14:00:37.107721", "value": {"packets": 120, "bytes": 28934 } },
+              {"ts": "2023-09-30T14:00:42.107721", "value": {"packets": 120, "bytes": 28934 } },
+              {"ts": "2023-09-30T14:00:47.107721", "value": {"packets": 120, "bytes": 28934 } },
+              {"ts": "2023-09-28T14:00:49.107721", "value": 0 },
+            ],
+     ...
+  },
+}
+
+measurement value per time slot is either:
+
+zero measurement: = { bytes=>0, packets=>0, } : at start of measurement = activation of the rule on the router
+
+or
+
+real measurement (example): = { bytes=>1414, packets=>5, } : cumulative (=absolute values from start of measurement = from last activation of the rule) bytes/packet counters 
+
+or 
+
+missing_value: =1 : no measurements from none of the routers queried answered values for the specific rule in the specific scheduling time slot (while rule is activated)
+
+null_value: =0 : used when rule is deactivated = measurement should stop on the router
+
+when rule is re-activated again on the router after some duration of having been deactivated it is started again with a zero measurement in the table and absolute values for the future real measurement are starting from zero again
+
+
+=== 2. support of distinct values for matched and dropped bytes/packets per time (in case of rules with rate-limiting):
+
+for rules with rate-limiting now there are 2 distinct kinds of statistics, 
+one for matched bytes/packets and one for dropped bytes/packets
+(for rules with drop action there is essentially only one statistic,
+as matched and dropped values are always the same).
+
+for rate-limiting rule in the statistic entry per time value (see 1.)
+with the array key "value" the matched statistic values (bytes and packets) are available,
+while with the array key "value_dropped" the dropped statistic values (bytes and packets) are available.
+
+For statistic entries created in the past before this distinction of statistic kinds
+was introduced only the "value" part (representing the matched values) will be available,
+only for new, future entries "value_dropped" is available.
+
+
+
diff --git a/flowspec/snmpstats.py b/flowspec/snmpstats.py
index b47a831150c2c18c4f78e23f5c7743d831803168..b8b36b6cd55f886b24bb8172f044a26f6ef1dee3 100644
--- a/flowspec/snmpstats.py
+++ b/flowspec/snmpstats.py
@@ -23,6 +23,7 @@ from datetime import datetime, timedelta
 import json
 import os
 import time
+import re
 
 from flowspec.models import Route
 from flowspec.junos import create_junos_name
@@ -36,17 +37,19 @@ identoffset = len(settings.SNMP_CNTPACKETS) + 1
 # noinspection PyUnusedLocal,PyUnusedLocal
 def snmpCallback(snmpEngine, sendRequestHandle, errorIndication,
           errorStatus, errorIndex, varBindTable, cbCtx):
+  try:
     (authData, transportTarget, results) = cbCtx
+    logger.error('snmpCallback(): called')
 
     # debug - which router replies:
     #print('%s via %s' % (authData, transportTarget))
 
     # CNTPACKETS and CNTBYTES are of the same length
     if errorIndication:
-        logger.error('Bad errorIndication.')
+        logger.error('snmpCallback(): Bad errorIndication.')
         return 0
     elif errorStatus:
-        logger.error('Bad errorStatus.')
+        logger.error('snmpCallback(): Bad errorStatus.')
         return 0
     for varBindRow in varBindTable:
         for name, val in varBindRow:
@@ -56,7 +59,7 @@ def snmpCallback(snmpEngine, sendRequestHandle, errorIndication,
             elif name.startswith(settings.SNMP_CNTBYTES):
                 counter = "bytes"
             else:
-                logger.debug('Finished {}.'.format(transportTarget))
+                logger.debug('snmpCallback(): Finished {}.'.format(transportTarget))
                 return 0
 
             ident = name[identoffset:]
@@ -70,28 +73,43 @@ def snmpCallback(snmpEngine, sendRequestHandle, errorIndication,
                 len2 = ordvals[len1] + 1
                 routename = "".join([chr(i) for i in ordvals[len1 + 1:len1 + len2]])
 
+                logger.error("routename="+str(routename))
+                xtype='counter'
+                if re.match(r'^[0-9]+[Mk]_', routename):
+                    ary=re.split(r'_', routename, maxsplit=1)
+                    xtype=ary[0]
+                    routename=ary[1]
+                logger.error("=> routename="+str(routename)+" xtype="+str(xtype))
+
                 # add value into dict
                 if routename in results:
-                    if counter in results[routename]:
-                        results[routename][counter] = results[routename][counter] + int(val)
+                  if xtype in results[routename]:
+                    if counter in results[routename][xtype]:
+                        results[routename][xtype][counter] = results[routename][xtype][counter] + int(val)
                     else:
-                        results[routename][counter] = int(val)
+                        results[routename][xtype][counter] = int(val)
+                  else:
+                    logger.debug("snmp stats: initial add2 %s %s %s %s = %s" %(transportTarget, counter, tablename, routename, int(val)))
+                    results[routename][xtype] = { counter: int(val) } 
                 else:
-                    results[routename] = {counter: int(val)}
+                    logger.debug("snmp stats: initial add1 %s %s %s %s = %s" %(transportTarget, counter, tablename, routename, int(val)))
+                    results[routename] = { xtype: { counter: int(val) } }
                 logger.debug("%s %s %s %s = %s" %(transportTarget, counter, tablename, routename, int(val)))
-
-    return 1  # continue table retrieval
+                
+  except Exception as e:
+    logger.error("snmpCallback(): got exception "+str(e), exc_info=True)
+  return 1  # continue table retrieval
 
 
 def get_snmp_stats():
-    """Return dict() of the sum of counters (bytes, packets) from all selected routes, where
-    route identifier is the key in dict.  The sum is counted over all routers.
-
-    Example output with one rule: {'77.72.72.1,0/0,proto=1': {'bytes': 13892216, 'packets': 165387}}
+  """Return dict() of the sum of counters (bytes, packets) from all selected routes, where
+  route identifier is the key in dict.  The sum is counted over all routers.
 
-    This function uses SNMP_IP list, SNMP_COMMUNITY, SNMP_CNTPACKETS and
-    SNMP_RULESFILTER list, all defined in settings."""
+  Example output with one rule: {'77.72.72.1,0/0,proto=1': {'bytes': 13892216, 'packets': 165387}}
 
+  This function uses SNMP_IP list, SNMP_COMMUNITY, SNMP_CNTPACKETS and
+  SNMP_RULESFILTER list, all defined in settings."""
+  try:
     if not isinstance(settings.SNMP_IP, list):
         settings.SNMP_IP = [settings.SNMP_IP]
 
@@ -132,6 +150,8 @@ def get_snmp_stats():
     snmpEngine.transportDispatcher.runDispatcher()
 
     return results
+  except Exception as e:
+      logger.error("get_snmp_stats(): got exception "+str(e), exc_info=True)
 
 def lock_history_file(wait=1, reason=""):
     first=1
@@ -217,6 +237,21 @@ def helper_rule_ts_parse(ts_string):
   #logger.info("helper_rule_ts_parse(): => ts="+str(ts))
   return ts
 
+#
+
+xtype_default='counter'
+
+def helper_get_countertype_of_rule(ruleobj):
+   xtype = xtype_default
+   limit_rate = None
+   for thenaction in ruleobj.then.all():
+       if thenaction.action and thenaction.action=='rate-limit':
+           limit_rate=thenaction.action_value
+           xtype=str(limit_rate)
+   return xtype
+
+#
+
 def poll_snmp_statistics():
     logger.debug("poll_snmp_statistics(): polling SNMP statistics.")
 
@@ -272,12 +307,22 @@ def poll_snmp_statistics():
         # proper update history
         samplecount = settings.SNMP_MAX_SAMPLECOUNT
         for rule in newdata:
-            counter = {"ts": nowstr, "value": newdata[rule]}
-            if rule in history:
-                history[rule].insert(0, counter)
-                history[rule] = history[rule][:samplecount]
-            else:
-                history[rule] = [counter]
+          counter=None
+          for xtype in newdata[rule]:
+              key = "value"
+              if xtype!="counter":
+                  key = "value_"+str(xtype)
+              if counter==None:
+                #counter = {"ts": nowstr, "value": newdata[rule]['counter']}
+                counter = {"ts": nowstr, key: newdata[rule][xtype]}
+                if rule in history:
+                    history[rule].insert(0, counter)
+                    history[rule] = history[rule][:samplecount]
+                else:
+                    history[rule] = [counter]
+              else:
+                counter[key] = newdata[rule][xtype]
+                #logger.info("poll_snmp_statistics(): reused existing rule x xtype entry:"+str(counter))
 
         # check for old rules and remove them
         toremove = []
@@ -310,12 +355,27 @@ def poll_snmp_statistics():
           for ruleobj in queryset:
             rule_id = str(ruleobj.id)
             rule_status = str(ruleobj.status).upper()
-            logger.debug("snmpstats: STATISTICS_PER_RULE rule_id="+str(rule_id)+" rule_status="+str(rule_status))
+
+            #xtype_default='counter'
+            #xtype = xtype_default
+            #limit_rate = None
+            #for thenaction in ruleobj.then.all():
+            #    if thenaction.action and thenaction.action=='rate-limit':
+            #        limit_rate=thenaction.action_value
+            #        xtype=str(limit_rate)
+            xtype = helper_get_countertype_of_rule(ruleobj)
+                        
+            logger.debug("snmpstats: STATISTICS_PER_RULE rule_id="+str(rule_id)+" rule_status="+str(rule_status)+" xtype="+str(xtype))
             #rule_last_updated = str(ruleobj.last_updated) # e.g. 2018-06-21 08:03:21+00:00
             #rule_last_updated = datetime.strptime(str(ruleobj.last_updated), '%Y-%m-%d %H:%M:%S+00:00') # TODO TZ offset assumed to be 00:00
             rule_last_updated = helper_rule_ts_parse(str(ruleobj.last_updated))
-            counter_null = {"ts": rule_last_updated.isoformat(), "value": null_measurement }
-            counter_zero = {"ts": rule_last_updated.isoformat(), "value": zero_measurement }
+
+            if xtype==xtype_default:
+              counter_null = {"ts": rule_last_updated.isoformat(), "value": null_measurement }
+              counter_zero = {"ts": rule_last_updated.isoformat(), "value": zero_measurement }
+            else:
+              counter_null = {"ts": rule_last_updated.isoformat(), "value": null_measurement, "value_dropped": null_measurement }
+              counter_zero = {"ts": rule_last_updated.isoformat(), "value": zero_measurement, "value_dropped": zero_measurement }
 
             #logger.info("snmpstats: STATISTICS_PER_RULE ruleobj="+str(ruleobj))
             #logger.info("snmpstats: STATISTICS_PER_RULE ruleobj.type="+str(type(ruleobj)))
@@ -326,7 +386,21 @@ def poll_snmp_statistics():
 
             if rule_status=="ACTIVE":
               try:
-                counter = {"ts": nowstr, "value": newdata[flowspec_params_str]}
+                if xtype==xtype_default:
+                  logger.info("poll_snmp_statistics(): 1a STATISTICS_PER_RULE rule_id="+str(rule_id))
+                  counter = {"ts": nowstr, "value": newdata[flowspec_params_str][xtype_default]}
+                else:
+                  logger.info("poll_snmp_statistics(): 1b STATISTICS_PER_RULE rule_id="+str(rule_id))
+                  try:
+                    val1 = newdata[flowspec_params_str][xtype_default]
+                  except Exception:
+                    pass
+                  try:
+                    val2 = newdata[flowspec_params_str][xtype]
+                  except Exception:
+                    pass
+                  counter = {"ts": nowstr, "value": val1, "value_dropped": val2}
+
                 counter_is_null = False
               except Exception as e:
                 logger.info("poll_snmp_statistics(): 1 STATISTICS_PER_RULE: exception: rule_id="+str(rule_id)+" newdata for flowspec_params_str='"+str(flowspec_params_str)+"' missing : "+str(e))
@@ -403,7 +477,8 @@ def poll_snmp_statistics():
     unlock_history_file()
     logger.info("poll_snmp_statistics(): polling end: old_nowstr="+str(nowstr)+" last_poll_no_time="+str(last_poll_no_time))
 
-def add_initial_zero_value(rule_id, zero_or_null=True):
+def add_initial_zero_value(rule_id, route_obj, zero_or_null=True):
+    rule_id=str(rule_id)
     logger.debug("add_initial_zero_value(): rule_id="+str(rule_id))
 
     # get new data
@@ -430,18 +505,27 @@ def add_initial_zero_value(rule_id, zero_or_null=True):
     else:
       zero_measurement = 0
     
-    counter = {"ts": nowstr, "value": zero_measurement }
+    #
+
+    xtype = helper_get_countertype_of_rule(route_obj)
+   
+    if xtype==xtype_default:
+      counter = {"ts": nowstr, "value": zero_measurement }
+    else:
+      counter = {"ts": nowstr, "value": zero_measurement, "value_dropped": zero_measurement }
         
     samplecount = settings.SNMP_MAX_SAMPLECOUNT
 
     try:
         if rule_id in history_per_rule:
+              logger.error("add_initial_zero_value(): rule_id="+str(rule_id)+" : already in hist");
               rec = history_per_rule[rule_id]
               last_rec = rec[0]
               if last_rec==None or (zero_or_null and last_rec['value']==0) or ((not zero_or_null) and last_rec['value']!=0):
                 rec.insert(0, counter)
                 history_per_rule[rule_id] = rec[:samplecount]
         else:
+              logger.error("add_initial_zero_value(): rule_id="+str(rule_id)+" : missing in hist");
               if zero_or_null:
                 history_per_rule[rule_id] = [counter]
 
diff --git a/flowspec/tasks.py b/flowspec/tasks.py
index 52cb4370f67282e5b37169b1816a7cd6fb55b6db..19aa89058caf0c677fd3f14aab59e9d806bba8f4 100644
--- a/flowspec/tasks.py
+++ b/flowspec/tasks.py
@@ -52,17 +52,21 @@ def add(routepk, callback=None):
     commit, response = applier.apply()
     if commit:
         route.status = "ACTIVE"
+        route.response = response
+        route.save()
         #snmp_add_initial_zero_value.delay(str(route.id), True)
-        snmp_add_initial_zero_value(str(route.id), True)
+        snmp_add_initial_zero_value(routepk, route.id, True) # route must exist, so that snmp_add_initial_zero_value can find it in DB
     elif response=="Task timeout":
         if deactivate_route.request.retries < settings.NETCONF_MAX_RETRY_BEFORE_ERROR:
             # repeat the action
             raise TimeoutError()
         route.status = "ERROR"
+        route.response = response
+        route.save()
     else:
         route.status = "ERROR"
-    route.response = response
-    route.save()
+        route.response = response
+        route.save()
     announce("[%s] Rule add: %s - Result: %s" % (route.applier_username_nice, route.name_visible, response), route.applier, route)
 
 
@@ -71,14 +75,16 @@ def edit(routepk, callback=None):
     from flowspec.models import Route
     route = Route.objects.get(pk=routepk)
     status_pre = route.status
-    logger.info("tasks::edit(): route="+str(route)+", status_pre="+str(status_pre))
+    logger.info("tasks::edit(): routepk="+str(routepk)+" => route="+str(route)+", status_pre="+str(status_pre))
     applier = PR.Applier(route_object=route)
     commit, response = applier.apply(operation="replace")
     if commit:
         route.status = "ACTIVE"
+        route.response = response
+        route.save() # save() has to be called before snmp_add_initial_zero_value, as last_updated DB filed is updated now on every call of save() and last db_measurement time must become >= this new last_updated value
         try:
           #snmp_add_initial_zero_value.delay(str(route.id), True)
-          snmp_add_initial_zero_value(str(route.id), True)
+          snmp_add_initial_zero_value(routepk, route.id, True)
         except Exception as e:
           logger.error("tasks::edit(): route="+str(route)+", ACTIVE, add_initial_zero_value failed: "+str(e))
     elif response=="Task timeout":
@@ -86,10 +92,14 @@ def edit(routepk, callback=None):
             # repeat the action
             raise TimeoutError()
         route.status = "ERROR"
+        route.response = response
+        route.save()
     else:
         route.status = "ERROR"
-    route.response = response
-    route.save()
+        route.response = response
+        route.save()
+    #route.response = response
+    #route.save()
     announce("[%s] Rule edit: %s - Result: %s" % (route.applier_username_nice, route.name_visible, response), route.applier, route)
 
 @shared_task(ignore_result=True, autoretry_for=(TimeoutError, TimeLimitExceeded, SoftTimeLimitExceeded), retry_backoff=True, retry_kwargs={'max_retries': settings.NETCONF_MAX_RETRY_BEFORE_ERROR})
@@ -120,7 +130,7 @@ def deactivate_route(routepk, **kwargs):
     if commit:
         route.status="INACTIVE"
         try:
-            snmp_add_initial_zero_value(str(route.id), False)
+            snmp_add_initial_zero_value(routepk, route.id, False)
         except Exception as e:
             logger.error("tasks::deactivate_route(): route="+str(route)+", INACTIVE, add_null_value failed: "+str(e))
 
@@ -432,18 +442,28 @@ def poll_snmp_statistics():
       logger.info("poll_snmp_statistics(): after os._exit() in child process (pid="+str(pid)+", ppid="+str(ppid)+")")
 
 @shared_task(ignore_result=True, max_retries=0)
-def snmp_add_initial_zero_value(rule_id, zero_or_null=True):
+def snmp_add_initial_zero_value(routepk, route_id, zero_or_null=True):
     from flowspec import snmpstats
+    logger.info("snmp_add_initial_zero_value(): routepk="+str(routepk)+" route_id="+str(route_id))
+   
+    route = None
+    if route==None:
+      try:
+        from flowspec.models import Route
+        route = Route.objects.get(pk=routepk)
+      except Exception as e:
+        logger.error("snmp_add_initial_zero_value(): route with routepk="+str(routepk)+" route_id="+str(route_id)+" not found")
+        return 
 
     use_fork = False
 
     if not use_fork:
-      snmpstats.add_initial_zero_value(rule_id, zero_or_null)
+      snmpstats.add_initial_zero_value(route_id, route, zero_or_null)
     else:
       signal.signal(signal.SIGCHLD, handleSIGCHLD)
 
       pid = os.getpid()
-      logger.info("snmp_add_initial_zero_value(): before fork (pid="+str(pid)+" rule_id="+str(rule_id)+","+str(zero_or_null)+")")
+      logger.info("snmp_add_initial_zero_value(): before fork (pid="+str(pid)+" routepk="+str(routepk)+","+str(zero_or_null)+")")
       npid = os.fork()
       if npid == -1:
         pass
@@ -455,10 +475,10 @@ def snmp_add_initial_zero_value(rule_id, zero_or_null=True):
         logger.info("snmp_add_initial_zero_value(): in child process (pid="+str(pid)+", ppid="+str(ppid)+")")
 
         try:
-          snmpstats.add_initial_zero_value(rule_id, zero_or_null)
-          logger.debug("snmp_add_initial_zero_value(): rule_id="+str(rule_id)+","+str(zero_or_null)+" sucesss")
+          snmpstats.add_initial_zero_value(route_id, route, zero_or_null)
+          logger.debug("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" sucesss")
         except Exception as e:
-          logger.error("snmp_add_initial_zero_value(): rule_id="+str(rule_id)+","+str(zero_or_null)+" failed: "+str(e))
+          logger.error("snmp_add_initial_zero_value(): routepk="+str(routepk)+","+str(zero_or_null)+" failed: "+str(e))
 
         #exit_process()
 
diff --git a/templates/flowspy/route_details.html b/templates/flowspy/route_details.html
index f8a0450ec7158ef0a35490e7dd9f05b22389b558..f05f4f718266dca7d3021235666aa4e842ce80e6 100644
--- a/templates/flowspy/route_details.html
+++ b/templates/flowspy/route_details.html
@@ -137,29 +137,66 @@ function myreloadPage() {
 <script src="{% static 'js/chartjs/chartjs-plugin-zoom.min.js' %}"></script>
 
 <script type="text/javascript">
-function plotGraph(data)
+function plotGraph(route_then_action, data)
 {
+   var is_rate_limiting_rule = route_then_action.includes("rate-limit"); 
+   var is_drop_rule = route_then_action.includes("discard") || route_then_action.includes("drop");
+   var is_accept_rule = route_then_action.includes("accept");
+
+   //	
+
    var xdata = Array();
-   var ydata = Array();
-   var ydatarel = Array();
+
+   var ypkgdata = Array();
+   var ypkgdatarel = Array();
    var ybytesdata = Array();
    var ybytesdatarel = Array();
 
+   var ydroppedpkgdata = Array();
+   var ydroppedpkgdatarel = Array();
+   var ydroppedbytesdata = Array();
+   var ydroppedbytesdatarel = Array();
+
+   var ydropped_available = false;
+
    for (i=0; i<data["data"].length; i++) {
        var d = data["data"][data["data"].length - 1 - i];
+
        xdata[i] = d.ts.replace(/\..*/, '').replace('T', ' ');
-       ydata[i] = d.value.packets;
+
+       ypkgdata[i] = d.value.packets;
        ybytesdata[i] = d.value.bytes;
+
        if (i == 0) {
-           ydatarel[i] = 0;
+           ypkgdatarel[i] = 0;
            ybytesdatarel[i] = 0;
        } else {
-           delta = (ydata[i]===undefined) ? undefined : (ydata[i-1]===undefined) ? ydata[i] : (ydata[i] - ydata[i-1]);
-           ydatarel[i] = (delta===undefined || delta>=0) ? delta : 0;
+           delta = (ypkgdata[i]===undefined) ? undefined : (ypkgdata[i-1]===undefined) ? ypkgdata[i] : (ypkgdata[i] - ypkgdata[i-1]);
+           ypkgdatarel[i] = (delta===undefined || delta>=0) ? delta : 0;
 
            bytesdelta = (ybytesdata[i]===undefined) ? undefined : (ybytesdata[i-1]===undefined) ? ybytesdata[i] : (ybytesdata[i] - ybytesdata[i-1]);
            ybytesdatarel[i] = (bytesdelta===undefined || bytesdelta>=0) ? bytesdelta : 0;
        }
+
+       if (d.value_dropped!=undefined) {
+         ydropped_available=true
+
+         ydroppedpkgdata[i] = d.value_dropped.packets;
+         ydroppedbytesdata[i] = d.value_dropped.bytes;
+  
+         if (i == 0) {
+             ydroppedpkgdatarel[i] = 0;
+             ydroppedbytesdatarel[i] = 0;
+         } else {
+             delta = (ydroppedpkgdata[i]===undefined) ? undefined : (ydroppedpkgdata[i-1]===undefined) ? ydroppedpkgdata[i] : (ydroppedpkgdata[i] - ydroppedpkgdata[i-1]);
+             ydroppedpkgdatarel[i] = (delta===undefined || delta>=0) ? delta : 0;
+  
+             bytesdelta = (ydroppedbytesdata[i]===undefined) ? undefined : (ydroppedbytesdata[i-1]===undefined) ? ydroppedbytesdata[i] : (ydroppedbytesdata[i] - ydroppedbytesdata[i-1]);
+             ydroppedbytesdatarel[i] = (bytesdelta===undefined || bytesdelta>=0) ? bytesdelta : 0;
+         }
+  
+  
+       }
    }
 
    var graphpktsabs = document.getElementById("traffic-plot-pkts-abs");
@@ -175,18 +212,124 @@ function plotGraph(data)
    graphbytesrel.width = 80;
    graphbytesrel.height = 20;
 
+   //
+
+   accept__borderColor = "#20ff20"; // green
+   accept__pointbackgroundColor = "#ccffcc"; // light green
+   accept__backgroundColor = "#ccffcc"; // light green
+
+   drop__borderColor = "#ff2020"; // red
+   drop__pointbackgroundColor = "#ffcccc"; // light red
+   drop__backgroundColor = "#ffcccc"; // light red
+
+   matched__borderColor = "#ffa500"; // orange
+   matched__pointbackgroundColor = "#ffff00"; // yellow
+   matched__backgroundColor = "#ffff00"; // yellow
+
+
+   if (is_drop_rule) {
+     matched_text = "matched and dropped";
+     value1__borderColor = drop__borderColor;
+     value1__pointbackgroundColor = drop__pointbackgroundColor;
+     value1__backgroundColor = drop__backgroundColor;
+   } else if (is_accept_rule) {
+     matched_text = "matched and accepted";
+     value1__borderColor = accept__borderColor;
+     value1__pointbackgroundColor = accept__pointbackgroundColor;
+     value1__backgroundColor = accept__backgroundColor;
+   } else { 
+     matched_text = "matched";
+     value1__borderColor = matched__borderColor;
+     value1__pointbackgroundColor = matched__pointbackgroundColor;
+     value1__backgroundColor = matched__backgroundColor;
+   }
+  
+   var ypkg_datasets = [{
+           label: '# packets '+matched_text,
+           data: ypkgdata,
+           borderWidth: 2,
+           borderColor: value1__borderColor,
+           pointBackgroundColor: value1__pointbackgroundColor,
+           backgroundColor: value1__backgroundColor
+           //borderColor: "#3c37c6",
+           //pointBackgroundColor:  "#3c37c6",
+           //backgroundColor: "#99bfff"
+       }];
+   var ypkgrel_datasets = [{
+           label: '# packets '+matched_text,
+           data: ypkgdatarel,
+           borderWidth: 2,
+           borderColor: value1__borderColor,
+           pointBackgroundColor: value1__pointbackgroundColor,
+           backgroundColor: value1__backgroundColor
+           //borderColor: "#c63737",
+           //pointBackgroundColor:  "#c63737",
+           //backgroundColor: "#ff877a"
+       }];
+   var ybytes_datasets = [{
+           label: '# bytes '+matched_text,
+           data: ybytesdata,
+           borderWidth: 2,
+           borderColor: value1__borderColor,
+           pointBackgroundColor: value1__pointbackgroundColor,
+           backgroundColor: value1__backgroundColor
+           //borderColor: "#3c37c6",
+           //pointBackgroundColor:  "#3c37c6",
+           //backgroundColor: "#99bfff"
+       }];
+    var ybytesrel_datasets = [{
+           label: '# bytes '+matched_text,
+           data: ybytesdatarel,
+           borderWidth: 2,
+           borderColor: value1__borderColor,
+           pointBackgroundColor: value1__pointbackgroundColor,
+           backgroundColor: value1__backgroundColor
+           //borderColor: "#c63737",
+           //pointBackgroundColor:  "#c63737",
+           //backgroundColor: "#ff877a"
+       }];
+
+
+   if (ydropped_available) {
+     ypkg_datasets.push({
+           label: '# packets dropped',
+           data: ydroppedpkgdata,
+           borderWidth: 2,
+           borderColor: drop__borderColor,
+           pointBackgroundColor: drop__pointbackgroundColor,
+           backgroundColor: drop__backgroundColor
+       });
+     ypkgrel_datasets.push({
+           label: '# packets dropped',
+           data: ydroppedpkgdatarel,
+           borderWidth: 2,
+           borderColor: drop__borderColor,
+           pointBackgroundColor: drop__pointbackgroundColor,
+           backgroundColor: drop__backgroundColor
+       });
+     ybytes_datasets.push({
+           label: '# bytes dropped',
+           data: ydroppedbytesdata,
+           borderWidth: 2,
+           borderColor: drop__borderColor,
+           pointBackgroundColor: drop__pointbackgroundColor,
+           backgroundColor: drop__backgroundColor
+       });
+     ybytesrel_datasets.push({
+           label: '# bytes dropped',
+           data: ydroppedbytesdatarel,
+           borderWidth: 2,
+           borderColor: drop__borderColor,
+           pointBackgroundColor: drop__pointbackgroundColor,
+           backgroundColor: drop__backgroundColor
+       });
+   }
+
    var graphabssetting = {
        type: 'line',
        data: {
        labels: xdata,
-       datasets: [{
-           label: '# packets',
-           data: ydata,
-           borderWidth: 2,
-           borderColor: "#3c37c6",
-           pointBackgroundColor:  "#3c37c6",
-           backgroundColor: "#99bfff"
-       }]
+       datasets: ypkg_datasets
        },
        options: {
        elements: {
@@ -214,14 +357,7 @@ function plotGraph(data)
        type: 'bar',
        data: {
        labels: xdata,
-       datasets: [{
-           label: '# packets',
-           data: ydatarel,
-           borderWidth: 2,
-           borderColor: "#c63737",
-           pointBackgroundColor:  "#c63737",
-           backgroundColor: "#ff877a"
-       }]
+       datasets: ypkgrel_datasets
        },
        options: {
        elements: {
@@ -240,14 +376,7 @@ function plotGraph(data)
        type: 'line',
        data: {
        labels: xdata,
-       datasets: [{
-           label: '# bytes',
-           data: ybytesdata,
-           borderWidth: 2,
-           borderColor: "#3c37c6",
-           pointBackgroundColor:  "#3c37c6",
-           backgroundColor: "#99bfff"
-       }]
+       datasets: ybytes_datasets
        },
        options: {
        elements: {
@@ -266,14 +395,7 @@ function plotGraph(data)
        type: 'bar',
        data: {
        labels: xdata,
-       datasets: [{
-           label: '# bytes',
-           data: ybytesdatarel,
-           borderWidth: 2,
-           borderColor: "#c63737",
-           pointBackgroundColor:  "#c63737",
-           backgroundColor: "#ff877a"
-       }]
+       datasets: ybytesrel_datasets
        },
        options: {
        elements: {
@@ -292,6 +414,8 @@ function plotGraph(data)
    var pktsrelChart = new Chart(graphpktsrel, graphrelsetting);
    var bytesabsChart = new Chart(graphbytesabs, graphbytesabssetting);
    var bytesrelChart = new Chart(graphbytesrel, graphbytesrelsetting);
+
+
 }
 
 $(document).ready(function() {
@@ -301,7 +425,7 @@ $(document).ready(function() {
           $("#traffic-plot-loading").text("No data, try later");
       } else {
           $("#traffic-plot-loading").hide();
-          plotGraph(data);
+          plotGraph("{{ route.get_then }}", data);
       }
    });
 });
diff --git a/vnet_router/fod_vnet_router b/vnet_router/fod_vnet_router
index 1dd378691a46f6b918467711b2f25f6d9c22f67d..f41566c6facea17cba81b845616aed6de6fbf31f 100755
--- a/vnet_router/fod_vnet_router
+++ b/vnet_router/fod_vnet_router
@@ -779,11 +779,13 @@ elif [ "$1" = "--helper__process_ruleinfo__ovs_method2_step1" ]; then #arg
   #echo "priority=1,table=0,$proto,${address_pre}src=$sourcex,${address_pre}dst=$destination${add1} cookie=0x$cookie actions=drop" >> "$ovs_replace_flow_file" 
 
   if [ "$protocol_num" = "" -o "$protocol_num" = "-" ]; then
+    echo "debug add to ovs_replace_flow_file: priority=1,$proto,${address_pre}src=$sourcex,${address_pre}dst=$destination${add1} cookie=0x$cookie actions=drop" 1>&2
     echo "priority=1,$proto,${address_pre}src=$sourcex,${address_pre}dst=$destination${add1} cookie=0x$cookie actions=drop" >> "$ovs_replace_flow_file" 
   else
 
     printf "%s\\n" "$protocol_num" | tr "," "\\n" | while read protocol_num1 rest; do
       add10=",ip_proto=$protocol_num1$add1"
+      echo "debug add to ovs_replace_flow_file: priority=1,$proto,${address_pre}src=$sourcex,${address_pre}dst=$destination${add10} cookie=0x$cookie actions=drop" 1>&2
       echo "priority=1,$proto,${address_pre}src=$sourcex,${address_pre}dst=$destination${add10} cookie=0x$cookie actions=drop" >> "$ovs_replace_flow_file" 
     done
 
@@ -815,6 +817,11 @@ elif [ "$1" = "--helper__process_ruleinfo__ovs_method2_postproc" ]; then #arg
   
     ovs-ofctl set-frags "$bridge" nx-match # fragment handling
 
+    (set -x
+    grep -H . "$ovs_replace_flow_file" 1>&2
+    echo 1>&2
+    )
+
     ovs-ofctl replace-flows "$bridge" "$ovs_replace_flow_file"
     ovs-ofctl diff-flows "$bridge" /dev/null
 
@@ -828,6 +835,13 @@ elif [ "$1" = "--process_ruleinfo" ]; then #arg
     use_ovs=0
   fi
 
+  use_random_values=0
+  if [ -f "/randomvalues" ]; then 
+    use_random_values=1
+  fi
+
+  #
+
   use_ovs_method2=1
 
   also_use_exabgp=0
@@ -863,7 +877,8 @@ elif [ "$1" = "--process_ruleinfo" ]; then #arg
   "$0" --filter-ruleinfo-unique | tr '\r' '\n' | grep -v "^ *$" | \
   while read -r name ipversion source destination protocol source_ports destination_ports fragment_options then rest; do
     #echo "name=$name ipversion=$ipversion source=$source ..." 1>&2
-  
+ 
+    echo 1>&2 
     echo "$0: process_ruleinfo: loop: step name=$name" 1>&2
  
     bgp_params1="$name $ipversion $source $destination $protocol $source_ports $destination_ports $fragment_options $then" 
@@ -885,12 +900,62 @@ elif [ "$1" = "--process_ruleinfo" ]; then #arg
       [ -n "$protocol_num" ] || protocol_num="-"
       #echo "protocol=$protocol => protocol_num=$protocol_num" 1>&2
   
-      counter_values="$("$0" --get_counters_for_rule "$cookie")";
-      echo "$0: process_ruleinfo: loop: step: name=$name counter_values=$counter_values" 1>&2
+      counter_values_drop="$("$0" --get_counters_for_rule "$cookie")";
+      echo "$0: process_ruleinfo: loop: step: name=$name counter_values_drop=$counter_values_drop" 1>&2
   
     fi #endof !exabgp
+   
+    ##
+
+    if [ \( -z "$counter_values_drop" -o "$counter_values_drop" = "0 0" \) -a "$use_random_values" = 1 ]; then
+
+      store_file_prefix="/dev/shm/rule_random_counter-$cookie"    
+
+      old_random_value_read_bytes="$(cat "$store_file_prefix-read-bytes.val")"
+      old_random_value_read_pkgs="$(cat "$store_file_prefix-read-pkgs.val")"
+      [ -n "$old_random_value_read_bytes" ] || old_random_value_read_bytes=0
+      [ -n "$old_random_value_read_pkgs" ] || old_random_value_read_pkgs=0
+
+      random_value_add_read_bytes=$(( $RANDOM * $RANDOM )) 
+      random_value_add_read_pkgs=$(( $RANDOM * $RANDOM / 100 / 100 )) 
+
+      random_value_read_bytes=$(( $old_random_value_read_bytes + $random_value_add_read_bytes )) 
+      random_value_read_pkgs=$(( $old_random_value_read_pkgs + $random_value_add_read_pkgs )) 
+
+      echo "$random_value_read_bytes" > "$store_file_prefix-read-bytes.val"
+      echo "$random_value_read_pkgs" > "$store_file_prefix-read-pkgs.val"
+
+      ##
+
+      if [ "$then" = "${then#rate-limit}" ]; then # not a rate-limiting rule?
+        random_value_drop_bytes="$random_value_read_bytes"
+        random_value_drop_pkgs="$random_value_read_pkgs"
+      else
+
+        old_random_value_drop_bytes="$(cat "$store_file_prefix-drop-bytes.val")"
+        old_random_value_drop_pkgs="$(cat "$store_file_prefix-drop-pkgs.val")"
+        [ -n "$old_random_value_drop_bytes" ] || old_random_value_drop_bytes=0
+        [ -n "$old_random_value_drop_pkgs" ] || old_random_value_drop_pkgs=0
+
+        random_value_add_drop_bytes=$(( $RANDOM * $random_value_add_read_bytes / 32678 )) 
+        random_value_add_drop_pkgs=$(( $RANDOM * $random_value_add_read_pkgs / 32678 )) 
+
+        random_value_drop_bytes=$(( $old_random_value_drop_bytes + $random_value_add_drop_bytes )) 
+        random_value_drop_pkgs=$(( $old_random_value_drop_pkgs + $random_value_add_drop_pkgs )) 
+
+        echo "$random_value_drop_bytes" > "$store_file_prefix-drop-bytes.val"
+        echo "$random_value_drop_pkgs" > "$store_file_prefix-drop-pkgs.val"
+
+      fi
+      
+      counter_values_read="$random_value_read_bytes $random_value_read_pkgs"
+      counter_values_drop="$random_value_drop_bytes $random_value_drop_pkgs"
+    fi
+
+    ##
     
-    [ -n "$counter_values" ] || counter_values="- -"
+    [ -n "$counter_values_drop" ] || counter_values_drop="- -"
+    [ -n "$counter_values_read" ] || counter_values_read="$counter_values_drop"
  
     #echo "cookie=$cookie name=$name" 1>&2 
     #echo "cookie=$cookie source=$source" 1>&2 
@@ -899,7 +964,8 @@ elif [ "$1" = "--process_ruleinfo" ]; then #arg
     #echo "cookie=$cookie fragment_options=$fragment_options" 1>&2 
     #echo "cookie=$cookie then=$then" 1>&2 
     #echo "0x$cookie bgpflowspec $counter_values $name $source $destination $protocol $protocol_num $source_ports $destination_ports $fragment_options $then" 
-    echo "0x$cookie bgpflowspec $counter_values $name $source $destination $protocol $protocol_num $source_ports $destination_ports $fragment_options $then" >> "$state_file"
+    #echo "0x$cookie bgpflowspec $counter_values $name $source $destination $protocol $protocol_num $source_ports $destination_ports $fragment_options $then" >> "$state_file"
+    echo "0x$cookie bgpflowspec $counter_values_read $counter_values_drop $name $source $destination $protocol $protocol_num $source_ports $destination_ports $fragment_options $then" >> "$state_file"
 
     echo 1>&2
 
@@ -911,12 +977,15 @@ elif [ "$1" = "--process_ruleinfo" ]; then #arg
   echo "$0: process_ruleinfo: post process" 1>&2
 
   if [ "$also_use_exabgp" = 1 ]; then #exabgp
+    echo "$0: process_ruleinfo: post process exabgp" 1>&2
     "$0" --helper__process_ruleinfo__exabgp_postproc "$dir1"
   fi #! exabgp
 
   if [ "$use_ovs" = 1 -a "$use_ovs_method2" = 0 ]; then
+    echo "$0: process_ruleinfo: post process ovs method1" 1>&2
     "$0" --helper__process_ruleinfo__ovs_method1_postproc "$state_file"
   elif [ "$use_ovs" = 1 -a "$use_ovs_method2" = 1 ]; then
+    echo "$0: process_ruleinfo: post process ovs method2" 1>&2
     "$0" --helper__process_ruleinfo__ovs_method2_postproc "$state_file" "$ovs_replace_flow_file"
   fi #endof !exabgp
 
diff --git a/vnet_router/snmp/pass_persisttest_bgpflowspec b/vnet_router/snmp/pass_persisttest_bgpflowspec
index d95210cb08f1cc7e63177d4846343a356e3c38d0..06ab91535c924a0420b762fa06b361f49d42a4fa 100755
--- a/vnet_router/snmp/pass_persisttest_bgpflowspec
+++ b/vnet_router/snmp/pass_persisttest_bgpflowspec
@@ -105,6 +105,10 @@ sub get_snmp_rulename
   $src =~ s/^0.0.0.0\/0$/0\/0/;
   $dst =~ s/^0.0.0.0\/0$/0\/0/;
 
+  # https://github.com/librenms/librenms/blob/master/mibs/junos/JUNIPER-FIREWALL-MIB
+ 
+  my $counter_type_value = 2; # default is normal counter
+
   my $rulename = $dst.",".$src;
 
   my $proto = $protocol_num;  
@@ -144,9 +148,18 @@ sub get_snmp_rulename
     $rulename .= ",frag".join(",", (map { $frag_types->{$_}; } split(/,/, $fragment_options)));
   }
 
+  my $rulename0 = $rulename;
+  if (defined($thenaction)) {
+    if ($thenaction eq 'discard') {
+    } elsif ($thenaction =~ /^rate-limit-([0-9]+([Mk]?))$/) {
+      $rulename = $1."_".$rulename;
+      $counter_type_value = 3; # policer
+    }
+  }
+
   print STDERR "rulename=$rulename$/" if ($debug);
 
-  return $rulename;
+  return ($rulename, $rulename0, $counter_type_value);
 }
 
 sub get_state($)
@@ -160,16 +173,25 @@ sub get_state($)
   for my $line (@lines) 
   {
     # 0x4c8c66ccd9b34fc0 mn1_O8VMZ6 10.0.0.1/32 10.0.0.2/32 icmp - - 
-    my ($cookie, $typex, $byte_ctr, $pkt_ctr, $rulename, @rule_params) = split(/\s+/, $line);
+    #my ($cookie, $typex, $byte_ctr, $pkt_ctr, $rulename, @rule_params) = split(/\s+/, $line);
+    my ($cookie, $typex, $byte_ctr__read, $pkt_ctr__read, $byte_ctr__drop, $pkt_ctr__drop, $rulename, @rule_params) = split(/\s+/, $line);
     next if ($typex eq 'default');
 
-    my $snmp_rulename = get_snmp_rulename(@rule_params);
+    my ($snmp_rulename, $snmp_rulename0, $counter_type_value) = get_snmp_rulename(@rule_params);
     if ($snmp_rulename ne '') {
+
+      if ($counter_type_value==3) { # read counters in case of rate-limiting, here just read = drop, but output both
+        my $prefix0 = string_to_oid_list($snmp_rulename0);
+        my $snmp_oid_counter_pkts = $place_pkts.$rulesfilter_oidlist.$prefix0.".2";
+        my $snmp_oid_counter_bytes = $place_bytes.$rulesfilter_oidlist.$prefix0.".2";
+        $state_hash->{$snmp_oid_counter_bytes} = $byte_ctr__read;
+        $state_hash->{$snmp_oid_counter_pkts} = $pkt_ctr__read;
+      }
       my $prefix1 = string_to_oid_list($snmp_rulename);
-      my $snmp_oid_counter_pkts = $place_pkts.$rulesfilter_oidlist.$prefix1.".2";
-      my $snmp_oid_counter_bytes = $place_bytes.$rulesfilter_oidlist.$prefix1.".2";
-      $state_hash->{$snmp_oid_counter_bytes} = $byte_ctr;
-      $state_hash->{$snmp_oid_counter_pkts} = $pkt_ctr;
+      my $snmp_oid_counter_pkts = $place_pkts.$rulesfilter_oidlist.$prefix1.".".$counter_type_value;
+      my $snmp_oid_counter_bytes = $place_bytes.$rulesfilter_oidlist.$prefix1.".".$counter_type_value;
+      $state_hash->{$snmp_oid_counter_bytes} = $byte_ctr__drop;
+      $state_hash->{$snmp_oid_counter_pkts} = $pkt_ctr__drop;
     }
   }