315 lines
7.2 KiB
Go
315 lines
7.2 KiB
Go
// Licensed to Elasticsearch B.V. under one or more contributor
|
|
// license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright
|
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
|
// the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package beater
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/tsg/gopacket/layers"
|
|
|
|
"github.com/elastic/beats/libbeat/beat"
|
|
"github.com/elastic/beats/libbeat/common"
|
|
"github.com/elastic/beats/libbeat/logp"
|
|
"github.com/elastic/beats/libbeat/processors"
|
|
"github.com/elastic/beats/libbeat/service"
|
|
|
|
"github.com/elastic/beats/packetbeat/config"
|
|
"github.com/elastic/beats/packetbeat/decoder"
|
|
"github.com/elastic/beats/packetbeat/flows"
|
|
"github.com/elastic/beats/packetbeat/procs"
|
|
"github.com/elastic/beats/packetbeat/protos"
|
|
"github.com/elastic/beats/packetbeat/protos/icmp"
|
|
"github.com/elastic/beats/packetbeat/protos/tcp"
|
|
"github.com/elastic/beats/packetbeat/protos/udp"
|
|
"github.com/elastic/beats/packetbeat/publish"
|
|
"github.com/elastic/beats/packetbeat/sniffer"
|
|
|
|
// Add packetbeat default processors
|
|
_ "github.com/elastic/beats/packetbeat/processor/add_kubernetes_metadata"
|
|
)
|
|
|
|
// Beater object. Contains all objects needed to run the beat
|
|
type packetbeat struct {
|
|
config config.Config
|
|
cmdLineArgs flags
|
|
sniff *sniffer.Sniffer
|
|
|
|
// publisher/pipeline
|
|
pipeline beat.Pipeline
|
|
transPub *publish.TransactionPublisher
|
|
flows *flows.Flows
|
|
}
|
|
|
|
type flags struct {
|
|
file *string
|
|
loop *int
|
|
oneAtAtime *bool
|
|
topSpeed *bool
|
|
dumpfile *string
|
|
}
|
|
|
|
var cmdLineArgs flags
|
|
|
|
func init() {
|
|
cmdLineArgs = flags{
|
|
file: flag.String("I", "", "Read packet data from specified file"),
|
|
loop: flag.Int("l", 1, "Loop file. 0 - loop forever"),
|
|
oneAtAtime: flag.Bool("O", false, "Read packets one at a time (press Enter)"),
|
|
topSpeed: flag.Bool("t", false, "Read packets as fast as possible, without sleeping"),
|
|
dumpfile: flag.String("dump", "", "Write all captured packets to this libpcap file"),
|
|
}
|
|
}
|
|
|
|
func New(b *beat.Beat, rawConfig *common.Config) (beat.Beater, error) {
|
|
config := config.Config{
|
|
Interfaces: config.InterfacesConfig{
|
|
File: *cmdLineArgs.file,
|
|
Loop: *cmdLineArgs.loop,
|
|
TopSpeed: *cmdLineArgs.topSpeed,
|
|
OneAtATime: *cmdLineArgs.oneAtAtime,
|
|
Dumpfile: *cmdLineArgs.dumpfile,
|
|
},
|
|
}
|
|
err := rawConfig.Unpack(&config)
|
|
if err != nil {
|
|
logp.Err("fails to read the beat config: %v, %v", err, config)
|
|
return nil, err
|
|
}
|
|
|
|
pb := &packetbeat{
|
|
config: config,
|
|
cmdLineArgs: cmdLineArgs,
|
|
}
|
|
err = pb.init(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return pb, nil
|
|
}
|
|
|
|
// init packetbeat components
|
|
func (pb *packetbeat) init(b *beat.Beat) error {
|
|
var err error
|
|
cfg := &pb.config
|
|
// Enable the process watcher only if capturing live traffic
|
|
if cfg.Interfaces.File == "" {
|
|
err = procs.ProcWatcher.Init(cfg.Procs)
|
|
if err != nil {
|
|
logp.Critical(err.Error())
|
|
return err
|
|
}
|
|
} else {
|
|
logp.Info("Process watcher disabled when file input is used")
|
|
}
|
|
|
|
pb.pipeline = b.Publisher
|
|
pb.transPub, err = publish.NewTransactionPublisher(
|
|
b.Info.Name,
|
|
b.Publisher,
|
|
pb.config.IgnoreOutgoing,
|
|
pb.config.Interfaces.File == "",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logp.Debug("main", "Initializing protocol plugins")
|
|
err = protos.Protos.Init(false, pb.transPub, cfg.Protocols, cfg.ProtocolsList)
|
|
if err != nil {
|
|
return fmt.Errorf("Initializing protocol analyzers failed: %v", err)
|
|
}
|
|
|
|
if err := pb.setupFlows(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return pb.setupSniffer()
|
|
}
|
|
|
|
func (pb *packetbeat) setupSniffer() error {
|
|
config := &pb.config
|
|
|
|
icmp, err := pb.icmpConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
withVlans := config.Interfaces.WithVlans
|
|
withICMP := icmp.Enabled()
|
|
|
|
filter := config.Interfaces.BpfFilter
|
|
if filter == "" && !config.Flows.IsEnabled() {
|
|
filter = protos.Protos.BpfFilter(withVlans, withICMP)
|
|
}
|
|
|
|
pb.sniff, err = sniffer.New(false, filter, pb.createWorker, config.Interfaces)
|
|
return err
|
|
}
|
|
|
|
func (pb *packetbeat) setupFlows() error {
|
|
config := &pb.config
|
|
if !config.Flows.IsEnabled() {
|
|
return nil
|
|
}
|
|
|
|
processors, err := processors.New(config.Flows.Processors)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
client, err := pb.pipeline.ConnectWith(beat.ClientConfig{
|
|
EventMetadata: config.Flows.EventMetadata,
|
|
Processor: processors,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pb.flows, err = flows.NewFlows(client.PublishAll, config.Flows)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (pb *packetbeat) Run(b *beat.Beat) error {
|
|
defer func() {
|
|
if service.ProfileEnabled() {
|
|
logp.Debug("main", "Waiting for streams and transactions to expire...")
|
|
time.Sleep(time.Duration(float64(protos.DefaultTransactionExpiration) * 1.2))
|
|
logp.Debug("main", "Streams and transactions should all be expired now.")
|
|
}
|
|
}()
|
|
|
|
defer pb.transPub.Stop()
|
|
|
|
timeout := pb.config.ShutdownTimeout
|
|
if timeout > 0 {
|
|
defer time.Sleep(timeout)
|
|
}
|
|
|
|
if pb.flows != nil {
|
|
pb.flows.Start()
|
|
defer pb.flows.Stop()
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
errC := make(chan error, 1)
|
|
|
|
// Run the sniffer in background
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
err := pb.sniff.Run()
|
|
if err != nil {
|
|
errC <- fmt.Errorf("Sniffer main loop failed: %v", err)
|
|
}
|
|
}()
|
|
|
|
logp.Debug("main", "Waiting for the sniffer to finish")
|
|
wg.Wait()
|
|
select {
|
|
default:
|
|
case err := <-errC:
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Called by the Beat stop function
|
|
func (pb *packetbeat) Stop() {
|
|
logp.Info("Packetbeat send stop signal")
|
|
pb.sniff.Stop()
|
|
}
|
|
|
|
func (pb *packetbeat) createWorker(dl layers.LinkType) (sniffer.Worker, error) {
|
|
var icmp4 icmp.ICMPv4Processor
|
|
var icmp6 icmp.ICMPv6Processor
|
|
cfg, err := pb.icmpConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if cfg.Enabled() {
|
|
reporter, err := pb.transPub.CreateReporter(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
icmp, err := icmp.New(false, reporter, cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
icmp4 = icmp
|
|
icmp6 = icmp
|
|
}
|
|
|
|
tcp, err := tcp.NewTCP(&protos.Protos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
udp, err := udp.NewUDP(&protos.Protos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
worker, err := decoder.New(pb.flows, dl, icmp4, icmp6, tcp, udp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return worker, nil
|
|
}
|
|
|
|
func (pb *packetbeat) icmpConfig() (*common.Config, error) {
|
|
var icmp *common.Config
|
|
if pb.config.Protocols["icmp"].Enabled() {
|
|
icmp = pb.config.Protocols["icmp"]
|
|
}
|
|
|
|
for _, cfg := range pb.config.ProtocolsList {
|
|
info := struct {
|
|
Type string `config:"type" validate:"required"`
|
|
}{}
|
|
|
|
if err := cfg.Unpack(&info); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if info.Type != "icmp" {
|
|
continue
|
|
}
|
|
|
|
if icmp != nil {
|
|
return nil, errors.New("More then one icmp configurations found")
|
|
}
|
|
|
|
icmp = cfg
|
|
}
|
|
|
|
return icmp, nil
|
|
}
|