Skip to content

Instantly share code, notes, and snippets.

@hillar
Last active March 4, 2016 06:34
Show Gist options
  • Save hillar/309e93d5b555095d07b9 to your computer and use it in GitHub Desktop.
Save hillar/309e93d5b555095d07b9 to your computer and use it in GitHub Desktop.
suricata plugin for telegraf

Telegraf Plugin: Suricata

Plugin arguments:

  • socket []string: socket name
  • zeros boolean: If false, it will not send measurments with value 0

Description

The Suricata plugin sends dump-counters command to Suricata unix socket Threads are used as tags

TODO

  • update to latest telegraf plugin system
  • a suricata message is sent in multiple send operations. This result in possible incomplete read on client side. The worse workaround is to sleep a bit before trying a recv call.

Question

to inliniac: how it will be reimplemented ?

A: https://github.com/inliniac/suricata/blob/master/src/counters.c#L782

counters: remove old unix socket json logic

https://github.com/inliniac/suricata/commit/83f27ae2a571e5345d5146eda3b8e850d39543fd

package suricata
// suricata.go
/*
tested with:
vagrant@foo:~$ uname -a
Linux foo 3.13.0-65-generic #105-Ubuntu SMP Mon Sep 21 18:50:58 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
vagrant@foo:~$ suricata -V
This is Suricata version 1.4.7 RELEASE
vagrant@foo:~$ suricata -V
This is Suricata version 2.1beta4 RELEASE
vagrant@foo:~$ /opt/telegraf/telegraf -version
Telegraf - Version v0.1.9-48-g81539c4
*/
import (
"time"
"net"
"io"
"encoding/json"
"errors"
"github.com/influxdb/telegraf/plugins"
)
type Suricata struct {
SocketName string `toml:"socket"`
SendZeros bool `toml:"zeros"`
}
func (s *Suricata) Description() string {
return "a suricata plugin"
}
func (s *Suricata) SampleConfig() string {
return `
# suricata unix socket name
socket = "/var/run/suricata/suricata-command.socket"
#send measurments if value is zero
zeros = true
`
}
func (s *Suricata) Gather(acc plugins.Accumulator) error {
r,e := getCountersFromSocket(s.SocketName)
if e != nil {
return e
}
for k,i := range r {
v := i.(map[string]interface{})
for kk,vv := range v {
if vv == 0.0 && s.SendZeros != true {
continue
}
//convert floats to ints
i := int(vv.(float64))
f := float64(i)
if (vv == f) {vv = i}
// add thread name as tag
if k != "Detect" && k != "FlowManagerThread" {
tags := map[string]string{"thread": k}
acc.Add(kk, vv, tags)
} else {
acc.Add(kk, vv, nil)
}
}
}
return nil
}
type Response struct {
ReturnCode string `json:"return"`
Message map[string]interface{} `json:"message"`
}
func getCountersFromSocket(socketName string) (counts map[string]interface{}, err error) {
conn, err := net.Dial("unix", socketName)
if err != nil {
return nil, err
}
defer conn.Close()
// see https://github.com/inliniac/suricata/blob/94571c5dd28858ff68c44b648fd41c5d87c0e28d/src/unix-manager.c#L288
_, err = conn.Write([]byte("{\"version\": \"0.1\"}"))
if err != nil {
return nil, errors.New("can not send version :: "+err.Error())
}
time.Sleep(100 * time.Millisecond)
var buf [128]byte
_, err = conn.Read(buf[0:])
if err != nil {
// see https://github.com/inliniac/suricata/blob/94571c5dd28858ff68c44b648fd41c5d87c0e28d/src/unix-manager.c#L319
return nil, errors.New("got kick on version :: "+err.Error())
}
_, err = conn.Write([]byte("{\"command\": \"dump-counters\"}"))
if err != nil {
return nil, errors.New("can not send command :: "+err.Error())
}
time.Sleep(100 * time.Millisecond)
data := ""
// read anser for command
// see https://github.com/inliniac/suricata/blob/672049632431bb695f56798c9c5f196afcf2fb27/scripts/suricatasc/src/suricatasc.py#L83
for i := 0; i < 16; i++ {
var buf2 [4096]byte
result2, err := conn.Read(buf2[0:])
if err != nil {
if err != io.EOF {
return nil, err
}
}
data = data + string(buf2[:result2])
var isJson Response
jerr := json.Unmarshal([]byte(data), &isJson)
if jerr == nil {
if isJson.ReturnCode == "OK" {
return isJson.Message, nil
} else {
return nil, errors.New(isJson.ReturnCode)
}
} else {
expect := "unexpected end of JSON input"
if jerr.Error() != expect {
// is json but not our counters
return nil, jerr
}
}
time.Sleep(10 * time.Millisecond)
}
return nil, errors.New("max limit for loop")
}
func init() {
plugins.Add("suricata", func() plugins.Plugin { return &Suricata{} })
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment