Initial commit: Import from /home/simon/test/ac
This commit is contained in:
1646
src/internal/mqtt/message/message.pb.go
Normal file
1646
src/internal/mqtt/message/message.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
190
src/internal/mqtt/message/message.proto
Normal file
190
src/internal/mqtt/message/message.proto
Normal file
@@ -0,0 +1,190 @@
|
||||
syntax = "proto2";
|
||||
|
||||
option go_package = "./message";
|
||||
|
||||
enum APMode {
|
||||
FIT_AP = 1;
|
||||
FAT_AP = 2; /* soho */
|
||||
CPE_BASE = 3; /* CPE 基站*/
|
||||
CPE_STA = 4; /* CPE 接收端*/
|
||||
}
|
||||
|
||||
enum RadioBand {
|
||||
RB_2G = 1;
|
||||
RB_5G = 2;
|
||||
}
|
||||
|
||||
enum RadioHTMode {
|
||||
RHT_20 = 1;
|
||||
RHT_40 = 2;
|
||||
RHT_40Minus = 3;
|
||||
RHT_40Plus = 4;
|
||||
RHT_80 = 5;
|
||||
RHT_160 = 6;
|
||||
RHT_160Plus = 7;
|
||||
}
|
||||
|
||||
enum CMDType {
|
||||
REBOOT = 1;
|
||||
UPGRADE = 2;
|
||||
SETACADDR = 4;
|
||||
/* 解绑AP 和AC, AP 和AC 的绑定关系由配置下发时确定*/
|
||||
UNBIND = 5;
|
||||
/* 下线用户支持*/
|
||||
KICK_USER = 6;
|
||||
/* 配置设备名称*/
|
||||
SETHOSTNAME = 7;
|
||||
/* 没有绑定的时候要求AP 断开和AC 的连接,重启查询AC 的流程*/
|
||||
KICK_AP = 8;
|
||||
/* 启动扫描*/
|
||||
START_SCAN = 9;
|
||||
/* 下发认证*/
|
||||
AUTH = 10;
|
||||
/* 下线用户*/
|
||||
LOGOUT = 11;
|
||||
/* 下线用户*/
|
||||
REBOOTAP = 12;
|
||||
|
||||
SHELL = 100;
|
||||
}
|
||||
|
||||
message WanConfig {
|
||||
required string ipproto = 1;
|
||||
optional string ip = 2;
|
||||
optional string netmask = 3;
|
||||
optional int32 metric = 4;
|
||||
optional string gateway = 5;
|
||||
optional string dns1 = 6;
|
||||
optional string dns2 = 7;
|
||||
}
|
||||
|
||||
message WlanConfig
|
||||
{
|
||||
required RadioBand band = 1;
|
||||
required string ssid = 2;
|
||||
required int32 gbk_enable = 3 [default = 0];
|
||||
required string encryption = 4;
|
||||
optional string key = 5;
|
||||
required int32 disabled = 6 [default = 0];
|
||||
|
||||
required int32 vlan = 10 [default = 0];
|
||||
required int32 maxsta = 11 [default = 0];
|
||||
required int32 rejrssi = 12 [default = -85];
|
||||
required int32 wmm = 13 [default = 1];
|
||||
required int32 isolate = 14 [default = 0];
|
||||
required int32 hide = 15 [default = 0];
|
||||
required int32 ieee80211r = 22 [default = 0];
|
||||
optional int32 auth_type= 25; /* 认证类型,将认证方式和ssid 关联*/
|
||||
}
|
||||
|
||||
message PingWatchdog {
|
||||
required int32 enable = 1;
|
||||
optional string target = 2;
|
||||
optional int32 ping_interval = 3;
|
||||
optional int32 ping_failures = 4;
|
||||
optional int32 ping_timeout = 5;
|
||||
optional int32 ping_watchdog_action = 6 [default = 3];
|
||||
}
|
||||
|
||||
message CMD {
|
||||
required CMDType type = 1;
|
||||
optional string args = 2;
|
||||
}
|
||||
|
||||
message Echo {
|
||||
required string sn = 1;
|
||||
required string product_name = 2;
|
||||
/* 标识设备的唯一MAC */
|
||||
optional string mac = 3;
|
||||
optional string board = 4;
|
||||
optional string hostname = 5;
|
||||
/* 运行时间*/
|
||||
optional string uptime = 6;
|
||||
optional uint64 uptime_sec = 61;
|
||||
|
||||
optional string version = 7;
|
||||
required APMode apmode = 8 [default = FIT_AP];
|
||||
|
||||
/* 是否是第一次连接(要求下发配置) */
|
||||
required int32 newconnect = 9 [default = 0];
|
||||
|
||||
/* 关键配置的MD5 值*/
|
||||
optional string apnetwork_md5 = 10;
|
||||
|
||||
optional string country = 11;
|
||||
|
||||
/* CPU 占用率*/
|
||||
optional string cpu = 12;
|
||||
|
||||
/* 是否云端管理的*/
|
||||
optional int32 is_on_cloud = 13 [default = 0];
|
||||
optional string username = 14;
|
||||
/* 通过设备的上下行总流量统计*/
|
||||
optional uint64 uploadspeed = 21;
|
||||
optional uint64 downloadspeed = 22;
|
||||
optional uint64 uploadbytes = 23;
|
||||
optional uint64 downloadbytes = 24;
|
||||
|
||||
required string acaddr = 25;
|
||||
|
||||
required ManageInterface mif = 50; /* 接口信息*/
|
||||
repeated RadioInfo radioinfo = 51; /* 射频信息*/
|
||||
optional WanConfig lanconfig = 52; /* lan 口配置*/
|
||||
optional PingWatchdog pingwatchdog = 53; /* Ping 看门狗*/
|
||||
repeated WlanConfig ssids = 54; /* ssid 配置*/
|
||||
|
||||
optional int32 iptvSupport = 70;
|
||||
optional int32 iptvEnable = 71;
|
||||
|
||||
/* 假设所有的CPE 都是单频的,单频2.4 或单频5G */
|
||||
/* 增加一个CPE 专用的信息上报,信道列表*/
|
||||
optional string cpeChannelsJson = 100;
|
||||
}
|
||||
|
||||
message StaInfo {
|
||||
required string mac = 2;
|
||||
optional string ip = 3;
|
||||
required int32 signal = 4;
|
||||
required int32 noise = 5;
|
||||
required int32 snr = 6;
|
||||
required string txrate = 7;
|
||||
required string rxrate = 8;
|
||||
}
|
||||
|
||||
message WlanInfo {
|
||||
optional string ssid = 1;
|
||||
repeated StaInfo stas = 2;
|
||||
}
|
||||
|
||||
message RadioInfo {
|
||||
required RadioBand band = 1;
|
||||
repeated WlanInfo wlaninfo = 2;
|
||||
|
||||
/* 基础三属性*/
|
||||
required RadioHTMode htmode = 3 [default = RHT_20];
|
||||
required uint32 txpower = 4 [default = 0];
|
||||
required uint32 channel = 5 [default = 0];
|
||||
|
||||
/* 其他属性*/
|
||||
optional int32 signal = 20;
|
||||
optional int32 noise = 21;
|
||||
optional string bitrate = 22;
|
||||
|
||||
/* 增加字段*/
|
||||
optional int32 maxsta = 23 [default = 0];
|
||||
optional int32 rejrssi = 24 [default = -85];
|
||||
optional string country = 25;
|
||||
optional int32 enable_fils = 26 [default = 1];
|
||||
optional string mac = 27;
|
||||
}
|
||||
|
||||
message ManageInterface {
|
||||
required string ifname = 1;
|
||||
optional string ip = 2;
|
||||
optional string mac = 3;
|
||||
optional string netmask = 4;
|
||||
optional string gateway = 5;
|
||||
optional string dns1= 6;
|
||||
optional string dns2= 7;
|
||||
optional string ipproto = 8; // dhcp static pppoe
|
||||
}
|
||||
222
src/internal/mqtt/server.go
Normal file
222
src/internal/mqtt/server.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
mqtt_server "github.com/mochi-mqtt/server/v2"
|
||||
"github.com/mochi-mqtt/server/v2/hooks/auth"
|
||||
"github.com/mochi-mqtt/server/v2/listeners"
|
||||
"github.com/mochi-mqtt/server/v2/packets"
|
||||
|
||||
"ac/internal/context"
|
||||
"ac/internal/logger"
|
||||
"ac/internal/mqtt/message"
|
||||
"ac/pkg/app"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
app.App
|
||||
|
||||
mqttServer *mqtt_server.Server
|
||||
}
|
||||
|
||||
var mqttServer *mqtt_server.Server
|
||||
|
||||
func NewServer(ac app.App) (*Server, error) {
|
||||
server := mqtt_server.New(&mqtt_server.Options{
|
||||
Logger: slog.New(slog.NewTextHandler(logger.Log.Out, &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
})),
|
||||
InlineClient: true, // you must enable inline client to use direct publishing and subscribing.
|
||||
})
|
||||
|
||||
_ = server.AddHook(new(auth.AllowHook), nil)
|
||||
tcp := listeners.NewTCP(listeners.Config{
|
||||
ID: "t1",
|
||||
Address: ":5247",
|
||||
})
|
||||
err := server.AddListener(tcp)
|
||||
if err != nil {
|
||||
logger.MqttLog.Fatal(err)
|
||||
}
|
||||
|
||||
// Add ac hook (AcHook) to the server
|
||||
err = server.AddHook(new(AcHook), &AcHookOptions{
|
||||
Server: server,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.MqttLog.Fatal(err)
|
||||
}
|
||||
|
||||
mqttServer = server
|
||||
|
||||
return &Server{ac, server}, nil
|
||||
}
|
||||
|
||||
// Options contains configuration settings for the hook.
|
||||
type AcHookOptions struct {
|
||||
Server *mqtt_server.Server
|
||||
}
|
||||
|
||||
type AcHook struct {
|
||||
mqtt_server.HookBase
|
||||
config *AcHookOptions
|
||||
}
|
||||
|
||||
func (h *AcHook) ID() string {
|
||||
return "events-ac"
|
||||
}
|
||||
|
||||
func (h *AcHook) Provides(b byte) bool {
|
||||
return bytes.Contains([]byte{
|
||||
mqtt_server.OnConnect,
|
||||
mqtt_server.OnDisconnect,
|
||||
mqtt_server.OnSubscribed,
|
||||
mqtt_server.OnUnsubscribed,
|
||||
mqtt_server.OnPublished,
|
||||
mqtt_server.OnPublish,
|
||||
}, []byte{b})
|
||||
}
|
||||
|
||||
func (h *AcHook) Init(config any) error {
|
||||
h.Log.Info("initialised")
|
||||
if _, ok := config.(*AcHookOptions); !ok && config != nil {
|
||||
return mqtt_server.ErrInvalidConfigType
|
||||
}
|
||||
|
||||
h.config = config.(*AcHookOptions)
|
||||
if h.config.Server == nil {
|
||||
return mqtt_server.ErrInvalidConfigType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// subscribeCallback handles messages for subscribed topics
|
||||
func (h *AcHook) subscribeCallback(cl *mqtt_server.Client, sub packets.Subscription, pk packets.Packet) {
|
||||
h.Log.Info("hook subscribed message", "client", cl.ID, "topic", pk.TopicName)
|
||||
}
|
||||
|
||||
func (h *AcHook) OnConnect(cl *mqtt_server.Client, pk packets.Packet) error {
|
||||
h.Log.Info("client connected", "client", cl.ID)
|
||||
|
||||
// Example demonstrating how to subscribe to a topic within the hook.
|
||||
h.config.Server.Subscribe("hook/direct/publish", 1, h.subscribeCallback)
|
||||
|
||||
// Example demonstrating how to publish a message within the hook
|
||||
err := h.config.Server.Publish("hook/direct/publish", []byte("packet hook message"), false, 0)
|
||||
if err != nil {
|
||||
h.Log.Error("hook.publish", "error", err)
|
||||
}
|
||||
|
||||
acSelf := context.GetSelf()
|
||||
ap, ok := acSelf.ApFindByClientId(cl.ID)
|
||||
if ok {
|
||||
acSelf.DeleteAp(ap.Client)
|
||||
}
|
||||
logger.MqttLog.Infof("Create a new Client for: %s", cl.ID)
|
||||
ap = acSelf.NewAp(cl)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *AcHook) OnDisconnect(cl *mqtt_server.Client, err error, expire bool) {
|
||||
if err != nil {
|
||||
h.Log.Info("client disconnected", "client", cl.ID, "expire", expire, "error", err)
|
||||
} else {
|
||||
h.Log.Info("client disconnected", "client", cl.ID, "expire", expire)
|
||||
}
|
||||
|
||||
context.GetSelf().DeleteAp(cl)
|
||||
}
|
||||
|
||||
func (h *AcHook) OnSubscribed(cl *mqtt_server.Client, pk packets.Packet, reasonCodes []byte) {
|
||||
h.Log.Info(fmt.Sprintf("subscribed qos=%v", reasonCodes), "client", cl.ID, "filters", pk.Filters)
|
||||
}
|
||||
|
||||
func (h *AcHook) OnUnsubscribed(cl *mqtt_server.Client, pk packets.Packet) {
|
||||
h.Log.Info("unsubscribed", "client", cl.ID, "filters", pk.Filters)
|
||||
}
|
||||
|
||||
func (h *AcHook) OnPublish(cl *mqtt_server.Client, pk packets.Packet) (packets.Packet, error) {
|
||||
h.Log.Info("received from client", "client", cl.ID, "payload", string(pk.Payload))
|
||||
|
||||
if cl.ID == "inline" {
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
acSelf := context.GetSelf()
|
||||
ap, ok := acSelf.ApFindByClient(cl)
|
||||
if !ok {
|
||||
logger.MqttLog.Infof("Create a new Client for: %s", cl.ID)
|
||||
ap = acSelf.NewAp(cl)
|
||||
}
|
||||
|
||||
if pk.TopicName == "AP/echo" {
|
||||
var unmarshaledEcho message.Echo
|
||||
err := proto.Unmarshal(pk.Payload, &unmarshaledEcho)
|
||||
if err != nil {
|
||||
logger.MqttLog.Errorf("Unmarshal to struct error: %v", err)
|
||||
} else {
|
||||
logger.MqttLog.Infof("received echo: %+v", &unmarshaledEcho)
|
||||
ap.LastEcho = &unmarshaledEcho
|
||||
}
|
||||
}
|
||||
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
func (h *AcHook) OnPublished(cl *mqtt_server.Client, pk packets.Packet) {
|
||||
h.Log.Info("published to client", "client", cl.ID, "payload", string(pk.Payload))
|
||||
}
|
||||
|
||||
func SendCmdReboot(apSn string) {
|
||||
var cmd message.CMD
|
||||
cmd.Type = new(message.CMDType)
|
||||
*cmd.Type = message.CMDType_REBOOT
|
||||
encoded, err := proto.Marshal(&cmd)
|
||||
if err != nil {
|
||||
logger.MqttLog.Errorf("Encode to protobuf data error: %v", err)
|
||||
} else {
|
||||
topic := "AC/" + apSn + "/M_cmd"
|
||||
err := mqttServer.Publish(topic, encoded, false, 2)
|
||||
if err != nil {
|
||||
logger.MqttLog.Error("publish", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SendCmdKickUser(apSn, staMac string) {
|
||||
var cmd message.CMD
|
||||
cmd.Type = new(message.CMDType)
|
||||
*cmd.Type = message.CMDType_KICK_USER
|
||||
cmd.Args = new(string)
|
||||
*cmd.Args = staMac
|
||||
encoded, err := proto.Marshal(&cmd)
|
||||
if err != nil {
|
||||
logger.MqttLog.Errorf("Encode to protobuf data error: %v", err)
|
||||
} else {
|
||||
topic := "AC/" + apSn + "/M_cmd"
|
||||
err := mqttServer.Publish(topic, encoded, false, 2)
|
||||
if err != nil {
|
||||
logger.MqttLog.Error("publish", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server)Run() error {
|
||||
logger.MqttLog.Infof("Start MQTT server")
|
||||
return s.mqttServer.Serve()
|
||||
}
|
||||
|
||||
func (s *Server)Stop() {
|
||||
if s.mqttServer != nil {
|
||||
logger.MqttLog.Infof("Stop MQTT server")
|
||||
if err := s.mqttServer.Close(); err != nil {
|
||||
logger.MqttLog.Errorf("Could not close MQTT server: %#v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user