Files
openlan/pkg/network/firewall.go
2023-12-27 23:00:44 +08:00

450 lines
9.4 KiB
Go
Executable File

package network
import (
"sync"
"github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
"github.com/moby/libnetwork/iptables"
)
const (
OLCInput = "XTT_in"
OLCForward = "XTT_for"
OLCOutput = "XTT_out"
OLCPre = "XTT_pre"
OLCPost = "XTT_pos"
)
type FireWallGlobal struct {
lock sync.Mutex
chains IpChains
rules IpRules
}
func NewFireWallGlobal(flows []config.FlowRule) *FireWallGlobal {
f := &FireWallGlobal{
chains: make(IpChains, 0, 8),
rules: make(IpRules, 0, 32),
}
// Load custom rules.
for _, rule := range flows {
f.rules = f.rules.Add(IpRule{
Table: rule.Table,
Chain: rule.Chain,
Source: rule.Source,
Dest: rule.Dest,
Jump: rule.Jump,
ToSource: rule.ToSource,
ToDest: rule.ToDest,
Comment: rule.Comment,
Proto: rule.Proto,
Match: rule.Match,
DstPort: rule.DstPort,
SrcPort: rule.SrcPort,
Input: rule.Input,
Output: rule.Output,
CtState: rule.CtState,
})
}
return f
}
func (f *FireWallGlobal) addOLC() {
f.AddChain(IpChain{Table: TFilter, Name: OLCInput})
f.AddChain(IpChain{Table: TFilter, Name: OLCForward})
f.AddChain(IpChain{Table: TFilter, Name: OLCOutput})
f.AddChain(IpChain{Table: TNat, Name: OLCPre})
f.AddChain(IpChain{Table: TNat, Name: OLCInput})
f.AddChain(IpChain{Table: TNat, Name: OLCPost})
f.AddChain(IpChain{Table: TNat, Name: OLCOutput})
f.AddChain(IpChain{Table: TMangle, Name: OLCPre})
f.AddChain(IpChain{Table: TMangle, Name: OLCInput})
f.AddChain(IpChain{Table: TMangle, Name: OLCForward})
f.AddChain(IpChain{Table: TMangle, Name: OLCPost})
f.AddChain(IpChain{Table: TMangle, Name: OLCOutput})
f.AddChain(IpChain{Table: TRaw, Name: OLCPre})
f.AddChain(IpChain{Table: TRaw, Name: OLCOutput})
}
func (f *FireWallGlobal) jumpOLC() {
// Filter Table
f.AddRule(IpRule{Order: "-I", Table: TFilter, Chain: CInput, Jump: OLCInput})
f.AddRule(IpRule{Order: "-I", Table: TFilter, Chain: CForward, Jump: OLCForward})
f.AddRule(IpRule{Order: "-I", Table: TFilter, Chain: COutput, Jump: OLCOutput})
// NAT Table
f.AddRule(IpRule{Order: "-I", Table: TNat, Chain: CPre, Jump: OLCPre})
f.AddRule(IpRule{Order: "-I", Table: TNat, Chain: CInput, Jump: OLCInput})
f.AddRule(IpRule{Order: "-I", Table: TNat, Chain: CPost, Jump: OLCPost})
f.AddRule(IpRule{Order: "-I", Table: TNat, Chain: COutput, Jump: OLCOutput})
// Mangle Table
f.AddRule(IpRule{Order: "-I", Table: TMangle, Chain: CPre, Jump: OLCPre})
f.AddRule(IpRule{Order: "-I", Table: TMangle, Chain: CInput, Jump: OLCInput})
f.AddRule(IpRule{Order: "-I", Table: TMangle, Chain: CForward, Jump: OLCForward})
f.AddRule(IpRule{Order: "-I", Table: TMangle, Chain: CPost, Jump: OLCPost})
f.AddRule(IpRule{Order: "-I", Table: TMangle, Chain: COutput, Jump: OLCOutput})
// Raw Table
f.AddRule(IpRule{Order: "-I", Table: TRaw, Chain: CPre, Jump: OLCPre})
f.AddRule(IpRule{Order: "-I", Table: TRaw, Chain: COutput, Jump: OLCOutput})
}
func (f *FireWallGlobal) Initialize() {
IptableInit()
// Init chains
f.addOLC()
f.jumpOLC()
}
func (f *FireWallGlobal) AddChain(chain IpChain) {
f.chains = f.chains.Add(chain)
}
func (f *FireWallGlobal) AddRule(rule IpRule) {
f.rules = f.rules.Add(rule)
}
func (f *FireWallGlobal) InstallRule(rule IpRule) error {
f.lock.Lock()
defer f.lock.Unlock()
order := rule.Order
if order == "" {
order = "-A"
}
if _, err := rule.Opr(order); err != nil {
return err
}
f.rules = f.rules.Add(rule)
return nil
}
func (f *FireWallGlobal) install() {
for _, c := range f.chains {
if _, err := c.Opr("-N"); err != nil {
libol.Error("FireWall.install %s", err)
}
}
for _, r := range f.rules {
order := r.Order
if order == "" {
order = "-A"
}
if _, err := r.Opr(order); err != nil {
libol.Error("FireWall.install %s", err)
}
}
}
func (f *FireWallGlobal) Start() {
f.lock.Lock()
defer f.lock.Unlock()
libol.Info("FireWall.Start")
f.install()
iptables.OnReloaded(func() {
libol.Info("FireWall.Start OnReloaded")
f.lock.Lock()
defer f.lock.Unlock()
f.install()
})
}
func (f *FireWallGlobal) cancel() {
for _, r := range f.rules {
if _, err := r.Opr("-D"); err != nil {
libol.Warn("FireWall.cancel %s", err)
}
}
for _, c := range f.chains {
if _, err := c.Opr("-X"); err != nil {
libol.Warn("FireWall.cancel %s", err)
}
}
}
func (f *FireWallGlobal) CancelRule(rule IpRule) error {
f.lock.Lock()
defer f.lock.Unlock()
if _, err := rule.Opr("-D"); err != nil {
return err
}
f.rules = f.rules.Remove(rule)
return nil
}
func (f *FireWallGlobal) Stop() {
f.lock.Lock()
defer f.lock.Unlock()
libol.Info("FireWall.Stop")
f.cancel()
}
func (f *FireWallGlobal) Refresh() {
f.cancel()
f.install()
}
type FireWallChain struct {
lock sync.Mutex
name string
parent string
rules IpRules
table string
}
func NewFireWallChain(name, table, parent string) *FireWallChain {
return &FireWallChain{
name: name,
table: table,
parent: parent,
}
}
func (ch *FireWallChain) Chain() IpChain {
name := ch.name
if ch.parent != "" {
name = ch.parent + "-" + ch.name
}
if len(name) > 28 {
name = name[:28]
}
return IpChain{
Table: ch.table,
Name: name,
From: ch.parent,
}
}
func (ch *FireWallChain) Jump() IpRule {
c := ch.Chain()
return IpRule{
Order: "-I",
Table: c.Table,
Chain: c.From,
Jump: c.Name,
}
}
func (ch *FireWallChain) AddRule(rule IpRule) {
chain := ch.Chain()
rule.Table = chain.Table
rule.Chain = chain.Name
ch.rules = ch.rules.Add(rule)
}
func (ch *FireWallChain) Install() {
ch.lock.Lock()
defer ch.lock.Unlock()
c := ch.Chain()
if _, err := c.Opr("-N"); err != nil {
libol.Error("FireWallChain.new %s", err)
}
for _, r := range ch.rules {
order := r.Order
if order == "" {
order = "-A"
}
if _, err := r.Opr(order); err != nil {
libol.Error("FireWallChain.install %s", err)
}
}
j := ch.Jump()
if j.Chain != "" {
if _, err := j.Opr(j.Order); err != nil {
libol.Error("FireWallChain.new %s", err)
}
}
}
func (ch *FireWallChain) Cancel() {
ch.lock.Lock()
defer ch.lock.Unlock()
j := ch.Jump()
if j.Chain != "" {
if _, err := j.Opr("-D"); err != nil {
libol.Error("FireWallChain.cancel %s", err)
}
}
for _, r := range ch.rules {
if _, err := r.Opr("-D"); err != nil {
libol.Warn("FireWall.cancel %s", err)
}
}
c := ch.Chain()
if _, err := c.Opr("-X"); err != nil {
libol.Error("FireWallChain.free %s", err)
}
}
type FireWallFilter struct {
name string
In *FireWallChain
Out *FireWallChain
For *FireWallChain
}
func NewFireWallFilter(name string) *FireWallFilter {
return &FireWallFilter{
In: NewFireWallChain(name, TFilter, OLCInput),
For: NewFireWallChain(name, TFilter, OLCForward),
Out: NewFireWallChain(name, TFilter, OLCOutput),
}
}
func (f *FireWallFilter) Install() {
// Install Chain Rules
f.In.Install()
f.For.Install()
f.Out.Install()
}
func (f *FireWallFilter) Cancel() {
// Cancel Chain Rules
f.In.Cancel()
f.For.Cancel()
f.Out.Cancel()
}
type FireWallNATPre struct {
*FireWallChain
}
func (ch *FireWallNATPre) Chain() IpChain {
return IpChain{
Table: TNat,
Name: OLCPre + "-" + ch.name,
From: ch.parent,
}
}
type FireWallNAT struct {
name string
Pre *FireWallChain
In *FireWallChain
Out *FireWallChain
Post *FireWallChain
}
func NewFireWallNAT(name string) *FireWallNAT {
return &FireWallNAT{
Pre: NewFireWallChain(name, TNat, OLCPre),
In: NewFireWallChain(name, TNat, OLCInput),
Out: NewFireWallChain(name, TNat, OLCOutput),
Post: NewFireWallChain(name, TNat, OLCPost),
}
}
func (n *FireWallNAT) Install() {
// Install Chain Rules
n.Pre.Install()
n.In.Install()
n.Out.Install()
n.Post.Install()
}
func (n *FireWallNAT) Cancel() {
// Cancel Chain Rules
n.Pre.Cancel()
n.In.Cancel()
n.Out.Cancel()
n.Post.Cancel()
}
type FireWallMangle struct {
name string
Pre *FireWallChain
In *FireWallChain
For *FireWallChain
Out *FireWallChain
Post *FireWallChain
}
func NewFireWallMangle(name string) *FireWallMangle {
return &FireWallMangle{
Pre: NewFireWallChain(name, TMangle, OLCPre),
In: NewFireWallChain(name, TMangle, OLCInput),
For: NewFireWallChain(name, TMangle, OLCForward),
Out: NewFireWallChain(name, TMangle, OLCOutput),
Post: NewFireWallChain(name, TMangle, OLCPost),
}
}
func (m *FireWallMangle) Install() {
// Install Chain Rules
m.Pre.Install()
m.In.Install()
m.For.Install()
m.Out.Install()
m.Post.Install()
}
func (m *FireWallMangle) Cancel() {
// Cancel Chain Rules
m.Pre.Cancel()
m.In.Cancel()
m.For.Cancel()
m.Out.Cancel()
m.Post.Cancel()
}
type FireWallRaw struct {
name string
Pre *FireWallChain
Out *FireWallChain
}
func NewFireWallRaw(name string) *FireWallRaw {
return &FireWallRaw{
Pre: NewFireWallChain(name, TRaw, OLCPre),
Out: NewFireWallChain(name, TRaw, OLCOutput),
}
}
func (r *FireWallRaw) Install() {
// Install Chain Rules
r.Pre.Install()
r.Out.Install()
}
func (r *FireWallRaw) Cancel() {
// Cancel Chain Rules
r.Pre.Cancel()
r.Out.Cancel()
}
type FireWallTable struct {
Filter *FireWallFilter
Nat *FireWallNAT
Mangle *FireWallMangle
Raw *FireWallRaw
}
func NewFireWallTable(name string) *FireWallTable {
IptableInit()
return &FireWallTable{
Filter: NewFireWallFilter(name),
Nat: NewFireWallNAT(name),
Mangle: NewFireWallMangle(name),
Raw: NewFireWallRaw(name),
}
}
func (t *FireWallTable) Start() {
t.Filter.Install()
t.Nat.Install()
t.Mangle.Install()
t.Raw.Install()
}
func (t *FireWallTable) Stop() {
t.Raw.Cancel()
t.Mangle.Cancel()
t.Nat.Cancel()
t.Filter.Cancel()
}