quorate/services/short/tunnel/vici.go

150 lines
3.8 KiB
Go
Raw Normal View History

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
}