database
This commit is contained in:
		
							parent
							
								
									39ff9a43b0
								
							
						
					
					
						commit
						3b98fb36cb
					
				|  | @ -51,17 +51,17 @@ func (msg *SocketMSG) Marshal() ([]byte, error) { | |||
| 	pos += 4 | ||||
| 
 | ||||
| 	if msg.Types.Is(SocketMSGTypeClient) { | ||||
| 		obj[pos] = msg.Client.Addr.HardwareAddr[0] | ||||
| 		obj[pos] = msg.Client.Addr[0] | ||||
| 		pos++ | ||||
| 		obj[pos] = msg.Client.Addr.HardwareAddr[1] | ||||
| 		obj[pos] = msg.Client.Addr[1] | ||||
| 		pos++ | ||||
| 		obj[pos] = msg.Client.Addr.HardwareAddr[2] | ||||
| 		obj[pos] = msg.Client.Addr[2] | ||||
| 		pos++ | ||||
| 		obj[pos] = msg.Client.Addr.HardwareAddr[3] | ||||
| 		obj[pos] = msg.Client.Addr[3] | ||||
| 		pos++ | ||||
| 		obj[pos] = msg.Client.Addr.HardwareAddr[4] | ||||
| 		obj[pos] = msg.Client.Addr[4] | ||||
| 		pos++ | ||||
| 		obj[pos] = msg.Client.Addr.HardwareAddr[5] | ||||
| 		obj[pos] = msg.Client.Addr[5] | ||||
| 		pos++ | ||||
| 		binary.BigEndian.PutUint32(obj[pos:(pos+4)], uint32(msg.Client.Time.Unix())) | ||||
| 		pos += 4 | ||||
|  | @ -100,7 +100,7 @@ func (msg *SocketMSG) Unmarshal(obj []byte) error { | |||
| 
 | ||||
| 	if msg.Types.Is(SocketMSGTypeClient) { | ||||
| 		msg.Client = &WifiClient{ | ||||
| 			Addr:           data.HardwareAddr{HardwareAddr: obj[pos:(pos + 6)]}, | ||||
| 			Addr:           data.HardwareAddr(obj[pos:(pos + 6)]), | ||||
| 			Time:           time.Unix(int64(binary.BigEndian.Uint32(obj[(pos+6):(pos+10)])), 0), | ||||
| 			TryProbe:       binary.BigEndian.Uint16(obj[(pos + 10):(pos + 12)]), | ||||
| 			TryAuth:        binary.BigEndian.Uint16(obj[(pos + 12):(pos + 14)]), | ||||
|  |  | |||
|  | @ -6,18 +6,18 @@ import ( | |||
| 	"os/signal" | ||||
| 	"syscall" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/golang-lib/database" | ||||
| 	"dev.sum7.eu/genofire/golang-lib/file" | ||||
| 	"github.com/bdlm/log" | ||||
| 	"github.com/spf13/cobra" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/capture" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/controller" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/database" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/web" | ||||
| ) | ||||
| 
 | ||||
| type ControllerConfig struct { | ||||
| 	Database   string                 `toml:"database"` | ||||
| 	Database   database.Config        `toml:"database"` | ||||
| 	Answer     bool                   `toml:"answer"` | ||||
| 	Webserver  *web.Config            `toml:"webserver"` | ||||
| 	Interfaces []*capture.IFaceConfig `toml:"interfaces"` | ||||
|  | @ -34,9 +34,12 @@ var controllerCmd = &cobra.Command{ | |||
| 
 | ||||
| 		file.ReadTOML(args[0], config) | ||||
| 
 | ||||
| 		db := database.NewDB(config.Database) | ||||
| 		if err := database.Open(config.Database); err != nil { | ||||
| 			log.Panicf("no database connection: %s", err) | ||||
| 		} | ||||
| 		defer database.Close() | ||||
| 
 | ||||
| 		ctr := controller.NewController(db) | ||||
| 		ctr := controller.NewController() | ||||
| 		defer ctr.Close() | ||||
| 
 | ||||
| 		var handlers []capture.Handler | ||||
|  |  | |||
|  | @ -1,6 +1,11 @@ | |||
| database = "/tmp/wifictld.json" | ||||
| answer = false | ||||
| 
 | ||||
| [database] | ||||
| type = "sqlite3" | ||||
| logging = true | ||||
| connection = "file:/tmp/wifictld.db" | ||||
| # For Master-Slave cluster | ||||
| # read_connection = "" | ||||
| 
 | ||||
| [webserver] | ||||
| enable  = true | ||||
|  |  | |||
|  | @ -2,24 +2,38 @@ package controller | |||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
| 	"time" | ||||
| 
 | ||||
| 	// "github.com/bdlm/log"
 | ||||
| 	"dev.sum7.eu/genofire/golang-lib/database" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/capture" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/data" | ||||
| ) | ||||
| 
 | ||||
| func (c *Controller) Handler(addr *net.UDPAddr, msg *capture.SocketMSG) (*capture.SocketMSG, error) { | ||||
| 	ignore := false | ||||
| 	if msg.Types.Is(capture.SocketMSGTypeClient) && msg.Client != nil { | ||||
| 		ignore = c.db.LearnClient(addr.IP, msg.Client) | ||||
| 		ignore = c.LearnClient(addr.IP, msg.Client) | ||||
| 	} | ||||
| 	if !msg.Types.Is(capture.SocketMSGTypeRequest) { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 
 | ||||
| 	client := &data.Client{Addr: msg.Client.Addr} | ||||
| 
 | ||||
| 	if result := database.Read.Select([]string{"try_probe", "try_auth"}).First(client); result.Error != nil { | ||||
| 		return nil, result.Error | ||||
| 	} | ||||
| 
 | ||||
| 	msg = &capture.SocketMSG{ | ||||
| 		Types: (capture.SocketMSGTypeResponse | capture.SocketMSGTypeClient), | ||||
| 		Client: c.db.GetClient(msg.Client.Addr), | ||||
| 		Client: &capture.WifiClient{ | ||||
| 			Addr:     msg.Client.Addr, | ||||
| 			Time:     time.Now(), | ||||
| 			TryProbe: client.TryProbe, | ||||
| 			TryAuth:  client.TryAuth, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	if !ignore { | ||||
|  |  | |||
|  | @ -0,0 +1,57 @@ | |||
| package controller | ||||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
| 	"time" | ||||
| 
 | ||||
| 	// "github.com/bdlm/log"
 | ||||
| 	"dev.sum7.eu/genofire/golang-lib/database" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/capture" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/data" | ||||
| ) | ||||
| 
 | ||||
| func (c *Controller) LearnClient(apIP net.IP, clientWifictl *capture.WifiClient) bool { | ||||
| 	ret := false | ||||
| 
 | ||||
| 	// learn ap
 | ||||
| 	ap := &data.AP{ | ||||
| 		IP:       apIP, | ||||
| 		Lastseen: time.Now(), | ||||
| 	} | ||||
| 	result := database.Read.First(ap) | ||||
| 	if result.RowsAffected > 0 { | ||||
| 		database.Write.Save(ap) | ||||
| 	} else { | ||||
| 		database.Write.Create(ap) | ||||
| 	} | ||||
| 
 | ||||
| 	// learn client
 | ||||
| 	client := &data.Client{ | ||||
| 		Addr:           clientWifictl.Addr, | ||||
| 		Lastseen:       time.Now(), | ||||
| 		APAddr:         apIP, | ||||
| 		Connected:      clientWifictl.Connected, | ||||
| 		SignalLowFreq:  clientWifictl.SignalLowFreq, | ||||
| 		SignalHighFreq: clientWifictl.SignalHighFreq, | ||||
| 	} | ||||
| 
 | ||||
| 	database.Write.FirstOrCreate(client) | ||||
| 
 | ||||
| 	if clientWifictl.TryAuth > client.TryAuth { | ||||
| 		client.TryAuth = clientWifictl.TryAuth | ||||
| 	} | ||||
| 	if clientWifictl.TryProbe > client.TryProbe { | ||||
| 		client.TryProbe = clientWifictl.TryProbe | ||||
| 	} | ||||
| 
 | ||||
| 	if client.FreqHighest < clientWifictl.FreqHighest { | ||||
| 		ret = (client.FreqHighest != 0) | ||||
| 		client.FreqHighest = clientWifictl.FreqHighest | ||||
| 	} | ||||
| 	if clientWifictl.Authed { | ||||
| 		client.Authed = clientWifictl.Authed | ||||
| 	} | ||||
| 	database.Write.Save(client) | ||||
| 	return ret | ||||
| } | ||||
|  | @ -4,23 +4,22 @@ import ( | |||
| 	"net" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/golang-lib/database" | ||||
| 	"github.com/bdlm/log" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/capture" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/database" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/data" | ||||
| ) | ||||
| 
 | ||||
| type Controller struct { | ||||
| 	SendTo func(addr *net.UDPAddr, msg *capture.SocketMSG) | ||||
| 	Send   func(msg *capture.SocketMSG) | ||||
| 	db     *database.DB | ||||
| 	ticker *time.Ticker | ||||
| } | ||||
| 
 | ||||
| func NewController(db *database.DB) *Controller { | ||||
| func NewController() *Controller { | ||||
| 	ctl := &Controller{ | ||||
| 		ticker: time.NewTicker(time.Minute), | ||||
| 		db:     db, | ||||
| 	} | ||||
| 	go ctl.Repeated() | ||||
| 	return ctl | ||||
|  | @ -31,7 +30,12 @@ func (c *Controller) Close() { | |||
| } | ||||
| 
 | ||||
| func (c *Controller) Repeated() { | ||||
| 	aps := 0 | ||||
| 	clients := 0 | ||||
| 
 | ||||
| 	for range c.ticker.C { | ||||
| 		log.Debugf("lerned: %d APs, %d Clients", len(c.db.APs), len(c.db.Clients)) | ||||
| 		database.Read.Model(&data.AP{}).Count(&aps) | ||||
| 		database.Read.Model(&data.Client{}).Count(&clients) | ||||
| 		log.Debugf("learned: %d APs, %d Clients", aps, clients) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,20 @@ | |||
| package data | ||||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
| 
 | ||||
| 	"time" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/golang-lib/database" | ||||
| ) | ||||
| 
 | ||||
| type AP struct { | ||||
| 	IP       net.IP    `json:"ip" gorm:"PRIMARY_KEY"` | ||||
| 	Lastseen time.Time `json:"lastseen"` | ||||
| 	Clients  []Client  `gorm:"foreignkey:APAddr" json:"-"` | ||||
| } | ||||
| 
 | ||||
| // Function to initialize the database
 | ||||
| func init() { | ||||
| 	database.AddModel(&AP{}) | ||||
| } | ||||
|  | @ -0,0 +1,26 @@ | |||
| package data | ||||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/golang-lib/database" | ||||
| ) | ||||
| 
 | ||||
| type Client struct { | ||||
| 	Addr           HardwareAddr `gorm:"PRIMARY_KEY" json:"addr"` | ||||
| 	APAddr         net.IP       `gorm:"column:ap" json:"ap"` | ||||
| 	TryProbe       uint16       `json:"try_probe"` | ||||
| 	TryAuth        uint16       `json:"try_auth"` | ||||
| 	Connected      bool         `json:"connected"` | ||||
| 	Authed         bool         `json:"authed"` | ||||
| 	FreqHighest    uint16       `json:"freq_highest"` | ||||
| 	SignalLowFreq  int16        `json:"signal_low_freq"` | ||||
| 	SignalHighFreq int16        `json:"signal_high_freq"` | ||||
| 	Lastseen       time.Time    `json:"lastseen"` | ||||
| } | ||||
| 
 | ||||
| // Function to initialize the database
 | ||||
| func init() { | ||||
| 	database.AddModel(&Client{}) | ||||
| } | ||||
|  | @ -2,7 +2,12 @@ package data | |||
| 
 | ||||
| import "net" | ||||
| 
 | ||||
| type HardwareAddr struct{ net.HardwareAddr } | ||||
| type HardwareAddr net.HardwareAddr | ||||
| 
 | ||||
| //MarshalJSON to bytearray
 | ||||
| func (a HardwareAddr) String() string { | ||||
| 	return net.HardwareAddr(a).String() | ||||
| } | ||||
| 
 | ||||
| //MarshalJSON to bytearray
 | ||||
| func (a HardwareAddr) MarshalText() ([]byte, error) { | ||||
|  | @ -10,7 +15,11 @@ func (a HardwareAddr) MarshalText() ([]byte, error) { | |||
| } | ||||
| 
 | ||||
| // UnmarshalJSON from bytearray
 | ||||
| func (a HardwareAddr) UnmarshalText(data []byte) (err error) { | ||||
| 	a.HardwareAddr, err = net.ParseMAC(string(data)) | ||||
| 	return | ||||
| func (a HardwareAddr) UnmarshalText(data []byte) error { | ||||
| 	b, err := net.ParseMAC(string(data)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	a = HardwareAddr(b) | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -1,22 +0,0 @@ | |||
| package database | ||||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
| 
 | ||||
| 	"github.com/FreifunkBremen/yanic/lib/jsontime" | ||||
| ) | ||||
| 
 | ||||
| type AP struct { | ||||
| 	IP       *net.IP       `json:"ip"` | ||||
| 	Lastseen jsontime.Time `json:"lastseen"` | ||||
| } | ||||
| 
 | ||||
| func (db *DB) GetClients(ap *AP) []*Client { | ||||
| 	var clients []*Client | ||||
| 	for _, client := range db.Clients { | ||||
| 		if client.AP == ap { | ||||
| 			clients = append(clients, client) | ||||
| 		} | ||||
| 	} | ||||
| 	return clients | ||||
| } | ||||
|  | @ -1,84 +0,0 @@ | |||
| package database | ||||
| 
 | ||||
| import ( | ||||
| 	"net" | ||||
| 	"time" | ||||
| 
 | ||||
| 	// "github.com/bdlm/log"
 | ||||
| 	"github.com/FreifunkBremen/yanic/lib/jsontime" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/capture" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/data" | ||||
| ) | ||||
| 
 | ||||
| type Client struct { | ||||
| 	AP             *AP               `json:"-"` | ||||
| 	APAddr         string            `json:"ap"` | ||||
| 	Addr           data.HardwareAddr `json:"-"` | ||||
| 	TryProbe       uint16            `json:"try_probe"` | ||||
| 	TryAuth        uint16            `json:"try_auth"` | ||||
| 	Connected      bool              `json:"connected"` | ||||
| 	Authed         bool              `json:"authed"` | ||||
| 	FreqHighest    uint16            `json:"freq_highest"` | ||||
| 	SignalLowFreq  int16             `json:"signal_low_freq"` | ||||
| 	SignalHighFreq int16             `json:"signal_high_freq"` | ||||
| 	Lastseen       jsontime.Time     `json:"lastseen"` | ||||
| } | ||||
| 
 | ||||
| func (db *DB) LearnClient(apIP net.IP, clientWifictl *capture.WifiClient) bool { | ||||
| 	ret := false | ||||
| 
 | ||||
| 	// learn ap
 | ||||
| 	apAddr := apIP.String() | ||||
| 	ap, ok := db.APs[apAddr] | ||||
| 	if !ok { | ||||
| 		ap = &AP{} | ||||
| 		db.APs[apAddr] = ap | ||||
| 	} | ||||
| 	ap.IP = &apIP | ||||
| 	ap.Lastseen = jsontime.Now() | ||||
| 
 | ||||
| 	// learn client
 | ||||
| 	clientAddr := clientWifictl.Addr.String() | ||||
| 	client, ok := db.Clients[clientAddr] | ||||
| 	if !ok { | ||||
| 		client = &Client{ | ||||
| 			Addr: clientWifictl.Addr, | ||||
| 		} | ||||
| 		db.Clients[clientAddr] = client | ||||
| 	} | ||||
| 	client.Lastseen = jsontime.Now() | ||||
| 	client.AP = ap | ||||
| 	client.APAddr = apAddr | ||||
| 	client.Connected = clientWifictl.Connected | ||||
| 	client.SignalLowFreq = clientWifictl.SignalLowFreq | ||||
| 	client.SignalHighFreq = clientWifictl.SignalHighFreq | ||||
| 
 | ||||
| 	if clientWifictl.TryAuth > client.TryAuth { | ||||
| 		client.TryAuth = clientWifictl.TryAuth | ||||
| 	} | ||||
| 	if clientWifictl.TryProbe > client.TryProbe { | ||||
| 		client.TryProbe = clientWifictl.TryProbe | ||||
| 	} | ||||
| 
 | ||||
| 	if client.FreqHighest < clientWifictl.FreqHighest { | ||||
| 		ret = (client.FreqHighest != 0) | ||||
| 		client.FreqHighest = clientWifictl.FreqHighest | ||||
| 	} | ||||
| 	if clientWifictl.Authed { | ||||
| 		client.Authed = clientWifictl.Authed | ||||
| 	} | ||||
| 	return ret | ||||
| } | ||||
| 
 | ||||
| func (db *DB) GetClient(addr data.HardwareAddr) *capture.WifiClient { | ||||
| 	client, ok := db.Clients[addr.String()] | ||||
| 	wClient := &capture.WifiClient{ | ||||
| 		Addr: addr, | ||||
| 		Time: time.Now(), | ||||
| 	} | ||||
| 	if ok { | ||||
| 		wClient.TryProbe = client.TryProbe | ||||
| 	} | ||||
| 	return wClient | ||||
| } | ||||
|  | @ -1,47 +0,0 @@ | |||
| package database | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/bdlm/log" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/golang-lib/file" | ||||
| 	"dev.sum7.eu/genofire/golang-lib/worker" | ||||
| ) | ||||
| 
 | ||||
| type DB struct { | ||||
| 	Clients map[string]*Client `json:"client"` | ||||
| 	APs     map[string]*AP     `json:"ap"` | ||||
| 	worker  *worker.Worker | ||||
| } | ||||
| 
 | ||||
| func NewDB(path string) *DB { | ||||
| 	db := &DB{ | ||||
| 		Clients: make(map[string]*Client), | ||||
| 		APs:     make(map[string]*AP), | ||||
| 	} | ||||
| 
 | ||||
| 	file.ReadJSON(path, db) | ||||
| 
 | ||||
| 	for addr, client := range db.Clients { | ||||
| 		client.Addr.UnmarshalText([]byte(addr)) | ||||
| 		if ap, ok := db.APs[client.APAddr]; ok { | ||||
| 			client.AP = ap | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	db.worker = worker.NewWorker(time.Minute, func() { | ||||
| 		file.SaveJSON(path, db) | ||||
| 		log.Debug("save db state") | ||||
| 	}) | ||||
| 
 | ||||
| 	db.worker.Start() | ||||
| 
 | ||||
| 	return db | ||||
| } | ||||
| 
 | ||||
| func (db *DB) Close() { | ||||
| 	if db.worker != nil { | ||||
| 		db.worker.Close() | ||||
| 	} | ||||
| } | ||||
|  | @ -1 +0,0 @@ | |||
| package database | ||||
|  | @ -7,9 +7,12 @@ import ( | |||
| 	"github.com/NYTimes/gziphandler" | ||||
| 	"github.com/bdlm/log" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/golang-lib/database" | ||||
| 	lib "dev.sum7.eu/genofire/golang-lib/http" | ||||
| 	"dev.sum7.eu/genofire/golang-lib/websocket" | ||||
| 
 | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/capture" | ||||
| 	"dev.sum7.eu/genofire/wifictld-analyzer/data" | ||||
| ) | ||||
| 
 | ||||
| type Server struct { | ||||
|  | @ -22,6 +25,26 @@ func New(config *Config) *Server { | |||
| 	ws := websocket.NewWebsocketHandlerService() | ||||
| 	ws.Listen("/ws") | ||||
| 
 | ||||
| 	http.HandleFunc("/data.json", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		type dataResponse struct { | ||||
| 			APs     []data.AP     `json:"aps"` | ||||
| 			Clients []data.Client `json:"clients"` | ||||
| 		} | ||||
| 		data := &dataResponse{} | ||||
| 		if result := database.Read.Find(&data.APs); result.Error != nil { | ||||
| 			log.WithField("error", result.Error.Error()).Warn("not possible to read APs") | ||||
| 			http.Error(w, "not possible to read APs", http.StatusNotFound) | ||||
| 			return | ||||
| 		} | ||||
| 		if result := database.Read.Find(&data.Clients); result.Error != nil { | ||||
| 			log.WithField("error", result.Error.Error()).Warn("not possible to read Clients") | ||||
| 			http.Error(w, "not possible to read Clients", http.StatusNotFound) | ||||
| 			return | ||||
| 		} | ||||
| 		lib.Write(w, data) | ||||
| 		log.Info("fetch data") | ||||
| 	}) | ||||
| 
 | ||||
| 	http.Handle("/", gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webroot)))) | ||||
| 	return &Server{ | ||||
| 		web: &http.Server{ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue