Files
openlan/pkg/switch/acl.go
2024-03-27 16:29:59 +08:00

182 lines
3.3 KiB
Go

package cswitch
import (
"fmt"
"strconv"
co "github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
cn "github.com/luscis/openlan/pkg/network"
"github.com/luscis/openlan/pkg/schema"
)
type ACLRule struct {
SrcIp string
DstIp string
Proto string // TCP, UDP or ICMP
SrcPort int
DstPort int
Action string // DROP or ACCEPT
rule *cn.IPRule
}
func (r *ACLRule) Id() string {
return fmt.Sprintf("%s:%s:%s:%d:%d", r.SrcIp, r.DstIp, r.Proto, r.DstPort, r.SrcPort)
}
func (r *ACLRule) Rule() cn.IPRule {
if r.rule == nil {
r.rule = &cn.IPRule{
Dest: r.DstIp,
Source: r.SrcIp,
Proto: r.Proto,
Jump: r.Action,
}
if r.DstPort > 0 {
r.rule.DstPort = strconv.Itoa(r.DstPort)
}
if r.SrcPort > 0 {
r.rule.SrcPort = strconv.Itoa(r.SrcPort)
}
}
return *r.rule
}
type ACL struct {
Name string
Rules map[string]*ACLRule
chain *cn.FireWallChain
out *libol.SubLogger
}
func NewACL(name string) *ACL {
return &ACL{
Name: name,
out: libol.NewSubLogger(name),
Rules: make(map[string]*ACLRule, 32),
}
}
func (a *ACL) Chain() string {
return "ATT_" + a.Name
}
func (a *ACL) Initialize() {
a.chain = cn.NewFireWallChain(a.Chain(), cn.TRaw, "")
}
func (a *ACL) Start() {
a.out.Info("ACL.Start")
cfg := co.GetAcl(a.Name)
if cfg != nil {
for _, rule := range cfg.Rules {
a.addRule(rule)
}
}
a.chain.Install()
}
func (a *ACL) Stop() {
a.out.Info("ACL.Stop")
a.chain.Cancel()
}
func (a *ACL) addRule(rule *co.ACLRule) {
ar := &ACLRule{
Proto: rule.Proto,
DstIp: rule.DstIp,
SrcIp: rule.SrcIp,
DstPort: rule.DstPort,
SrcPort: rule.SrcPort,
Action: rule.Action,
}
a.out.Info("ACL.addRule %s", ar.Id())
if _, ok := a.Rules[ar.Id()]; !ok {
a.chain.AddRule(ar.Rule())
a.Rules[ar.Id()] = ar
}
}
func (a *ACL) AddRule(rule *schema.ACLRule) error {
ar := &ACLRule{
Proto: rule.Proto,
DstIp: rule.DstIp,
SrcIp: rule.SrcIp,
DstPort: rule.DstPort,
SrcPort: rule.SrcPort,
Action: rule.Action,
}
a.out.Info("ACL.AddRule %s", ar.Id())
if _, ok := a.Rules[ar.Id()]; ok {
return libol.NewErr("AddRule: already existed")
}
if err := a.chain.AddRuleX(ar.Rule()); err == nil {
a.Rules[ar.Id()] = ar
} else {
a.out.Warn("ACL.AddRule %s", err)
}
return nil
}
func (a *ACL) DelRule(rule *schema.ACLRule) error {
ar := &ACLRule{
Proto: rule.Proto,
DstIp: rule.DstIp,
SrcIp: rule.SrcIp,
DstPort: rule.DstPort,
SrcPort: rule.SrcPort,
Action: rule.Action,
}
a.out.Info("ACL.DelRule %s", ar.Id())
if _, ok := a.Rules[ar.Id()]; !ok {
return nil
}
if err := a.chain.DelRuleX(ar.Rule()); err == nil {
delete(a.Rules, ar.Id())
} else {
a.out.Warn("ACL.DelRule %s", err)
}
return nil
}
func (a *ACL) ListRules(call func(obj schema.ACLRule)) {
for _, rule := range a.Rules {
obj := schema.ACLRule{
SrcIp: rule.SrcIp,
DstIp: rule.DstIp,
SrcPort: rule.SrcPort,
DstPort: rule.DstPort,
Proto: rule.Proto,
Action: rule.Action,
}
call(obj)
}
}
func (a *ACL) Save() {
cfg := co.GetAcl(a.Name)
cfg.Rules = nil
for _, rule := range a.Rules {
cr := &co.ACLRule{
DstIp: rule.DstIp,
SrcIp: rule.SrcIp,
Proto: rule.Proto,
DstPort: rule.DstPort,
SrcPort: rule.SrcPort,
Action: rule.Action,
}
cfg.Rules = append(cfg.Rules, cr)
}
cfg.Save()
}