mirror of
https://github.com/luscis/openlan.git
synced 2025-10-05 16:47:11 +08:00
408 lines
9.1 KiB
Go
Executable File
408 lines
9.1 KiB
Go
Executable File
package _switch
|
|
|
|
import (
|
|
"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"
|
|
"net"
|
|
"os/exec"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
UDPBin = "openudp"
|
|
)
|
|
|
|
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-%s", ms.Local, ms.Remote)
|
|
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-%s", ms.Local, ms.Remote)
|
|
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.Source, err)
|
|
return
|
|
}
|
|
dst, err := libol.ParseNet(mp.Dest)
|
|
if err != nil {
|
|
w.out.Error("EspWorker.addPolicy %s %s", mp.Dest, err)
|
|
return
|
|
}
|
|
w.out.Info("EspWorker.addPolicy %s-%s", mp.Source, mp.Dest)
|
|
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-%s", mp.Source, mp.Dest)
|
|
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: 600,
|
|
}
|
|
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 %s", state.In.Spi)
|
|
if err := nl.XfrmStateAdd(state.In); err != nil {
|
|
w.out.Error("EspWorker.Start.in State.in %s", err)
|
|
}
|
|
if err := nl.XfrmStateAdd(state.Out); err != nil {
|
|
w.out.Error("EspWorker.Start.out State.out %s", err)
|
|
}
|
|
}
|
|
for _, policy := range w.policies {
|
|
w.out.Debug("EspWorker.AddXfrm Policy %s", policy.Out.Dst)
|
|
if err := nl.XfrmPolicyAdd(policy.In); err != nil {
|
|
w.out.Error("EspWorker.addXfrm.in Policy %s", err)
|
|
}
|
|
if err := nl.XfrmPolicyAdd(policy.Fwd); err != nil {
|
|
w.out.Error("EspWorker.addXfrm.fwd Policy %s", err)
|
|
}
|
|
if err := nl.XfrmPolicyAdd(policy.Out); err != nil {
|
|
w.out.Error("EspWorker.addXfrm.out Policy %s", 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 %s-%s: %s", mp.Source, mp.Dest, err)
|
|
}
|
|
if err := nl.XfrmPolicyDel(mp.Fwd); err != nil {
|
|
w.out.Warn("EspWorker.delXfrm Policy %s-%s: %s", mp.Source, mp.Dest, err)
|
|
}
|
|
if err := nl.XfrmPolicyDel(mp.Out); err != nil {
|
|
w.out.Warn("EspWorker.delXfrm Policy %s-%s: %s", mp.Source, mp.Dest, 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 %s-%s: %s", ms.Local, ms.Remote, err)
|
|
}
|
|
if err := nl.XfrmStateDel(ms.Out); err != nil {
|
|
w.out.Warn("EspWorker.delXfrm State %s-%s: %s", ms.Local, ms.Remote, 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
func OpenUDP() {
|
|
libol.Go(func() {
|
|
args := []string{
|
|
"-p", strconv.Itoa(co.EspLocalUdp),
|
|
"-vconsole:emer",
|
|
"--log-file=/var/openlan/openudp.log",
|
|
}
|
|
cmd := exec.Command(UDPBin, args...)
|
|
if err := cmd.Run(); err != nil {
|
|
libol.Error("esp.init %s", err)
|
|
}
|
|
})
|
|
}
|