fea: support vxlan over ipsec.

This commit is contained in:
Daniel Ding
2024-06-08 00:24:35 +08:00
parent 0fc0ee6d6f
commit ac607c638b
11 changed files with 169 additions and 96 deletions

View File

@@ -26,6 +26,7 @@ func (o Output) Add(c *cli.Context) error {
Segment: c.Int("segment"),
Protocol: c.String("protocol"),
DstPort: c.Int("dstport"),
Secret: c.String("secret"),
}
url := o.Url(c.String("url"), network)
clt := o.NewHttp(c.String("token"))
@@ -99,8 +100,7 @@ func (o Output) Commands(app *api.App) {
&cli.StringFlag{Name: "protocol"},
&cli.StringFlag{Name: "dstport"},
//&cli.StringFlag{Name: "connection"},
//&cli.StringFlag{Name: "secret"},
//&cli.StringFlag{Name: "auth"},
&cli.StringFlag{Name: "secret"},
},
Action: o.Add,
},

View File

@@ -24,24 +24,4 @@ cat >> /etc/openlan/switch/switch.json << EOF
EOF
fi
if echo $ENABLED | grep -w "confd" -q; then
# wait confd service
while true; do
if [ -e /var/openlan/confd/confd.sock ]; then
break
fi
sleep 5
done
fi
if echo $ENABLED | grep -w "openvswitch" -q; then
# wait openvswitch service
while true; do
if [ -e /var/run/openvswitch/db.sock ]; then
break
fi
sleep 5
done
fi
exec /usr/bin/openlan-switch -conf:dir /etc/openlan/switch -log:level 20

View File

@@ -8,6 +8,7 @@ services:
entrypoint: ["/var/openlan/script/ipsec.sh"]
volumes:
- /opt/openlan/etc/ipsecd.d:/etc/ipsec.d
- /opt/openlan/run/pluto:/run/pluto
switch:
restart: always
image: "luscis/openlan:latest.x86_64"
@@ -17,6 +18,7 @@ services:
volumes:
- /opt/openlan/etc/openlan:/etc/openlan
- /opt/openlan/etc/ipsecd.d:/etc/ipsec.d
- /opt/openlan/run/pluto:/run/pluto
depends_on:
- ipsec
proxy:

View File

@@ -2,5 +2,3 @@
- [软件安装](install.md)
- [分支接入](central.md)
- [多区域互联](multiarea.md)
- [全互连网络](fabric.md)
- [IPSec网络](ipsec.md)

View File

@@ -6,4 +6,5 @@ type Output struct {
Remote string `json:"remote"`
DstPort int `json:"dstport,omitempty"`
Link string `json:"link,omitempty"` // link name
Secret string `json:"secret"`
}

View File

@@ -8,6 +8,7 @@ type Output struct {
Remote string
Segment int
Device string
Secret string
RxBytes uint64
TxBytes uint64
ErrPkt uint64

View File

@@ -130,6 +130,7 @@ func NewOutputSchema(o *Output) schema.Output {
Device: o.Device,
RxBytes: o.RxBytes,
TxBytes: o.TxBytes,
Secret: o.Secret,
AliveTime: o.UpTime(),
}
}

View File

@@ -9,7 +9,6 @@ type Index struct {
OnLines []OnLine `json:"online"`
Network []Network `json:"network"`
Clients []VPNClient `json:"clients"`
States []EspState `json:"states"`
Outputs []Output `json:"output"`
}

View File

@@ -1,45 +0,0 @@
package schema
import "net"
type Esp struct {
Name string `json:"name"`
Address string `json:"address"`
Members []EspMember `json:"members,omitempty"`
}
type EspState struct {
Name string `json:"name"`
AliveTime int64 `json:"alive"`
Spi int `json:"spi"`
Local net.IP `json:"source"`
Mode uint8 `json:"mode"`
Proto uint8 `json:"proto"`
Remote net.IP `json:"destination"`
Auth string `json:"auth"`
Crypt string `json:"crypt"`
Encap string `json:"encap" `
RemotePort int `json:"remotePort"`
TxBytes uint64 `json:"txBytes"`
TxPackages uint64 `json:"txPackages"`
RxBytes uint64 `json:"rxBytes"`
RxPackages uint64 `json:"rxPackages"`
}
type EspPolicy struct {
Name string `json:"name"`
Spi int `json:"spi"`
Local net.IP `json:"local"`
Remote net.IP `json:"remote"`
Source string `json:"source"`
Dest string `json:"destination"`
Priority int `json:"priority"`
}
type EspMember struct {
Name string `json:"name"`
Spi uint32 `json:"spi"`
Peer string `json:"peer"`
State EspState `json:"state"`
Policy []EspPolicy `json:"policy"`
}

View File

@@ -6,6 +6,7 @@ type Output struct {
Remote string `json:"remote"`
DstPort int `json:"dstPort"`
Segment int `json:"segment"`
Secret string `json:"secret"`
Device string `json:"device"`
RxBytes uint64 `json:"rxBytes"`
TxBytes uint64 `json:"txBytes"`

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"net"
"strings"
"text/template"
"time"
"github.com/luscis/openlan/pkg/api"
@@ -30,30 +31,34 @@ func NewNetworker(c *co.Network) api.Networker {
return obj
}
func toLinkName(protocol, remote string, segment int) string {
if protocol == "gre" {
return fmt.Sprintf("%s%d", "gre", segment)
}
if protocol == "vxlan" {
return fmt.Sprintf("%s%d", "vxlan", segment)
}
if segment > 0 {
return fmt.Sprintf("%s.%d", remote, segment)
}
return remote
}
type LinuxPort struct {
cfg co.Output
link string
output co.Output
link string
}
func (l *LinuxPort) String() string {
return fmt.Sprintf("%s:%s:%d", l.cfg.Protocol, l.cfg.Remote, l.cfg.Segment)
return fmt.Sprintf("%s:%s:%d", l.output.Protocol, l.output.Remote, l.output.Segment)
}
func (l *LinuxPort) GenName() {
if l.link != "" {
return
}
cfg := l.cfg
if cfg.Protocol == "gre" {
l.link = fmt.Sprintf("%s%d", "gre", cfg.Segment)
} else if cfg.Protocol == "vxlan" {
l.link = fmt.Sprintf("%s%d", "vxlan", cfg.Segment)
} else if cfg.Segment > 0 {
l.link = fmt.Sprintf("%s.%d", cfg.Remote, cfg.Segment)
} else {
l.link = cfg.Remote
}
out := l.output
l.link = toLinkName(out.Protocol, out.Remote, out.Segment)
}
type WorkerImpl struct {
@@ -179,7 +184,13 @@ func (w *WorkerImpl) AddPhysical(bridge string, output string) {
}
func (w *WorkerImpl) addOutput(bridge string, port *LinuxPort) {
cfg := port.cfg
if port.output.Secret != "" {
if err := w.addSecConn(port); err != nil {
w.out.Error("WorkerImpl.addOutput %s", err)
}
}
cfg := port.output
out := &models.Output{
Network: w.cfg.Name,
NewTime: time.Now().Unix(),
@@ -189,7 +200,6 @@ func (w *WorkerImpl) addOutput(bridge string, port *LinuxPort) {
}
mtu := 0
port.GenName()
if cfg.Protocol == "gre" {
mtu = 1450
link := &nl.Gretap{
@@ -379,8 +389,9 @@ func (w *WorkerImpl) Start(v api.Switcher) {
for _, output := range cfg.Outputs {
port := &LinuxPort{
cfg: output,
output: output,
}
port.GenName()
w.addOutput(cfg.Bridge.Name, port)
w.outputs = append(w.outputs, port)
}
@@ -458,7 +469,13 @@ func (w *WorkerImpl) DelPhysical(bridge string, output string) {
}
func (w *WorkerImpl) delOutput(bridge string, port *LinuxPort) {
cfg := port.cfg
if port.output.Secret != "" {
if err := w.delSecConn(port); err != nil {
w.out.Error("WorkerImpl.AddOutput %s", err)
}
}
cfg := port.output
w.out.Info("WorkerImpl.delOutput %s %s", port.link, port.String())
cache.Output.Del(port.link)
@@ -484,7 +501,7 @@ func (w *WorkerImpl) delOutput(bridge string, port *LinuxPort) {
w.out.Error("WorkerImpl.LinkDel %s %s", link.Name, err)
return
}
} else if port.cfg.Segment > 0 {
} else if port.output.Segment > 0 {
link := &nl.Vlan{
LinkAttrs: nl.LinkAttrs{
Name: port.link,
@@ -1069,43 +1086,161 @@ func (w *WorkerImpl) ACLer() api.ACLer {
return w.acl
}
const (
vxlanConn = `
conn vxlan{{ .Segment }}-in
keyingtries=%forever
auto=route
ike=aes_gcm256-sha2_256
esp=aes_gcm256
ikev2=insist
type=transport
left=%defaultroute
right={{ .Remote }}
authby=secret
leftprotoport=udp/8472
rightprotoport=udp
conn vxlan{{ .Segment }}-out
keyingtries=%forever
auto=route
ike=aes_gcm256-sha2_256
esp=aes_gcm256
ikev2=insist
type=transport
left=%defaultroute
right={{ .Remote }}
authby=secret
leftprotoport=udp
rightprotoport=udp/8472
`
greConn = `
conn gre{{ .Segment }}
keyingtries=%forever
auto=route
ike=aes_gcm256-sha2_256
esp=aes_gcm256
ikev2=insist
type=transport
left=%defaultroute
right={{ .Remote }}
authby=secret
leftprotoport=gre
rightprotoport=gre
`
secretConn = `
%any {{ .Remote }} : PSK "{{ .Secret }}"
`
)
func (w *WorkerImpl) addSecConn(port *LinuxPort) error {
connTmpl := ""
secTmpl := ""
name := port.link
data := port.output
if data.Protocol == "vxlan" {
connTmpl = vxlanConn
secTmpl = secretConn
} else if data.Protocol == "gre" {
connTmpl = vxlanConn
secTmpl = secretConn
}
if secTmpl != "" {
file := fmt.Sprintf("/etc/ipsec.d/%s.secrets", name)
out, err := libol.CreateFile(file)
if err != nil || out == nil {
return err
}
defer out.Close()
if tmpl, err := template.New("main").Parse(secTmpl); err != nil {
return err
} else {
if err := tmpl.Execute(out, data); err != nil {
return err
}
}
libol.Exec("ipsec", "auto", "--rereadsecrets")
}
if connTmpl != "" {
file := fmt.Sprintf("/etc/ipsec.d/%s.conf", name)
out, err := libol.CreateFile(file)
if err != nil || out == nil {
return err
}
defer out.Close()
if tmpl, err := template.New("main").Parse(connTmpl); err != nil {
return err
} else {
if err := tmpl.Execute(out, data); err != nil {
return err
}
}
if data.Protocol == "vxlan" {
libol.Exec("ipsec", "auto", "--start", "--asynchronous", name+"-in")
libol.Exec("ipsec", "auto", "--start", "--asynchronous", name+"-out")
} else if data.Protocol == "gre" {
libol.Exec("ipsec", "auto", "--start", "--asynchronous", name)
}
}
return nil
}
func (w *WorkerImpl) AddOutput(data schema.Output) {
output := co.Output{
Segment: data.Segment,
Protocol: data.Protocol,
Remote: data.Remote,
DstPort: data.DstPort,
Secret: data.Secret,
}
w.cfg.Outputs = append(w.cfg.Outputs, output)
port := &LinuxPort{
cfg: output,
output: output,
}
port.GenName()
w.addOutput(w.cfg.Bridge.Name, port)
w.outputs = append(w.outputs, port)
}
// ipsec auto --delete --asynchronous tunx-in-1
func (w *WorkerImpl) delSecConn(port *LinuxPort) error {
data := port.output
name := port.link
if data.Protocol == "vxlan" {
libol.Exec("ipsec", "auto", "--delete", "--asynchronous", name+"-in")
libol.Exec("ipsec", "auto", "--delete", "--asynchronous", name+"-out")
} else if data.Protocol == "gre" {
libol.Exec("ipsec", "auto", "--delete", "--asynchronous", name)
}
return nil
}
func (w *WorkerImpl) DelOutput(device string) {
var linuxport *LinuxPort
var port *LinuxPort
for _, v := range w.outputs {
if v.link == device {
linuxport = v
port = v
break
}
}
if linuxport == nil {
if port == nil {
return
}
Outputs := make([]co.Output, 0, len(w.cfg.Outputs))
for _, v := range w.cfg.Outputs {
if v != linuxport.cfg {
if v != port.output {
Outputs = append(Outputs, v)
}
}
w.cfg.Outputs = Outputs
w.delOutput(w.cfg.Bridge.Name, linuxport)
w.delOutput(w.cfg.Bridge.Name, port)
outputs := make([]*LinuxPort, 0, len(w.outputs))
for _, v := range w.outputs {
if v != linuxport {
if v != port {
outputs = append(outputs, v)
}
}