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;
}
}