mirror of
				https://github.com/luscis/openlan.git
				synced 2025-10-31 04:26:21 +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)
 | |
| 		}
 | |
| 	}
 | |
| }
 | 
