mirror of
https://github.com/luscis/openlan.git
synced 2025-10-05 08:36:59 +08:00
390 lines
8.8 KiB
Go
Executable File
390 lines
8.8 KiB
Go
Executable File
package cswitch
|
|
|
|
import (
|
|
"net"
|
|
|
|
"github.com/luscis/openlan/pkg/api"
|
|
"github.com/luscis/openlan/pkg/cache"
|
|
co "github.com/luscis/openlan/pkg/config"
|
|
"github.com/luscis/openlan/pkg/libol"
|
|
"github.com/luscis/openlan/pkg/models"
|
|
"github.com/luscis/openlan/pkg/schema"
|
|
nl "github.com/vishvananda/netlink"
|
|
)
|
|
|
|
func GetStateEncap(mode string, sport, dport int) *nl.XfrmStateEncap {
|
|
if mode == "udp" {
|
|
return &nl.XfrmStateEncap{
|
|
Type: nl.XFRM_ENCAP_ESPINUDP,
|
|
SrcPort: sport,
|
|
DstPort: dport,
|
|
OriginalAddress: net.ParseIP("0.0.0.0"),
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type EspWorker struct {
|
|
*WorkerImpl
|
|
proto nl.Proto
|
|
mode nl.Mode
|
|
states []*models.EspState
|
|
policies []*models.EspPolicy
|
|
spec *co.EspSpecifies
|
|
}
|
|
|
|
func NewESPWorker(c *co.Network) *EspWorker {
|
|
w := &EspWorker{
|
|
WorkerImpl: NewWorkerApi(c),
|
|
proto: nl.XFRM_PROTO_ESP,
|
|
mode: nl.XFRM_MODE_TUNNEL,
|
|
}
|
|
w.spec, _ = c.Specifies.(*co.EspSpecifies)
|
|
return w
|
|
}
|
|
|
|
type StateParameters struct {
|
|
spi int
|
|
local, remote net.IP
|
|
auth, crypt string
|
|
}
|
|
|
|
func (w *EspWorker) newState(args StateParameters) *nl.XfrmState {
|
|
state := &nl.XfrmState{
|
|
Src: args.local,
|
|
Dst: args.remote,
|
|
Proto: w.proto,
|
|
Mode: w.mode,
|
|
Spi: args.spi,
|
|
Auth: &nl.XfrmStateAlgo{
|
|
Name: "hmac(sha256)",
|
|
Key: []byte(args.auth),
|
|
},
|
|
Crypt: &nl.XfrmStateAlgo{
|
|
Name: "cbc(aes)",
|
|
Key: []byte(args.crypt),
|
|
},
|
|
}
|
|
return state
|
|
}
|
|
|
|
type PolicyParameter struct {
|
|
spi int
|
|
local, remote net.IP
|
|
src, dst *net.IPNet
|
|
dir nl.Dir
|
|
pri int
|
|
}
|
|
|
|
func (w *EspWorker) newPolicy(args PolicyParameter) *nl.XfrmPolicy {
|
|
policy := &nl.XfrmPolicy{
|
|
Src: args.src,
|
|
Dst: args.dst,
|
|
Dir: args.dir,
|
|
Priority: args.pri,
|
|
}
|
|
tmpl := nl.XfrmPolicyTmpl{
|
|
Src: args.local,
|
|
Dst: args.remote,
|
|
Proto: w.proto,
|
|
Mode: w.mode,
|
|
Spi: args.spi,
|
|
}
|
|
policy.Tmpls = append(policy.Tmpls, tmpl)
|
|
return policy
|
|
}
|
|
|
|
func (w *EspWorker) addState(ms *models.EspState) {
|
|
spi := ms.Spi
|
|
w.out.Info("EspWorker.addState %s", ms.String())
|
|
if st := w.newState(StateParameters{
|
|
spi, ms.Local, ms.Remote, ms.Auth, ms.Crypt,
|
|
}); st != nil {
|
|
st.Encap = GetStateEncap(ms.Encap, co.EspLocalUdp, ms.RemotePort)
|
|
ms.In = st
|
|
} else {
|
|
return
|
|
}
|
|
if st := w.newState(StateParameters{
|
|
spi, ms.Remote, ms.Local, ms.Auth, ms.Crypt,
|
|
}); st != nil {
|
|
st.Encap = GetStateEncap(ms.Encap, ms.RemotePort, co.EspLocalUdp)
|
|
ms.Out = st
|
|
} else {
|
|
return
|
|
}
|
|
w.states = append(w.states, ms)
|
|
cache.EspState.Add(ms)
|
|
}
|
|
|
|
func (w *EspWorker) delState(ms *models.EspState) {
|
|
w.out.Info("EspWorker.delState %s", ms.String())
|
|
cache.EspState.Del(ms.ID())
|
|
}
|
|
|
|
func (w *EspWorker) addPolicy(mp *models.EspPolicy) {
|
|
spi := mp.Spi
|
|
src, err := libol.ParseNet(mp.Source)
|
|
if err != nil {
|
|
w.out.Error("EspWorker.addPolicy %s: %s", mp.String(), err)
|
|
return
|
|
}
|
|
dst, err := libol.ParseNet(mp.Dest)
|
|
if err != nil {
|
|
w.out.Error("EspWorker.addPolicy %s: %s", mp.String(), err)
|
|
return
|
|
}
|
|
w.out.Info("EspWorker.addPolicy %s", mp.String())
|
|
if po := w.newPolicy(PolicyParameter{
|
|
spi, mp.Local, mp.Remote, src, dst, nl.XFRM_DIR_OUT, mp.Priority,
|
|
}); po != nil {
|
|
mp.Out = po
|
|
} else {
|
|
return
|
|
}
|
|
if po := w.newPolicy(PolicyParameter{
|
|
spi, mp.Remote, mp.Local, dst, src, nl.XFRM_DIR_FWD, mp.Priority,
|
|
}); po != nil {
|
|
mp.Fwd = po
|
|
} else {
|
|
return
|
|
}
|
|
if po := w.newPolicy(PolicyParameter{
|
|
spi, mp.Remote, mp.Local, dst, src, nl.XFRM_DIR_IN, mp.Priority,
|
|
}); po != nil {
|
|
mp.In = po
|
|
} else {
|
|
return
|
|
}
|
|
w.policies = append(w.policies, mp)
|
|
cache.EspPolicy.Add(mp)
|
|
}
|
|
|
|
func (w *EspWorker) delPolicy(mp *models.EspPolicy) {
|
|
w.out.Info("EspWorker.delPolicy %s", mp.String())
|
|
cache.EspPolicy.Del(mp.ID())
|
|
}
|
|
|
|
func (w *EspWorker) updateXfrm() {
|
|
for _, mem := range w.spec.Members {
|
|
if mem == nil {
|
|
continue
|
|
}
|
|
state := mem.State
|
|
if state.LocalIp == nil || state.RemoteIp == nil {
|
|
continue
|
|
}
|
|
|
|
ms := &models.EspState{
|
|
EspState: &schema.EspState{
|
|
Name: w.spec.Name,
|
|
Spi: mem.Spi,
|
|
Local: state.LocalIp,
|
|
Remote: state.RemoteIp,
|
|
Proto: uint8(w.proto),
|
|
Mode: uint8(w.mode),
|
|
Encap: state.Encap,
|
|
Auth: state.Auth,
|
|
Crypt: state.Crypt,
|
|
RemotePort: state.RemotePort,
|
|
},
|
|
}
|
|
w.addState(ms)
|
|
for _, pol := range mem.Policies {
|
|
if pol == nil || pol.Dest == "" {
|
|
continue
|
|
}
|
|
mp := &models.EspPolicy{
|
|
EspPolicy: &schema.EspPolicy{
|
|
Name: w.spec.Name,
|
|
Spi: mem.Spi,
|
|
Local: state.LocalIp,
|
|
Remote: state.RemoteIp,
|
|
Source: pol.Source,
|
|
Dest: pol.Dest,
|
|
Priority: pol.Priority,
|
|
},
|
|
}
|
|
w.addPolicy(mp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *EspWorker) Initialize() {
|
|
w.WorkerImpl.Initialize()
|
|
w.updateXfrm()
|
|
}
|
|
|
|
func (w *EspWorker) addRoute(device, src, remote string) error {
|
|
link, err := nl.LinkByName(device)
|
|
if link == nil {
|
|
return err
|
|
}
|
|
// add peer routes.
|
|
dst, err := libol.ParseNet(remote)
|
|
if err != nil {
|
|
return libol.NewErr("%s %s.", err, remote)
|
|
}
|
|
gw := libol.ParseAddr(src)
|
|
rte := &nl.Route{
|
|
Dst: dst,
|
|
Gw: gw,
|
|
LinkIndex: link.Attrs().Index,
|
|
Priority: 650,
|
|
AdvMSS: w.spec.TcpMss,
|
|
}
|
|
w.out.Debug("EspWorker.addRoute: %s", rte)
|
|
if err := nl.RouteReplace(rte); err != nil {
|
|
return libol.NewErr("%s %s.", err, remote)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *EspWorker) UpDummy(name, addr, peer string) error {
|
|
link, _ := nl.LinkByName(name)
|
|
if link == nil {
|
|
port := &nl.Dummy{
|
|
LinkAttrs: nl.LinkAttrs{
|
|
TxQLen: -1,
|
|
Name: name,
|
|
},
|
|
}
|
|
if err := nl.LinkAdd(port); err != nil {
|
|
return err
|
|
}
|
|
link, _ = nl.LinkByName(name)
|
|
}
|
|
if err := nl.LinkSetUp(link); err != nil {
|
|
w.out.Error("EspWorker.UpDummy: %s", err)
|
|
}
|
|
if addr != "" {
|
|
ipAddr, err := nl.ParseAddr(addr)
|
|
if err != nil {
|
|
return libol.NewErr("%s %s.", err, addr)
|
|
}
|
|
if err := nl.AddrReplace(link, ipAddr); err != nil {
|
|
w.out.Warn("EspWorker.UpDummy: %s", err)
|
|
}
|
|
}
|
|
w.out.Info("EspWorker.Open %s success", name)
|
|
return nil
|
|
}
|
|
|
|
func (w *EspWorker) addXfrm() {
|
|
for _, state := range w.states {
|
|
w.out.Debug("EspWorker.AddXfrm State %v", state)
|
|
if err := nl.XfrmStateAdd(state.In); err != nil {
|
|
w.out.Error("EspWorker.addXfrm in %s: %s", state.String(), err)
|
|
}
|
|
if err := nl.XfrmStateAdd(state.Out); err != nil {
|
|
w.out.Error("EspWorker.addXfrm out %s: %s", state.String(), err)
|
|
}
|
|
}
|
|
for _, pol := range w.policies {
|
|
w.out.Debug("EspWorker.AddXfrm Policy %v", pol)
|
|
if err := nl.XfrmPolicyAdd(pol.In); err != nil {
|
|
w.out.Error("EspWorker.addXfrm in %v: %s", pol.In, err)
|
|
}
|
|
if err := nl.XfrmPolicyAdd(pol.Fwd); err != nil {
|
|
w.out.Error("EspWorker.addXfrm fwd %v: %s", pol.Fwd, err)
|
|
}
|
|
if err := nl.XfrmPolicyAdd(pol.Out); err != nil {
|
|
w.out.Error("EspWorker.addXfrm out %v: %s", pol.Out, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *EspWorker) Start(v api.Switcher) {
|
|
w.uuid = v.UUID()
|
|
w.upMember()
|
|
w.addXfrm()
|
|
cache.Esp.Add(&models.Esp{
|
|
Name: w.cfg.Name,
|
|
Address: w.spec.Address,
|
|
})
|
|
w.WorkerImpl.Start(v)
|
|
}
|
|
|
|
func (w *EspWorker) DownDummy(name string) error {
|
|
link, _ := nl.LinkByName(name)
|
|
if link == nil {
|
|
return nil
|
|
}
|
|
port := &nl.Dummy{
|
|
LinkAttrs: nl.LinkAttrs{
|
|
TxQLen: -1,
|
|
Name: name,
|
|
},
|
|
}
|
|
if err := nl.LinkDel(port); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *EspWorker) delXfrm() {
|
|
for _, mp := range w.policies {
|
|
w.delPolicy(mp)
|
|
if err := nl.XfrmPolicyDel(mp.In); err != nil {
|
|
w.out.Warn("EspWorker.delXfrm Policy.in %s: %s", mp.String(), err)
|
|
}
|
|
if err := nl.XfrmPolicyDel(mp.Fwd); err != nil {
|
|
w.out.Warn("EspWorker.delXfrm Policy.fwd %s: %s", mp.String(), err)
|
|
}
|
|
if err := nl.XfrmPolicyDel(mp.Out); err != nil {
|
|
w.out.Warn("EspWorker.delXfrm Policy.out %s: %s", mp.String(), err)
|
|
}
|
|
}
|
|
w.policies = nil
|
|
for _, ms := range w.states {
|
|
w.delState(ms)
|
|
if err := nl.XfrmStateDel(ms.In); err != nil {
|
|
w.out.Warn("EspWorker.delXfrm State.in %s: %s", ms.String(), err)
|
|
}
|
|
if err := nl.XfrmStateDel(ms.Out); err != nil {
|
|
w.out.Warn("EspWorker.delXfrm State.out %s: %s", ms.String(), err)
|
|
}
|
|
}
|
|
w.states = nil
|
|
}
|
|
|
|
func (w *EspWorker) Stop() {
|
|
w.WorkerImpl.Stop()
|
|
cache.Esp.Del(w.cfg.Name)
|
|
w.downMember()
|
|
w.delXfrm()
|
|
}
|
|
|
|
func (w *EspWorker) Reload(v api.Switcher) {
|
|
w.Stop()
|
|
w.Initialize()
|
|
w.Start(v)
|
|
}
|
|
|
|
func (w *EspWorker) upMember() {
|
|
for _, mem := range w.spec.Members {
|
|
if mem.Peer == "" {
|
|
continue
|
|
}
|
|
if err := w.UpDummy(w.spec.Name, mem.Address, mem.Peer); err != nil {
|
|
w.out.Warn("EspWorker.UpDummy %d %s", mem.Spi, err)
|
|
}
|
|
for _, po := range mem.Policies {
|
|
if err := w.addRoute(w.spec.Name, mem.Address, po.Dest); err != nil {
|
|
w.out.Warn("EspWorker.addRoute %d %s", mem.Spi, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *EspWorker) downMember() {
|
|
for _, mem := range w.spec.Members {
|
|
if mem.Peer == "" {
|
|
continue
|
|
}
|
|
if err := w.DownDummy(w.spec.Name); err != nil {
|
|
w.out.Error("EspWorker.downMember %d %s", mem.Spi, err)
|
|
}
|
|
}
|
|
}
|