diff --git a/cmd/main.go b/cmd/main.go index fe7f767..68471c6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,5 +1,80 @@ package main +import ( + "flag" + "fmt" + p "github.com/alessio-perugini/peng" + "log" + "net/url" + "os" +) + +var ( + config = p.Config{ + NumberOfBin: 8, + NumberOfModule: 1024, + InfluxUrl: "http://localhost", + InfluxPort: 9999, + InfluxBucket: "", + InfluxOrganization: "", + InfluxAuthToken: "", + } + + versionFlag bool + version = "0.0.0" + commit = "commithash" +) + +func init() { + //Bitmap + flag.UintVar(&config.InfluxPort, "bin", 128, "number of bin in your bitmap") + flag.UintVar(&config.InfluxPort, "module", 1024, "maximum size of your bitmap") + + //influx + flag.StringVar(&config.InfluxUrl, "influxUrl", "http://localhost", "influx url") + flag.UintVar(&config.InfluxPort, "influxPort", 9999, "influxPort number") + flag.StringVar(&config.InfluxBucket, "bucket", "", "bucket string for telegraf") + flag.StringVar(&config.InfluxOrganization, "org", "", "organization string for telegraf") + flag.StringVar(&config.InfluxAuthToken, "token", "", "auth token for influxdb") + + //other + flag.BoolVar(&versionFlag, "version", false, "output version") +} + +func flagConfig() { + appString := fmt.Sprintf("sys-status version %s %s", version, commit) + + flag.Usage = func() { //help flag + fmt.Fprintf(flag.CommandLine.Output(), "%s\n\nUsage: sys-status [options]\n", appString) + flag.PrintDefaults() + } + + flag.Parse() + + if versionFlag { //version flag + fmt.Fprintf(flag.CommandLine.Output(), "%s\n", appString) + os.Exit(2) + } + + if config.InfluxBucket == "" || config.InfluxOrganization == "" || config.InfluxAuthToken == "" { + log.Fatal("You must provide bucket, organization and influxAuthToken") + } + + if _, err := url.ParseRequestURI(config.InfluxUrl); err != nil { + log.Fatal("Influx url is not valid") + } + + fmt.Printf("%s\n", appString) +} + func main() { - + flagConfig() + + peng := p.New(&config) + peng.Portbitmap.HashFunc = func(port uint16) (uint16, uint64) { + portModuled := (port / uint16(config.NumberOfBin)) % uint16(config.NumberOfModule) + index, bit := portModuled/uint16(config.NumberOfBits), uint64(portModuled)%uint64(config.NumberOfBits) + return index, bit + } + peng.Start() } diff --git a/peng.go b/peng.go index f3fc4ab..be6c263 100644 --- a/peng.go +++ b/peng.go @@ -1,10 +1,8 @@ -package main +package peng import ( - "errors" - "flag" "fmt" - b "github.com/alessio-perugini/peng/pkg/bitmap" + "github.com/alessio-perugini/peng/pkg/portbitmap" "github.com/google/gopacket" "github.com/google/gopacket/layers" _ "github.com/google/gopacket/layers" //Used to init internal struct @@ -18,42 +16,50 @@ import ( "time" ) -//Clear bit &= ^flag -//ToggleFlag ^= flag +type Peng struct { + Config *Config + Portbitmap *portbitmap.PortBitmap +} + +type Config struct { + NumberOfBin uint //TODO forse rimuovere i 2 campi e usare la config del portbitmap + NumberOfModule uint + NumberOfBits uint + InfluxUrl string + InfluxPort uint + InfluxBucket string + InfluxOrganization string + InfluxAuthToken string +} var ( - nBin = 128 - nModule = 1024 - nBits = uint(nModule / nBin) - bitmap = make([]b.Bitmap, nBin) myIPs = make([]net.IP, 0, 2) epsilon = math.Nextafter(1.0, 2.0) - 1.0 - - influxUrl string - bucket string - organization string - influxPort uint - influxAuthToken string ) -//TODO influxdb stuff -func main() { - flag.StringVar(&influxUrl, "influxUrl", "http://localhost", "influx url") - flag.UintVar(&influxPort, "influxPort", 9999, "influxPort number") - flag.StringVar(&bucket, "bucket", "", "bucket string for telegraf") - flag.StringVar(&organization, "org", "", "organization string for telegraf") - flag.StringVar(&influxAuthToken, "token", "", "auth token for influxdb") - flag.Parse() +func New(cfg *Config) *Peng { + cfg.NumberOfBits = cfg.NumberOfModule / cfg.NumberOfBin + bitmapConfig := &portbitmap.Config{ + NumberOfBin: cfg.NumberOfBin, + SizeBitmap: cfg.NumberOfModule, + NumberOfBits: cfg.NumberOfBits, + } - GetMyIp() - initBitmap() + return &Peng{ + Config: cfg, + Portbitmap: portbitmap.New(bitmapConfig), + } +} + +func (p *Peng) Start() { + getMyIp() c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { for sig := range c { fmt.Println(sig) - fmt.Println(bitmap) + fmt.Println(p.Portbitmap.InnerBitmap) os.Exit(1) } }() @@ -71,9 +77,9 @@ func main() { packet := gopacket.NewPacketSource(pHandle, pHandle.LinkType()) - time.AfterFunc(time.Minute, End) + time.AfterFunc(time.Minute, p.end) for packet := range packet.Packets() { - inspect(packet) + p.inspect(packet) //TODO multithread? //TODO proper handle termination //TODO maybe use custom layers to avoid realloc for each packets (memory improvment) @@ -81,52 +87,24 @@ func main() { } } -func initBitmap() { - //Create bitmap - for i := 0; i < nBin; i++ { - bitmap[i] = *b.New(uint64(nBits)) - } -} - -func hash(port uint16) (uint16, uint64) { - portModuled := port % uint16(nModule) - index, bit := portModuled/uint16(nBits), uint64(portModuled)%uint64(nBits) - return index, bit -} - -func InsertInBitmap(port uint16) error { - indexBin, bitBin := hash(port) - if indexBin >= uint16(len(bitmap)) { - return errors.New("index to access the bin is invalid") - } - bitmap[indexBin].SetBit(bitBin, true) - return nil -} - -func ResetBitmap() { - for i := 0; i < len(bitmap); i++ { - bitmap[i].ResetAllBits() - } -} - -func EntropyTotal(binsEntropy []float64) float64 { +func (p *Peng) EntropyTotal(binsEntropy []float64) float64 { var totalEntropy float64 for _, v := range binsEntropy { totalEntropy += v } - return totalEntropy / float64(nBin) + return totalEntropy / float64(p.Portbitmap.Config.NumberOfBin) } //reference https://rosettacode.org/wiki/Entropy -func EntropyOfEachBin() []float64 { - var total = float64(nBits) //number of bits in the bin - var sum float64 //used to compute the entropy - allEntropy := make([]float64, 0, nBin) //used to calculate entropy of each bin +func (p *Peng) EntropyOfEachBin() []float64 { + var total = float64(p.Portbitmap.Config.NumberOfBits) //number of bits in the bin + var sum float64 //used to compute the entropy + allEntropy := make([]float64, 0, p.Portbitmap.Config.NumberOfBin) //used to calculate entropy of each bin - for i := 0; i < len(bitmap); i++ { - bitsAt1 := float64(bitmap[i].GetBitSets()) / total - bitsAt0 := float64(uint64(nBits)-bitmap[i].GetBitSets()) / total + for i := 0; i < len(p.Portbitmap.InnerBitmap); i++ { + bitsAt1 := float64(p.Portbitmap.InnerBitmap[i].GetBitSets()) / total + bitsAt0 := float64(uint64(p.Portbitmap.Config.NumberOfBits)-p.Portbitmap.InnerBitmap[i].GetBitSets()) / total if bitsAt1 > epsilon && bitsAt0 > epsilon { sum -= bitsAt1 * math.Log(bitsAt1) @@ -145,24 +123,24 @@ func EntropyOfEachBin() []float64 { return allEntropy } -func End() { - fmt.Println(bitmap) +func (p *Peng) end() { + fmt.Println(p.Portbitmap) fmt.Println("Bit set: ") - for i := 0; i < len(bitmap); i++ { - fmt.Println("pos [", i, "] num: ", bitmap[i].GetBitSets()) + for i := 0; i < len(p.Portbitmap.InnerBitmap); i++ { + fmt.Println("pos [", i, "] num: ", p.Portbitmap.InnerBitmap[i].GetBitSets()) } - binsEntropy := EntropyOfEachBin() - totalEntropy := EntropyTotal(binsEntropy) - PushToInfluxDb("server", totalEntropy, binsEntropy, time.Minute) //TODO generalizzare meglio + binsEntropy := p.EntropyOfEachBin() + totalEntropy := p.EntropyTotal(binsEntropy) + p.PushToInfluxDb("server", totalEntropy, binsEntropy, time.Minute) //TODO generalizzare meglio fmt.Println("EntropyOfEachBin: ", binsEntropy) fmt.Println("EntropyTotal: ", totalEntropy) - ResetBitmap() + p.Portbitmap.ClearAll() os.Exit(1) } -func GetMyIp() { +func getMyIp() { addrs, err := net.InterfaceAddrs() if err != nil { log.Fatal(err.Error()) @@ -178,7 +156,7 @@ func GetMyIp() { } } -func inspect(packet gopacket.Packet) { +func (p *Peng) inspect(packet gopacket.Packet) { var ipv4Layer gopacket.Layer //skip inspection if i can't obtain ip layer if ipv4Layer = packet.Layer(layers.LayerTypeIPv4); ipv4Layer == nil { return @@ -204,16 +182,19 @@ func inspect(packet gopacket.Packet) { if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { tcp, _ := tcpLayer.(*layers.TCP) if tcp.SYN { - InsertInBitmap(uint16(tcp.DstPort)) + err := p.Portbitmap.AddPort(uint16(tcp.DstPort)) + if err != nil { + log.Println(err.Error()) + } } } } //TODO generalizzare meglio -func PushToInfluxDb(typeName string, totalEntropy float64, binsEntropy []float64, interval time.Duration) { - client := influxdb2.NewClient(influxUrl+":"+fmt.Sprint(influxPort), influxAuthToken) +func (p *Peng) PushToInfluxDb(typeName string, totalEntropy float64, binsEntropy []float64, interval time.Duration) { + client := influxdb2.NewClient(p.Config.InfluxUrl+":"+fmt.Sprint(p.Config.InfluxPort), p.Config.InfluxAuthToken) defer client.Close() - writeApi := client.WriteApi(organization, bucket) //non-blocking + writeApi := client.WriteApi(p.Config.InfluxOrganization, p.Config.InfluxBucket) //non-blocking //Create fields to send to influx influxFields := make(map[string]interface{}, len(binsEntropy)+2) @@ -225,7 +206,7 @@ func PushToInfluxDb(typeName string, totalEntropy float64, binsEntropy []float64 influxFields["total_entropy"] = totalEntropy //Send point of system with hostname and values about in and out bits - p := influxdb2.NewPoint( + point := influxdb2.NewPoint( "entropy", map[string]string{ "type": typeName, @@ -233,8 +214,7 @@ func PushToInfluxDb(typeName string, totalEntropy float64, binsEntropy []float64 influxFields, time.Now()) - writeApi.WritePoint(p) + writeApi.WritePoint(point) writeApi.Flush() // Force all unwritten data to be sent - } diff --git a/pkg/portbitmap/portbitmap.go b/pkg/portbitmap/portbitmap.go new file mode 100644 index 0000000..e262fa1 --- /dev/null +++ b/pkg/portbitmap/portbitmap.go @@ -0,0 +1,55 @@ +package portbitmap + +import ( + "errors" + b "github.com/alessio-perugini/peng/pkg/bitmap" +) + +type PortBitmap struct { + Config *Config + InnerBitmap []b.Bitmap + HashFunc func(port uint16) (uint16, uint64) +} + +type Config struct { + NumberOfBin uint + SizeBitmap uint + NumberOfBits uint +} + +//TODO levare il puntatore a config +func New(cfg *Config) *PortBitmap { + var InnerBitmap = make([]b.Bitmap, cfg.NumberOfBin) + cfg.NumberOfBits = cfg.SizeBitmap / cfg.NumberOfBin + + for i := 0; i < int(cfg.NumberOfBin); i++ { + InnerBitmap[i] = *b.New(uint64(cfg.NumberOfBits)) + } + + var hashFunc = func(port uint16) (uint16, uint64) { + portModuled := port % uint16(cfg.SizeBitmap) + index, bit := portModuled/uint16(cfg.NumberOfBits), uint64(portModuled)%uint64(cfg.NumberOfBits) + return index, bit + } + + return &PortBitmap{ + InnerBitmap: InnerBitmap, + HashFunc: hashFunc, + Config: cfg, + } +} + +func (p *PortBitmap) AddPort(port uint16) error { + indexBin, bitBin := p.HashFunc(port) + if indexBin >= uint16(len(p.InnerBitmap)) { + return errors.New("index to access the bin is invalid") + } + p.InnerBitmap[indexBin].SetBit(bitBin, true) + return nil +} + +func (p *PortBitmap) ClearAll() { + for i := 0; i < len(p.InnerBitmap); i++ { + p.InnerBitmap[i].ResetAllBits() + } +}