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 }