156 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
package unifi
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"sort"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
// IDS holds an Intrusion Prevention System Event.
 | 
						|
type IDS struct {
 | 
						|
	Archived              FlexBool  `json:"archived"`
 | 
						|
	DestPort              int       `json:"dest_port,omitempty"`
 | 
						|
	SrcPort               int       `json:"src_port,omitempty"`
 | 
						|
	FlowID                int64     `json:"flow_id"`
 | 
						|
	InnerAlertRev         int64     `json:"inner_alert_rev"`
 | 
						|
	InnerAlertSeverity    int64     `json:"inner_alert_severity"`
 | 
						|
	InnerAlertGID         int64     `json:"inner_alert_gid"`
 | 
						|
	InnerAlertSignatureID int64     `json:"inner_alert_signature_id"`
 | 
						|
	Time                  int64     `json:"time"`
 | 
						|
	Timestamp             int64     `json:"timestamp"`
 | 
						|
	Datetime              time.Time `json:"datetime"`
 | 
						|
	AppProto              string    `json:"app_proto,omitempty"`
 | 
						|
	Catname               string    `json:"catname"`
 | 
						|
	DestIP                string    `json:"dest_ip"`
 | 
						|
	DstMAC                string    `json:"dst_mac"`
 | 
						|
	DstIPASN              string    `json:"dstipASN"`
 | 
						|
	DstIPCountry          string    `json:"dstipCountry"`
 | 
						|
	EventType             string    `json:"event_type"`
 | 
						|
	Host                  string    `json:"host"`
 | 
						|
	ID                    string    `json:"_id"`
 | 
						|
	InIface               string    `json:"in_iface"`
 | 
						|
	InnerAlertAction      string    `json:"inner_alert_action"`
 | 
						|
	InnerAlertCategory    string    `json:"inner_alert_category"`
 | 
						|
	InnerAlertSignature   string    `json:"inner_alert_signature"`
 | 
						|
	Key                   string    `json:"key"`
 | 
						|
	Msg                   string    `json:"msg"`
 | 
						|
	Proto                 string    `json:"proto"`
 | 
						|
	SiteID                string    `json:"site_id"`
 | 
						|
	SiteName              string    `json:"-"`
 | 
						|
	SourceName            string    `json:"-"`
 | 
						|
	SrcIP                 string    `json:"src_ip"`
 | 
						|
	SrcIPASN              string    `json:"srcipASN"`
 | 
						|
	SrcIPCountry          string    `json:"srcipCountry"`
 | 
						|
	SrcMAC                string    `json:"src_mac"`
 | 
						|
	Subsystem             string    `json:"subsystem"`
 | 
						|
	UniqueAlertID         string    `json:"unique_alertid"`
 | 
						|
	USGIP                 string    `json:"usgip"`
 | 
						|
	USGIPASN              string    `json:"usgipASN"`
 | 
						|
	USGIPCountry          string    `json:"usgipCountry"`
 | 
						|
	DestIPGeo             IPGeo     `json:"dstipGeo"`
 | 
						|
	SourceIPGeo           IPGeo     `json:"srcipGeo"`
 | 
						|
	USGIPGeo              IPGeo     `json:"usgipGeo"`
 | 
						|
}
 | 
						|
 | 
						|
// GetIDS returns Intrusion Detection Systems events for a list of Sites.
 | 
						|
// timeRange may have a length of 0, 1 or 2. The first time is Start, the second is End.
 | 
						|
// Events between start and end are returned. End defaults to time.Now().
 | 
						|
func (u *Unifi) GetIDS(sites []*Site, timeRange ...time.Time) ([]*IDS, error) {
 | 
						|
	data := []*IDS{}
 | 
						|
 | 
						|
	for _, site := range sites {
 | 
						|
		response, err := u.GetIDSSite(site, timeRange...)
 | 
						|
		if err != nil {
 | 
						|
			return data, err
 | 
						|
		}
 | 
						|
 | 
						|
		data = append(data, response...)
 | 
						|
	}
 | 
						|
 | 
						|
	return data, nil
 | 
						|
}
 | 
						|
 | 
						|
// GetIDSSite retreives the Intrusion Detection System Data for a single Site.
 | 
						|
// timeRange may have a length of 0, 1 or 2. The first time is Start, the second is End.
 | 
						|
// Events between start and end are returned. End defaults to time.Now().
 | 
						|
func (u *Unifi) GetIDSSite(site *Site, timeRange ...time.Time) ([]*IDS, error) {
 | 
						|
	if site == nil || site.Name == "" {
 | 
						|
		return nil, ErrNoSiteProvided
 | 
						|
	}
 | 
						|
 | 
						|
	u.DebugLog("Polling Controller for IDS Events, site %s (%s)", site.Name, site.Desc)
 | 
						|
 | 
						|
	var (
 | 
						|
		path = fmt.Sprintf(APIEventPathIDS, site.Name)
 | 
						|
		ids  struct {
 | 
						|
			Data idsList `json:"data"`
 | 
						|
		}
 | 
						|
	)
 | 
						|
 | 
						|
	if params, err := makeEventParams(timeRange...); err != nil {
 | 
						|
		return ids.Data, err
 | 
						|
	} else if err = u.GetData(path, &ids, params); err != nil {
 | 
						|
		return ids.Data, err
 | 
						|
	}
 | 
						|
 | 
						|
	for i := range ids.Data {
 | 
						|
		// Add special SourceName value.
 | 
						|
		ids.Data[i].SourceName = u.URL
 | 
						|
		// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
 | 
						|
		ids.Data[i].SiteName = site.Desc + " (" + site.Name + ")"
 | 
						|
	}
 | 
						|
 | 
						|
	sort.Sort(ids.Data)
 | 
						|
 | 
						|
	return ids.Data, nil
 | 
						|
}
 | 
						|
 | 
						|
func makeEventParams(timeRange ...time.Time) (string, error) {
 | 
						|
	type eventReq struct {
 | 
						|
		Start int64  `json:"start,omitempty"`
 | 
						|
		End   int64  `json:"end,omitempty"`
 | 
						|
		Limit int    `json:"_limit,omitempty"`
 | 
						|
		Sort  string `json:"_sort"`
 | 
						|
	}
 | 
						|
 | 
						|
	rp := eventReq{Limit: eventLimit, Sort: "-time"}
 | 
						|
 | 
						|
	switch len(timeRange) {
 | 
						|
	case 0:
 | 
						|
		rp.End = time.Now().Unix() * int64(time.Microsecond)
 | 
						|
	case 1:
 | 
						|
		rp.Start = timeRange[0].Unix() * int64(time.Microsecond)
 | 
						|
		rp.End = time.Now().Unix() * int64(time.Microsecond)
 | 
						|
	case 2: // nolint: gomnd
 | 
						|
		rp.Start = timeRange[0].Unix() * int64(time.Microsecond)
 | 
						|
		rp.End = timeRange[1].Unix() * int64(time.Microsecond)
 | 
						|
	default:
 | 
						|
		return "", ErrInvalidTimeRange
 | 
						|
	}
 | 
						|
 | 
						|
	params, err := json.Marshal(&rp)
 | 
						|
	if err != nil {
 | 
						|
		return "", fmt.Errorf("json marshal: %w", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return string(params), nil
 | 
						|
}
 | 
						|
 | 
						|
type idsList []*IDS
 | 
						|
 | 
						|
// Len satisfies sort.Interface.
 | 
						|
func (e idsList) Len() int {
 | 
						|
	return len(e)
 | 
						|
}
 | 
						|
 | 
						|
// Swap satisfies sort.Interface.
 | 
						|
func (e idsList) Swap(i, j int) {
 | 
						|
	e[i], e[j] = e[j], e[i]
 | 
						|
}
 | 
						|
 | 
						|
// Less satisfies sort.Interface. Sort our list by Datetime.
 | 
						|
func (e idsList) Less(i, j int) bool {
 | 
						|
	return e[i].Datetime.Before(e[j].Datetime)
 | 
						|
}
 |