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