150 lines
3.8 KiB
Go
150 lines
3.8 KiB
Go
|
|
package tunnel
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"net"
|
||
|
|
"strconv"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/strongswan/govici/vici"
|
||
|
|
"quorate.dev/services/short/config"
|
||
|
|
)
|
||
|
|
|
||
|
|
type auth struct {
|
||
|
|
Method string `vici:"auth"`
|
||
|
|
ID string `vici:"id"`
|
||
|
|
}
|
||
|
|
type child struct {
|
||
|
|
Local []string `vici:"local_ts"`
|
||
|
|
Remote []string `vici:"remote_ts"`
|
||
|
|
Proposals []string `vici:"esp_proposals"`
|
||
|
|
In string `vici:"if_id_in"`
|
||
|
|
Out string `vici:"if_id_out"`
|
||
|
|
Rekey string `vici:"rekey_time"`
|
||
|
|
DPD string `vici:"dpd_action"`
|
||
|
|
}
|
||
|
|
type connection struct {
|
||
|
|
Version string `vici:"version"`
|
||
|
|
Remote []string `vici:"remote_addrs"`
|
||
|
|
Proposals []string `vici:"proposals"`
|
||
|
|
Local auth `vici:"local"`
|
||
|
|
Peer auth `vici:"remote"`
|
||
|
|
Children map[string]child `vici:"children"`
|
||
|
|
Encap string `vici:"encap"`
|
||
|
|
Mobike string `vici:"mobike"`
|
||
|
|
DPD string `vici:"dpd_delay"`
|
||
|
|
Rekey string `vici:"rekey_time"`
|
||
|
|
}
|
||
|
|
type shared struct {
|
||
|
|
ID string `vici:"id"`
|
||
|
|
Type string `vici:"type"`
|
||
|
|
Data string `vici:"data"`
|
||
|
|
Owners []string `vici:"owners"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func dial() (*vici.Session, error) {
|
||
|
|
return vici.NewSession(vici.WithDialContext(func(ctx context.Context, network, address string) (net.Conn, error) {
|
||
|
|
conn, err := (&net.Dialer{Timeout: 2 * time.Second}).DialContext(ctx, network, address)
|
||
|
|
if err == nil {
|
||
|
|
err = conn.SetDeadline(time.Now().Add(5 * time.Second))
|
||
|
|
if err != nil {
|
||
|
|
conn.Close()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return conn, err
|
||
|
|
}))
|
||
|
|
}
|
||
|
|
|
||
|
|
func settings(wan string, p config.Peer, index int) connection {
|
||
|
|
id := strconv.Itoa(42 + index)
|
||
|
|
return connection{
|
||
|
|
Version: "2", Remote: []string{p.Address}, Proposals: []string{"aes256-sha256-modp2048"},
|
||
|
|
Local: auth{Method: "psk", ID: wan}, Peer: auth{Method: "psk", ID: p.Address}, Encap: "yes", Mobike: "no", DPD: "10s", Rekey: "7h",
|
||
|
|
Children: map[string]child{peerName(index): {Local: []string{"0.0.0.0/0"}, Remote: []string{"0.0.0.0/0"}, Proposals: []string{"aes256gcm16-modp2048"}, In: id, Out: id, Rekey: "50m", DPD: "restart"}},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func peerName(index int) string { return fmt.Sprintf("short%d", index) }
|
||
|
|
|
||
|
|
func load(wan string, p config.Peer, index int) error {
|
||
|
|
s, err := dial()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
defer s.Close()
|
||
|
|
m, err := vici.MarshalMessage(shared{ID: peerName(index), Type: "IKE", Data: p.Secret, Owners: []string{wan, p.Address}})
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if _, err = s.CommandRequest("load-shared", m); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
m, err = vici.MarshalMessage(map[string]connection{peerName(index): settings(wan, p, index)})
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
_, err = s.CommandRequest("load-conn", m)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func initiate(index int) error {
|
||
|
|
s, err := dial()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
defer s.Close()
|
||
|
|
m := vici.NewMessage()
|
||
|
|
m.Set("child", peerName(index))
|
||
|
|
m.Set("timeout", "-1")
|
||
|
|
_, err = s.CommandRequest("initiate", m)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func installed() ([]bool, error) {
|
||
|
|
s, err := dial()
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
defer s.Close()
|
||
|
|
messages, err := s.StreamedCommandRequest("list-sas", "list-sa", nil)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return livePeers(messages), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func livePeers(messages []*vici.Message) []bool {
|
||
|
|
up := make([]bool, 2)
|
||
|
|
for _, m := range messages {
|
||
|
|
for index := range up {
|
||
|
|
conn, ok := m.Get(peerName(index)).(*vici.Message)
|
||
|
|
if !ok {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if conn.Get("state") != "ESTABLISHED" {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
children, ok := conn.Get("child-sas").(*vici.Message)
|
||
|
|
if !ok {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
for _, name := range children.Keys() {
|
||
|
|
c, ok := children.Get(name).(*vici.Message)
|
||
|
|
if ok && c.Get("state") == "INSTALLED" {
|
||
|
|
up[index] = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return up
|
||
|
|
}
|
||
|
|
|
||
|
|
func returnPeer(up []bool) int {
|
||
|
|
for index, live := range up {
|
||
|
|
if live {
|
||
|
|
return index
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return -1
|
||
|
|
}
|