Cisco Meraki
// Description:
// Syslog (Event/Traffic logs) from Cisco Meraki Firewalls
// Data input format: ({ obj, size, source }) or ( doc )
function main({obj, size, source}) {
// Event selection criteria
let msg = obj["@message"]
if (!msg){
return {"status":"abort"}
}
let tags = obj["@tags"]
if (!tags) {
return {"status":"abort"}
}
if (!tags.Some( (_, tag) => startsWith(tag, "1" ))) {
return {"status":"abort"}
}
if (!startsWith(msg, "16") && !startsWith(msg, "17") && !startsWith(msg, "18") && !startsWith(msg, "19")){
return {"status":"abort"}
}
// Output field settings
obj["@type"] = "event"
obj["@parser"] = "fpl-CiscoMerakiSyslog"
obj["@parserVersion"] = "20240130-1"
obj["@event_type"]="meraki"
obj["@eventType"]="CiscoMeraki"
// Event parsing
// msg
let sp = split(msg, " ")
if (len(sp) < 6) {
obj["@parserError"] = "Meraki event parser split error"
return {"status":"abort"}
}
let f = {}
tags = []
if (indexOf(sp[1], "=") < 0) {
tags = append(tags, sp[1])
f.deviceName = sp[1]
}
if (indexOf(sp[2], "=") < 0) {
tags = append(tags, sp[2])
f.dataType = sp[2]
}
if(sp[3] == "dhcp"){
let i = 0
for s, item = range sp {
// item = sp[s]
// TODO: unparsed
f[sprintf("dhcp_item%d",i)]=item
i++
}
}
let i = -1
for s, item = range sp {
// find ending non-standard key:value string (w/o '=')
if (indexOf(item, "request") > -1){
i = s
f.protocol = "tcp"
break
}
if (indexOf(item, "pattern") > -1) {
i = s
break
}
// regulard fields
let sp2 = split(item, "=")
if (len(sp2) == 2) {
let k = toLower(sp2[0])
let v = replaceAll(sp2[1], "'", "", -1)
f[k] = v
}
}
if (i > -1) {
let str = ""
// for (let i = 0; i < len(list); i++)
for (let j = i + 1; j < len(sp); j++) {
str = str + sp[j] + " "
}
str = trim(str, " ")
let key = replaceAll(sp[i], ":", "", -1)
f[key] = str
}
/*
if(sp[2] == "flows"){
let a = sp[3]
if (indexOf(a, "=") < 0){
f.action = a
}
}
*/
if(f.src){
let s = f.src
let count = 0
for (let i = 0; i < len(s); i++) {
let c = subString(s, i, i+1)
if (c == ':') {
count++
}
}
if (count == 1) {
let sp2 = split(s, ':')
f.src = sp2[0]
f.sport = sp2[1]
} elseif (count > 1) { // ipv6 support
// uhh.
/*
let index = s.lastIndexOf(":")
if(index >-1){
f.src = s.substring(0, index).replace(/[\[\]']+/g, '')
f.sport = s.substring(index+1, src.length)
}
*/
}
}
if(f.dst){
let d = f.dst
let count = 0
for (let i = 0; i < len(d); i++) {
let c = subString(d, i, i+1)
if (c == ':') {
count++
}
}
if (count == 1) {
let sp2 = split(d, ':')
f.dst = sp2[0]
f.dport = sp2[1]
} elseif (count > 1) { // ipv6 support
// uhh.
/*
let index = s.lastIndexOf(":")
if(index >-1){
f.dst = s.substring(0, index).replace(/[\[\]']+/g, '')
f.dport = s.substring(index+1, dst.length)
}
*/
}
}
// adjust src/dest ip (ip w/ port, ipv6, etc)
//tags = append(tags, deviceName)
obj["@tags"] = tags
obj["@meraki"] = f
// Discard original message
// obj["@message"] = ""
// Collect device metrics
recordDeviceMetrics(obj, size)
// Metaflow, data normalization
generateFusionEvent(obj)
return {"status":"pass"}
}
function recordDeviceMetrics(obj, size) {
let sender = obj["@sender"]
let source = obj["@source"]
let f = obj["@meraki"]
let deviceName = (f.deviceName ? f.deviceName : "unknown")
let deviceEntry = Fluency_Device_LookupName(deviceName)
if (!deviceEntry) {
deviceEntry = {
name:deviceName,
ips: [sender],
group:"FPL-detect: Cisco Meraki FW",
device: {
name:"Cisco Meraki FW",
category:"Firewall"
}
}
Fluency_Device_Add(deviceEntry)
}
let dimensions = {
namespace:"fluency",
app:"import",
eventType:"CiscoMeraki",
syslogSender:sender,
customer: "default",
importSource: deviceEntry.name,
deviceType: deviceEntry.device.name
}
if deviceEntry.group {
dimensions.group = deviceEntry.group
}
Platform_Metric_Counter("fluency_import_count", dimensions, 1)
Platform_Metric_Counter("fluency_import_bytes", dimensions, size)
}
function parseProto(proto) {
if (startsWith(proto, "tcp")) {
return 6
} elseif (startsWith(proto, "udp")) {
return 17
} elseif (startsWith(proto, "icmp")) {
return 1
} elseif (startsWith(proto, "igmp")) {
return 2
} elseif (startsWith(proto, "icmpv6") || startsWith(proto, "icmp6")) {
return 6
}
return 0
}
function generateFusionEvent(obj) {
let f = obj["@meraki"]
if (!(f.src && f.dst && f.sport && f.dport && f.protocol)) {
// printf("invalid event record for flow: %v", f)
return
}
let ts = obj["@timestamp"]
let envelop = {
partition: "default",
dataType: "event",
time_ms: ts
}
let sp = (f.sport ? parseInt(f.sport) : 0)
let dp = (f.dport ? parseInt(f.dport) : 0)
let prot = (f.protocol ? parseProto(f.protocol) : 0)
let source={
flow: {
sip: f.src,
dip: f.dst,
sp: sp,
dp: dp,
prot: prot,
rxB: 0,
txB: 0,
totalB: 0,
rxP: 0,
txP: 0,
dur: 0,
time_ms: ts
},
dtype:"meraki"
}
obj["@metaflow"] = source
// printf("%v",source)
Fluency_FusionEvent(envelop, source)
}
Updated 9 months ago