1
0
mirror of https://github.com/nttgin/BGPalerter.git synced 2024-05-19 06:50:08 +00:00

improved accuracy rules matching

This commit is contained in:
Massimo Candela
2019-08-18 23:19:33 +02:00
parent 4d816feb08
commit 7ea02e88af
9 changed files with 90 additions and 57 deletions

View File

@@ -154,6 +154,17 @@ export default class ConnectorTest extends Connector{
path: "1,2,3,204092".split(",").map(i => parseInt(i))
},
type: "ris_message"
},
{
data: {
announcements: [{
prefixes: ["2a0e:f40::/32"],
next_hop: "124.0.0.3"
}],
peer: "124.0.0.3",
path: "1,2,3,204092".split(",").map(i => parseInt(i))
},
type: "ris_message"
}
];
break;
@@ -180,6 +191,13 @@ export default class ConnectorTest extends Connector{
peer: "124.0.0.2"
},
type: "ris_message"
},
{
data: {
withdrawals: ["2a0e:f40::/32"],
peer: "124.0.0.2"
},
type: "ris_message"
}
];
break;
@@ -189,7 +207,7 @@ export default class ConnectorTest extends Connector{
this.timer = setInterval(() => {
updates.forEach(update => {
this.message(JSON.stringify(update));
this._message(JSON.stringify(update));
if (type === 'visibility') {
let peer = update.data.peer.split('.');
peer[3] = Math.min(parseInt(peer[3]) + 1, 254);

View File

@@ -36,6 +36,8 @@ import ipUtils from "../ipUtils";
export default class Input {
constructor(config){
this.prefixes = [];
this.cache = {};
};
validateAS = (asn) => {
@@ -60,4 +62,28 @@ export default class Input {
throw new Error('The method getMonitoredPrefixes MUST be implemented');
};
getMoreSpecificMatch = (prefix) => {
for (let p of this.prefixes) {
if (p.prefix === prefix) {
return p;
} else {
if (!this.cache[p.prefix]) {
this.cache[p.prefix] = ipUtils.getNetmask(p.prefix);
}
const p2 = ipUtils.getNetmask(prefix);
if (ipUtils.isSubnetBinary(this.cache[p.prefix], p2)) {
if (p.ignoreMorespecifics){
return null;
} else {
return p;
}
}
}
}
return null;
};
}

View File

@@ -34,6 +34,7 @@ import yaml from "js-yaml";
import fs from "fs";
import Input from "./input";
import env from "../env";
import ipUtils from "../ipUtils";
export default class InputYml extends Input {
@@ -70,9 +71,12 @@ export default class InputYml extends Input {
this.prefixes = this.prefixes.concat(monitoredPrefixes);
}
}
this.prefixes = this.prefixes.sort((a, b) => {
return ipUtils.sortByPrefixLength(b.prefix, a.prefix);
});
};
validate = (fileContent) => {

View File

@@ -47,7 +47,7 @@ const ip = {
toDecimal: function(ip) {
let bytes = "";
if (ip.indexOf(":") == -1) {
if (ip.indexOf(":") === -1) {
bytes = ip.split(".").map(ip => parseInt(ip).toString(2).padStart(8, '0')).join("");
} else {
bytes = ip.split(":").filter(ip => ip !== "").map(ip => parseInt(ip, 16).toString(2).padStart(16, '0')).join("");
@@ -63,7 +63,7 @@ const ip = {
let binaryRoot;
if (ip.indexOf(":") == -1){
if (ip.indexOf(":") === -1){
binaryRoot = this.toDecimal(ip).padEnd(32, '0').slice(0, bits);
} else {
binaryRoot = this.toDecimal(ip).padEnd(128, '0').slice(0, bits);
@@ -73,11 +73,12 @@ const ip = {
},
isSubnet: function (prefixContainer, prefixContained) {
prefixContained = this.getNetmask(prefixContained);
prefixContainer = this.getNetmask(prefixContainer);
isSubnetBinary: (prefixContainer, prefixContained) => {
return prefixContained != prefixContainer && prefixContained.includes(prefixContainer);
},
isSubnet: function (prefixContainer, prefixContained) {
return this.isSubnetBinary(this.getNetmask(prefixContainer), this.getNetmask(prefixContained));
}
};

View File

@@ -55,31 +55,20 @@ export default class MonitorHijack extends Monitor {
new Promise((resolve, reject) => {
const messagePrefix = message.prefix;
const matchedRule = this.input.getMoreSpecificMatch(messagePrefix);
let matches = this.monitored.filter(item => {
const sameOrigin = item.asn.includes(message.originAs);
return !sameOrigin &&
(item.prefix == messagePrefix ||
(!item.ignoreMorespecifics && ipUtils.isSubnet(item.prefix, messagePrefix)));
});
if (matches.length > 1) {
matches = [matches.sort((a, b) => ipUtils.sortByPrefixLength(a.prefix, b.prefix)).pop()];
}
if (matchedRule && !matchedRule.asn.includes(message.originAs)) {
const asnText = matchedRule.asn.join(", or AS");
if (matches.length !== 0) {
const match = matches[0];
const asnText = match.asn.join(", or AS");
const text = (message.prefix === match.prefix) ?
`The prefix ${match.prefix} (${match.description}) is announced by AS${message.originAs} instead of AS${asnText}` :
const text = (message.prefix === matchedRule.prefix) ?
`The prefix ${matchedRule.prefix} (${matchedRule.description}) is announced by AS${message.originAs} instead of AS${asnText}` :
`A new prefix ${message.prefix} is announced by AS${message.originAs}. ` +
`It should be instead ${match.prefix} (${match.description}) announced by AS${asnText}`;
`It should be instead ${matchedRule.prefix} (${matchedRule.description}) announced by AS${asnText}`;
this.publishAlert(message.originAs + "-" + message.prefix,
text,
match.asn[0],
matches[0],
matchedRule.asn[0],
matchedRule,
message,
{});
}

View File

@@ -56,27 +56,15 @@ export default class MonitorNewPrefix extends Monitor {
new Promise((resolve, reject) => {
const messagePrefix = message.prefix;
const matchedRule = this.input.getMoreSpecificMatch(messagePrefix);
let matches = this.monitored.filter(item => {
const sameOrigin = item.asn.includes(message.originAs);
return sameOrigin &&
item.prefix != messagePrefix &&
ipUtils.isSubnet(item.prefix, messagePrefix);
});
if (matches.length > 1) {
matches = [matches.sort((a, b) => ipUtils.sortByPrefixLength(a.prefix, b.prefix)).pop()];
}
if (matches.length !== 0) {
const match = matches[0];
const text = `Possible change of configuration. A new prefix ${message.prefix} is announced by AS${message.originAs}. It is a more specific of ${match.prefix} (${match.description}).`;
if (matchedRule && matchedRule.asn.includes(message.originAs) && matchedRule.prefix !== messagePrefix) {
const text = `Possible change of configuration. A new prefix ${message.prefix} is announced by AS${message.originAs}. It is a more specific of ${matchedRule.prefix} (${matchedRule.description}).`;
this.publishAlert(message.originAs + "-" + message.prefix,
text,
match.asn[0],
matches[0],
matchedRule.asn[0],
matchedRule,
message,
{});
}

View File

@@ -68,22 +68,17 @@ export default class MonitorVisibility extends Monitor {
new Promise((resolve, reject) => {
const messagePrefix = message.prefix;
const matchedRule = this.input.getMoreSpecificMatch(messagePrefix);
let matches = this.monitored.filter(item => {
return item.prefix == messagePrefix;
});
if (matches.length > 1) {
matches = [matches.sort((a, b) => ipUtils.sortByPrefixLength(a.prefix, b.prefix)).pop()];
}
if (matchedRule && matchedRule.prefix === messagePrefix) {
const text = `Possible change of configuration. A new prefix ${message.prefix} is announced by AS${message.originAs}. It is a more specific of ${matchedRule.prefix} (${matchedRule.description}).`;
if (matches.length !== 0) {
const match = matches[0];
let key = match.prefix;
let key = matchedRule.prefix;
this.publishAlert(key,
`The prefix ${match.prefix} has been withdrawn.`,
match.asn[0],
matches[0],
`The prefix ${matchedRule.prefix} has been withdrawn.`,
matchedRule.asn[0],
matchedRule,
message,
{});
}

View File

@@ -22,3 +22,8 @@
description: alarig fix test 2
asn: 208585
ignoreMorespecifics: false
2a0e:f40::/30:
description: ignore sub test
asn: 1234
ignoreMorespecifics: true

View File

@@ -126,7 +126,7 @@ describe("Tests", function() {
it("loading prefixes", function () {
expect(env.input.prefixes.length).to.equal(5);
expect(env.input.prefixes.length).to.equal(6);
expect(env.input).to
.containSubset({
"prefixes": [
@@ -164,6 +164,13 @@ describe("Tests", function() {
"ignoreMorespecifics": false,
"prefix": "2a0e:f40::/29",
"user": "default"
},
{
"asn": [1234],
"description": "ignore sub test",
"ignoreMorespecifics": true,
"prefix": "2a0e:f40::/30",
"user": "default"
}
]