├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── client.go ├── clientConn.go ├── doc.go ├── docker-compose.yaml ├── err.go ├── err_test.go ├── go.mod ├── go.sum ├── initiate.go ├── integration_test.go ├── listCerts.go ├── listConns.go ├── listSas.go ├── loadCert.go ├── loadConn.go ├── loadPrivateKey.go ├── marshal.go ├── monitorSA.go ├── msg.go ├── msg_test.go ├── pools.go ├── shared.go ├── stats.go ├── terminate.go ├── unloadConn.go └── version.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | services: 4 | - docker 5 | 6 | go: 7 | - "1.11.x" 8 | 9 | before_script: 10 | - docker-compose up -d strongswan 11 | 12 | script: 13 | - docker-compose run app go test -v ./... 14 | 15 | after_script: 16 | - docker-compose down -v 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Docker image for building the Go app 2 | FROM golang:latest 3 | 4 | WORKDIR /app 5 | ADD . /app 6 | 7 | RUN GO111MODULE=on go mod download 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 bronze1man 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | strongswan vici golang client 2 | ============================= 3 | [![Build Status](https://travis-ci.org/bronze1man/goStrongswanVici.svg)](https://travis-ci.org/bronze1man/goStrongswanVici) 4 | [![GoDoc](https://godoc.org/github.com/bronze1man/goStrongswanVici?status.svg)](https://godoc.org/github.com/bronze1man/goStrongswanVici) 5 | [![docs examples](https://sourcegraph.com/api/repos/github.com/bronze1man/goStrongswanVici/badges/docs-examples.png)](https://sourcegraph.com/github.com/bronze1man/goStrongswanVici) 6 | [![Total views](https://sourcegraph.com/api/repos/github.com/bronze1man/goStrongswanVici/counters/views.png)](https://sourcegraph.com/github.com/bronze1man/goStrongswanVici) 7 | [![GitHub issues](https://img.shields.io/github/issues/bronze1man/goStrongswanVici.svg)](https://github.com/bronze1man/goStrongswanVici/issues) 8 | [![GitHub stars](https://img.shields.io/github/stars/bronze1man/goStrongswanVici.svg)](https://github.com/bronze1man/goStrongswanVici/stargazers) 9 | [![GitHub forks](https://img.shields.io/github/forks/bronze1man/goStrongswanVici.svg)](https://github.com/bronze1man/goStrongswanVici/network) 10 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/bronze1man/goStrongswanVici/blob/master/LICENSE) 11 | 12 | a golang implement of strongswan vici plugin client. 13 | 14 | * GoStrongswanVici is not no longer actively maintained. 15 | * It is not compatible to current strongswan version like 5.8.2 . (https://github.com/bronze1man/goStrongswanVici/issues/46) 16 | * You can try https://github.com/strongswan/govici/ . 17 | 18 | ### document 19 | * http://godoc.org/github.com/bronze1man/goStrongswanVici 20 | * https://docs.strongswan.org/docs/5.9/plugins/vici.html 21 | * https://github.com/strongswan/strongswan/tree/master/src/libcharon/plugins/vici 22 | 23 | ### Implemented command list 24 | * version() 25 | * list-sas() 26 | * get-shared() 27 | * terminate() 28 | * load-conn() 29 | * load-cert() 30 | * load-key() 31 | * load-pool() 32 | * load-shared() 33 | * list-conns() 34 | * unload-conn() 35 | * unload-shared() 36 | 37 | If you need some commands, but it is not here .you can implement yourself, and send a pull request to this project. 38 | 39 | ### Testing 40 | 41 | To test the library's functionality, `docker-compose` is used to spin up strongswan in a separate Docker container. 42 | 43 | ```bash 44 | $ docker-compose up -V 45 | Creating network "gostrongswanvici_default" with the default drive 46 | Creating volume "gostrongswanvici_charondata" with default driver 47 | Creating gostrongswanvici_strongswan_1 ... done 48 | Creating gostrongswanvici_go-test_1 ... done 49 | Attaching to gostrongswanvici_strongswan_1, gostrongswanvici_go-test_1 50 | ... 51 | go-test_1 | ok github.com/RenaultAI/goStrongswanVici 0.017s 52 | gostrongswanvici_go-test_1 exited with code 0 53 | ``` 54 | 55 | ### example 56 | ```go 57 | package main 58 | 59 | import ( 60 | "fmt" 61 | "github.com/bronze1man/goStrongswanVici" 62 | ) 63 | 64 | func main(){ 65 | // create a client. 66 | client, err := goStrongswanVici.NewClientConnFromDefaultSocket() 67 | if err != nil { 68 | panic(err) 69 | } 70 | defer client.Close() 71 | 72 | // get strongswan version 73 | v, err := client.Version() 74 | if err != nil { 75 | panic(err) 76 | } 77 | fmt.Printf("%#v\n", v) 78 | 79 | childConfMap := make(map[string]goStrongswanVici.ChildSAConf) 80 | childSAConf := goStrongswanVici.ChildSAConf{ 81 | Local_ts: []string{"10.10.59.0/24"}, 82 | Remote_ts: []string{"10.10.40.0/24"}, 83 | ESPProposals: []string{"aes256-sha256-modp2048"}, 84 | StartAction: "trap", 85 | CloseAction: "restart", 86 | Mode: "tunnel", 87 | ReqID: "10", 88 | RekeyTime: "10m", 89 | InstallPolicy: "no", 90 | } 91 | childConfMap["test-child-conn"] = childSAConf 92 | 93 | localAuthConf := goStrongswanVici.AuthConf{ 94 | AuthMethod: "psk", 95 | } 96 | remoteAuthConf := goStrongswanVici.AuthConf{ 97 | AuthMethod: "psk", 98 | } 99 | 100 | ikeConfMap := make(map[string] goStrongswanVici.IKEConf) 101 | 102 | ikeConf := goStrongswanVici.IKEConf{ 103 | LocalAddrs: []string{"192.168.198.10"}, 104 | RemoteAddrs: []string{"192.168.198.11"}, 105 | Proposals: []string{"aes256-sha256-modp2048"}, 106 | Version: "1", 107 | LocalAuth: localAuthConf, 108 | RemoteAuth: remoteAuthConf, 109 | Children: childConfMap, 110 | Encap: "no", 111 | } 112 | 113 | ikeConfMap["test-connection"] = ikeConf 114 | 115 | //load connenction information into strongswan 116 | err = client.LoadConn(&ikeConfMap) 117 | if err != nil { 118 | fmt.Printf("error loading connection: %v") 119 | panic(err) 120 | } 121 | 122 | sharedKey := &goStrongswanVici.Key{ 123 | Typ: "IKE", 124 | Data: "this is the key", 125 | Owners: []string{"192.168.198.10"}, //IP of the remote host 126 | } 127 | 128 | //load shared key into strongswan 129 | err = client.LoadShared(sharedKey) 130 | if err != nil { 131 | fmt.Printf("error returned from loadsharedkey \n") 132 | panic(err) 133 | } 134 | 135 | //list-conns 136 | connList, err := client.ListConns("") 137 | if err != nil { 138 | fmt.Printf("error list-conns: %v \n", err) 139 | } 140 | 141 | for _, connection := range connList { 142 | fmt.Printf("connection map: %v", connection) 143 | } 144 | 145 | // get all conns info from strongswan 146 | connInfo, err := client.ListAllVpnConnInfo() 147 | if err != nil { 148 | panic(err) 149 | } 150 | fmt.Printf("found %d connections. \n", len(connInfo)) 151 | 152 | //unload connection from strongswan 153 | unloadConnReq := &goStrongswanVici.UnloadConnRequest{ 154 | Name: "test-connection", 155 | } 156 | err = client.UnloadConn(unloadConnReq) 157 | if err != nil { 158 | panic(err) 159 | } 160 | 161 | // kill all conns in strongswan 162 | for _, info := range connInfo { 163 | fmt.Printf("kill connection id %s\n", info.Uniqueid) 164 | err = client.Terminate(&goStrongswanVici.TerminateRequest{ 165 | Ike_id: info.Uniqueid, 166 | }) 167 | if err != nil { 168 | panic(err) 169 | } 170 | } 171 | } 172 | ``` 173 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type ClientOptions struct { 8 | Network string 9 | Addr string 10 | // Dialer creates new network connection and has priority over 11 | // Network and Addr options. 12 | Dialer func() (net.Conn, error) 13 | } 14 | 15 | type Client struct { 16 | o ClientOptions 17 | } 18 | 19 | func NewClient(options ClientOptions) (client *Client) { 20 | if options.Dialer == nil { 21 | options.Dialer = func() (net.Conn, error) { 22 | return net.Dial(options.Network, options.Addr) 23 | } 24 | } 25 | return &Client{ 26 | o: options, 27 | } 28 | } 29 | 30 | func NewClientFromDefaultSocket() (client *Client) { 31 | return NewClient(ClientOptions{ 32 | Network: "unix", 33 | Addr: "/var/run/charon.vici", 34 | }) 35 | } 36 | 37 | func (c *Client) NewConn() (conn *ClientConn, err error) { 38 | conn1, err := c.o.Dialer() 39 | if err != nil { 40 | return nil, err 41 | } 42 | return NewClientConn(conn1), nil 43 | } 44 | 45 | func (c *Client) ListSas(ike string, ike_id string) (sas []map[string]IkeSa, err error) { 46 | conn, err := c.NewConn() 47 | if err != nil { 48 | return nil, err 49 | } 50 | defer conn.Close() 51 | return conn.ListSas(ike, ike_id) 52 | } 53 | 54 | func (c *Client) ListAllVpnConnInfo() (list []VpnConnInfo, err error) { 55 | conn, err := c.NewConn() 56 | if err != nil { 57 | return nil, err 58 | } 59 | defer conn.Close() 60 | return conn.ListAllVpnConnInfo() 61 | } 62 | 63 | func (c *Client) Version() (out *Version, err error) { 64 | conn, err := c.NewConn() 65 | if err != nil { 66 | return nil, err 67 | } 68 | defer conn.Close() 69 | return conn.Version() 70 | } 71 | 72 | func (c *Client) Terminate(r *TerminateRequest) (err error) { 73 | conn, err := c.NewConn() 74 | if err != nil { 75 | return err 76 | } 77 | defer conn.Close() 78 | return conn.Terminate(r) 79 | } 80 | -------------------------------------------------------------------------------- /clientConn.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | const ( 12 | DefaultReadTimeout = 15 * time.Second 13 | ) 14 | 15 | // This object is not thread safe. 16 | // if you want concurrent, you need create more clients. 17 | type ClientConn struct { 18 | conn net.Conn 19 | responseChan chan segment 20 | eventHandlers map[string]func(response map[string]interface{}) 21 | lastError error 22 | 23 | // ReadTimeout specifies a time limit for requests made 24 | // by this client. 25 | ReadTimeout time.Duration 26 | 27 | lock sync.RWMutex 28 | } 29 | 30 | func (c *ClientConn) Close() error { 31 | c.lock.Lock() 32 | defer c.lock.Unlock() 33 | c.lastError = io.ErrClosedPipe 34 | 35 | return c.conn.Close() 36 | } 37 | 38 | func NewClientConn(conn net.Conn) (client *ClientConn) { 39 | client = &ClientConn{ 40 | conn: conn, 41 | responseChan: make(chan segment, 2), 42 | eventHandlers: map[string]func(response map[string]interface{}){}, 43 | ReadTimeout: DefaultReadTimeout, 44 | } 45 | go client.readThread() 46 | 47 | return client 48 | } 49 | 50 | // it dial from unix:///var/run/charon.vici 51 | func NewClientConnFromDefaultSocket() (client *ClientConn, err error) { 52 | conn, err := net.Dial("unix", "/var/run/charon.vici") 53 | if err != nil { 54 | return 55 | } 56 | 57 | return NewClientConn(conn), nil 58 | } 59 | 60 | func (c *ClientConn) Request(apiname string, request map[string]interface{}) (response map[string]interface{}, err error) { 61 | err = writeSegment(c.conn, segment{ 62 | typ: stCMD_REQUEST, 63 | name: apiname, 64 | msg: request, 65 | }) 66 | if err != nil { 67 | fmt.Printf("error writing segment \n") 68 | 69 | return 70 | } 71 | 72 | outMsg := c.readResponse() 73 | c.lock.RLock() 74 | err = c.lastError 75 | if err != nil { 76 | c.lock.RUnlock() 77 | return nil, err 78 | } 79 | c.lock.RUnlock() 80 | 81 | if outMsg.typ != stCMD_RESPONSE { 82 | return nil, fmt.Errorf("[%s] response error %d", apiname, outMsg.typ) 83 | } 84 | 85 | return outMsg.msg, nil 86 | } 87 | 88 | func (c *ClientConn) readResponse() segment { 89 | select { 90 | case outMsg := <-c.responseChan: 91 | return outMsg 92 | case <-time.After(c.ReadTimeout): 93 | if c.lastError == nil { 94 | c.lock.Lock() 95 | c.lastError = fmt.Errorf("Timeout waiting for message response") 96 | c.lock.Unlock() 97 | } 98 | 99 | return segment{} 100 | } 101 | } 102 | 103 | func (c *ClientConn) RegisterEvent(name string, handler func(response map[string]interface{})) (err error) { 104 | c.lock.Lock() 105 | if c.eventHandlers[name] != nil { 106 | c.lock.Unlock() 107 | return fmt.Errorf("[event %s] register a event twice.", name) 108 | } 109 | 110 | c.eventHandlers[name] = handler 111 | err = writeSegment(c.conn, segment{ 112 | typ: stEVENT_REGISTER, 113 | name: name, 114 | }) 115 | if err != nil { 116 | delete(c.eventHandlers, name) 117 | c.lock.Unlock() 118 | 119 | return 120 | } 121 | c.lock.Unlock() 122 | outMsg := c.readResponse() 123 | // fmt.Printf("registerEvent %#v\n", outMsg) 124 | c.lock.Lock() 125 | lastError := c.lastError 126 | 127 | if lastError != nil { 128 | delete(c.eventHandlers, name) 129 | c.lock.Unlock() 130 | 131 | return err 132 | } 133 | 134 | if outMsg.typ != stEVENT_CONFIRM { 135 | delete(c.eventHandlers, name) 136 | c.lock.Unlock() 137 | 138 | return fmt.Errorf("[event %s] response error %d", name, outMsg.typ) 139 | } 140 | c.lock.Unlock() 141 | 142 | return nil 143 | } 144 | 145 | func (c *ClientConn) UnregisterEvent(name string) (err error) { 146 | err = writeSegment(c.conn, segment{ 147 | typ: stEVENT_UNREGISTER, 148 | name: name, 149 | }) 150 | if err != nil { 151 | return 152 | } 153 | 154 | outMsg := c.readResponse() 155 | // fmt.Printf("UnregisterEvent %#v\n", outMsg) 156 | c.lock.Lock() 157 | if c.lastError != nil { 158 | c.lock.Unlock() 159 | 160 | return c.lastError 161 | } 162 | c.lock.Unlock() 163 | 164 | if outMsg.typ != stEVENT_CONFIRM { 165 | return fmt.Errorf("[event %s] response error %d", name, outMsg.typ) 166 | } 167 | 168 | c.lock.Lock() 169 | delete(c.eventHandlers, name) 170 | c.lock.Unlock() 171 | 172 | return nil 173 | } 174 | 175 | func (c *ClientConn) readThread() { 176 | defer close(c.responseChan) 177 | 178 | for { 179 | outMsg, err := readSegment(c.conn) 180 | if err != nil { 181 | c.lock.Lock() 182 | c.lastError = err 183 | c.lock.Unlock() 184 | 185 | return 186 | } 187 | 188 | switch outMsg.typ { 189 | case stCMD_RESPONSE, stEVENT_CONFIRM: 190 | c.responseChan <- outMsg 191 | case stEVENT: 192 | c.lock.Lock() 193 | handler := c.eventHandlers[outMsg.name] 194 | c.lock.Unlock() 195 | 196 | if handler != nil { 197 | handler(outMsg.msg) 198 | } 199 | default: 200 | c.lock.Lock() 201 | c.lastError = fmt.Errorf("[Client.readThread] unknow msg type %d", outMsg.typ) 202 | c.lock.Unlock() 203 | 204 | return 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | a golang implement of strongswan vici plugin client. 3 | https://github.com/strongswan/strongswan/tree/master/src/libcharon/plugins/vici 4 | */ 5 | package goStrongswanVici 6 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | strongswan: 4 | image: philplckthun/strongswan 5 | ports: 6 | - "500:500/udp" 7 | - "4500:4500/udp" 8 | - "1701:1701/udp" 9 | privileged: true 10 | volumes: 11 | - "charondata:/var/run" 12 | 13 | app: 14 | build: . 15 | volumes: 16 | - ".:/app" 17 | - "charondata:/var/run" 18 | depends_on: 19 | - strongswan 20 | 21 | volumes: 22 | charondata: 23 | -------------------------------------------------------------------------------- /err.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func handlePanic(f func() error) (err error) { 8 | defer func() { 9 | r := recover() 10 | //no panic 11 | if r == nil { 12 | return 13 | } 14 | //panic a error 15 | if e, ok := r.(error); ok { 16 | err = e 17 | return 18 | } 19 | //panic another stuff 20 | err = fmt.Errorf("%s", r) 21 | }() 22 | err = f() 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /err_test.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestHandlePanic(ot *testing.T) { 9 | err := handlePanic(func() error { 10 | panic("1") 11 | }) 12 | if err == nil { 13 | panic("err==nil") 14 | } 15 | if err.Error() != "1" { 16 | panic(`err.Error()!="1"`) 17 | } 18 | 19 | err = handlePanic(func() error { 20 | return fmt.Errorf("%d", 2) 21 | }) 22 | if err == nil { 23 | panic("err==nil") 24 | } 25 | if err.Error() != "2" { 26 | panic(`err.Error()!="2" ` + err.Error()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bronze1man/goStrongswanVici 2 | 3 | require ( 4 | github.com/davecgh/go-spew v1.1.1 // indirect 5 | github.com/pmezard/go-difflib v1.0.0 // indirect 6 | github.com/stretchr/testify v1.2.2 7 | ) 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 6 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 7 | -------------------------------------------------------------------------------- /initiate.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Initiate is used to initiate an SA. This is the 8 | // equivalent of `swanctl --initiate -c childname` 9 | func (c *ClientConn) Initiate(child string, ike string) (err error) { 10 | inMap := map[string]interface{}{} 11 | if child != "" { 12 | inMap["child"] = child 13 | } 14 | if ike != "" { 15 | inMap["ike"] = ike 16 | } 17 | msg, err := c.Request("initiate", inMap) 18 | if err != nil { 19 | return err 20 | } 21 | if msg["success"] != "yes" { 22 | return fmt.Errorf("unsuccessful Initiate: %v", msg["errmsg"]) 23 | } 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /integration_test.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestConnection(t *testing.T) { 11 | client, err := NewClientConnFromDefaultSocket() 12 | require.NoError(t, err) 13 | defer client.Close() 14 | 15 | // Get initial list of connections. 16 | initialConnections, err := client.ListConns("") 17 | require.NoError(t, err) 18 | 19 | // Create connection object. 20 | childConfMap := make(map[string]ChildSAConf) 21 | childSAConf := ChildSAConf{ 22 | Local_ts: []string{"10.10.59.0/24"}, 23 | Remote_ts: []string{"10.10.40.0/24"}, 24 | ESPProposals: []string{"aes256-sha256-modp2048"}, 25 | StartAction: "trap", 26 | CloseAction: "restart", 27 | Mode: "tunnel", 28 | ReqID: "10", 29 | RekeyTime: "10m", 30 | InstallPolicy: "no", 31 | } 32 | childConfMap["test-child-conn"] = childSAConf 33 | 34 | localAuthConf := AuthConf{ 35 | AuthMethod: "psk", 36 | } 37 | remoteAuthConf := AuthConf{ 38 | AuthMethod: "psk", 39 | } 40 | ikeConf := IKEConf{ 41 | LocalAddrs: []string{"192.168.198.10"}, 42 | RemoteAddrs: []string{"192.168.198.11"}, 43 | Proposals: []string{"aes256-sha256-modp2048"}, 44 | Version: "1", 45 | LocalAuth: localAuthConf, 46 | RemoteAuth: remoteAuthConf, 47 | Children: childConfMap, 48 | Encap: "no", 49 | } 50 | ikeConfMap := map[string]IKEConf{"test-connection": ikeConf} 51 | 52 | // Add connection. 53 | err = client.LoadConn(&ikeConfMap) 54 | require.NoError(t, err) 55 | 56 | // Verify connection is added. 57 | connections, err := client.ListConns("") 58 | require.NoError(t, err) 59 | assert.Len(t, connections, len(initialConnections)+1) 60 | } 61 | -------------------------------------------------------------------------------- /listCerts.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type Cert struct { 9 | Type string 10 | Flag string 11 | HasPrivKey bool 12 | Data string 13 | Subject string 14 | NotBefore string 15 | NotAfter string 16 | } 17 | 18 | // typ = certificate type to filter for, X509|X509_AC|X509_CRL|OCSP_RESPONSE|PUBKEY or ANY 19 | // flag = X.509 certificate flag to filter for, NONE|CA|AA|OCSP or ANY 20 | // subject = set to list only certificates having subject 21 | func (c *ClientConn) ListCerts(typ, flag, subject string) ([]*Cert, error) { 22 | certs := []*Cert{} 23 | var eventErr error 24 | err := c.RegisterEvent("list-cert", func(response map[string]interface{}) { 25 | cert := &Cert{} 26 | cert.Type, _ = response["type"].(string) 27 | cert.Flag, _ = response["flag"].(string) 28 | cert.HasPrivKey, _ = response["hasPrivKey"].(bool) 29 | cert.Data, _ = response["data"].(string) 30 | cert.Subject, _ = response["subject"].(string) 31 | cert.NotBefore, _ = response["notBefore"].(string) 32 | cert.NotAfter, _ = response["notAfter"].(string) 33 | if hasPri, ok := response["hasPriKey"].(string); ok { 34 | cert.HasPrivKey, _ = strconv.ParseBool(hasPri) 35 | } 36 | certs = append(certs, cert) 37 | }) 38 | if err != nil { 39 | return nil, fmt.Errorf("error registering list-cert event: %v", err) 40 | } 41 | if eventErr != nil { 42 | return nil, eventErr 43 | } 44 | reqMap := map[string]interface{}{ 45 | "type": typ, 46 | "flag": flag, 47 | "subject": subject, 48 | } 49 | _, err = c.Request("list-certs", reqMap) 50 | if err != nil { 51 | return nil, fmt.Errorf("error requesting list-certs: %v", err) 52 | } 53 | 54 | err = c.UnregisterEvent("list-cert") 55 | if err != nil { 56 | return nil, fmt.Errorf("error unregistering list-cert event: %v", err) 57 | } 58 | return certs, nil 59 | } 60 | -------------------------------------------------------------------------------- /listConns.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func (c *ClientConn) ListConns(ike string) ([]map[string]IKEConf, error) { 8 | conns := []map[string]IKEConf{} 9 | var eventErr error 10 | var err error 11 | 12 | err = c.RegisterEvent("list-conn", func(response map[string]interface{}) { 13 | conn := &map[string]IKEConf{} 14 | err = ConvertFromGeneral(response, conn) 15 | if err != nil { 16 | eventErr = fmt.Errorf("list-conn event error: %v", err) 17 | return 18 | } 19 | conns = append(conns, *conn) 20 | }) 21 | 22 | if err != nil { 23 | return nil, fmt.Errorf("error registering list-conn event: %v", err) 24 | } 25 | 26 | if eventErr != nil { 27 | return nil, eventErr 28 | } 29 | 30 | reqMap := map[string]interface{}{} 31 | 32 | if ike != "" { 33 | reqMap["ike"] = ike 34 | } 35 | 36 | _, err = c.Request("list-conns", reqMap) 37 | if err != nil { 38 | return nil, fmt.Errorf("error requesting list-conns: %v", err) 39 | } 40 | 41 | err = c.UnregisterEvent("list-conn") 42 | if err != nil { 43 | return nil, fmt.Errorf("error unregistering list-conns event: %v", err) 44 | } 45 | 46 | return conns, nil 47 | } 48 | -------------------------------------------------------------------------------- /listSas.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | //from list-sa event 9 | type IkeSa struct { 10 | Uniqueid string `json:"uniqueid"` //called ike_id in terminate() argument. 11 | Version string `json:"version"` 12 | State string `json:"state"` //had saw: ESTABLISHED 13 | Local_host string `json:"local-host"` 14 | Local_port string `json:"local-port"` 15 | Local_id string `json:"local-id"` 16 | Remote_host string `json:"remote-host"` 17 | Remote_port string `json:"remote-port"` 18 | Remote_id string `json:"remote-id"` 19 | Remote_xauth_id string `json:"remote-xauth-id"` //client username for ikev1 20 | Remote_eap_id string `json:"remote-eap-id"` //client username for ikev2 21 | Initiator string `json:"initiator"` 22 | Initiator_spi string `json:"initiator-spi"` 23 | Responder_spi string `json:"responder-spi"` 24 | Encr_alg string `json:"encr-alg"` 25 | Encr_keysize string `json:"encr-keysize"` 26 | Integ_alg string `json:"integ-alg"` 27 | Integ_keysize string `json:"integ-keysize"` 28 | Prf_alg string `json:"prf-alg"` 29 | Dh_group string `json:"dh-group"` 30 | Established string `json:"established"` 31 | Rekey_time string `json:"rekey-time"` 32 | Reauth_time string `json:"reauth-time"` 33 | Remote_vips []string `json:"remote-vips"` 34 | Child_sas map[string]Child_sas `json:"child-sas"` //key means child-sa-name(conn name in ipsec.conf) 35 | Tasks_active []string `json:"tasks-active"` 36 | Tasks_queued []string `json:"tasks-queued"` 37 | } 38 | 39 | type Child_sas struct { 40 | Reqid string `json:"reqid"` 41 | State string `json:"state"` //had saw: INSTALLED 42 | Mode string `json:"mode"` //had saw: TUNNEL 43 | Protocol string `json:"protocol"` 44 | Encap string `json:"encap"` 45 | Spi_in string `json:"spi-in"` 46 | Spi_out string `json:"spi-out"` 47 | Cpi_in string `json:"cpi-in"` 48 | Cpi_out string `json:"cpi-out"` 49 | Encr_alg string `json:"encr-alg"` 50 | Encr_keysize string `json:"encr-keysize"` 51 | Integ_alg string `json:"integ-alg"` 52 | Integ_keysize string `json:"integ-keysize"` 53 | Prf_alg string `json:"prf-alg"` 54 | Dh_group string `json:"dh-group"` 55 | Esn string `json:"esn"` 56 | Bytes_in string `json:"bytes-in"` //bytes into this machine 57 | Packets_in string `json:"packets-in"` 58 | Use_in string `json:"use-in"` 59 | Bytes_out string `json:"bytes-out"` // bytes out of this machine 60 | Packets_out string `json:"packets-out"` 61 | Use_out string `json:"use-out"` 62 | Rekey_time string `json:"rekey-time"` 63 | Life_time string `json:"life-time"` 64 | Install_time string `json:"install-time"` 65 | Local_ts []string `json:"local-ts"` 66 | Remote_ts []string `json:"remote-ts"` 67 | } 68 | 69 | func (s *Child_sas) GetBytesIn() uint64 { 70 | num, err := strconv.ParseUint(s.Bytes_in, 10, 64) 71 | if err != nil { 72 | return 0 73 | } 74 | return num 75 | } 76 | 77 | func (s *Child_sas) GetBytesOut() uint64 { 78 | num, err := strconv.ParseUint(s.Bytes_out, 10, 64) 79 | if err != nil { 80 | return 0 81 | } 82 | return num 83 | } 84 | 85 | func (s *Child_sas) GetPacketsIn() uint64 { 86 | num, err := strconv.ParseUint(s.Packets_in, 10, 64) 87 | if err != nil { 88 | return 0 89 | } 90 | return num 91 | } 92 | 93 | func (s *Child_sas) GetPacketsOut() uint64 { 94 | num, err := strconv.ParseUint(s.Packets_out, 10, 64) 95 | if err != nil { 96 | return 0 97 | } 98 | return num 99 | } 100 | 101 | // To be simple, list all clients that are connecting to this server . 102 | // A client is a sa. 103 | // Lists currently active IKE_SAs 104 | func (c *ClientConn) ListSas(ike string, ike_id string) (sas []map[string]IkeSa, err error) { 105 | sas = []map[string]IkeSa{} 106 | var eventErr error 107 | //register event 108 | err = c.RegisterEvent("list-sa", func(response map[string]interface{}) { 109 | sa := &map[string]IkeSa{} 110 | err = ConvertFromGeneral(response, sa) 111 | if err != nil { 112 | fmt.Printf("list-sa event error: %s\n", err) 113 | eventErr = err 114 | return 115 | } 116 | sas = append(sas, *sa) 117 | //fmt.Printf("event %#v\n", response) 118 | }) 119 | if err != nil { 120 | return 121 | } 122 | if eventErr != nil { 123 | return 124 | } 125 | 126 | inMap := map[string]interface{}{} 127 | if ike != "" { 128 | inMap["ike"] = ike 129 | } 130 | if ike_id != "" { 131 | inMap["ike_id"] = ike_id 132 | } 133 | _, err = c.Request("list-sas", inMap) 134 | if err != nil { 135 | return 136 | } 137 | //fmt.Printf("request finish %#v\n", sas) 138 | err = c.UnregisterEvent("list-sa") 139 | if err != nil { 140 | return 141 | } 142 | return 143 | } 144 | 145 | //a vpn conn in the strongswan server 146 | type VpnConnInfo struct { 147 | IkeSa 148 | Child_sas 149 | IkeSaName string //looks like conn name in ipsec.conf, content is same as ChildSaName 150 | ChildSaName string //looks like conn name in ipsec.conf 151 | } 152 | 153 | func (c *VpnConnInfo) GuessUserName() string { 154 | if c.Remote_xauth_id != "" { 155 | return c.Remote_xauth_id 156 | } 157 | if c.Remote_id != "" { 158 | return c.Remote_id 159 | } 160 | return "" 161 | } 162 | 163 | // a helper method to avoid complex data struct in ListSas 164 | // if it only have one child_sas ,it will put it into info.Child_sas 165 | func (c *ClientConn) ListAllVpnConnInfo() (list []VpnConnInfo, err error) { 166 | sasList, err := c.ListSas("", "") 167 | if err != nil { 168 | return 169 | } 170 | list = make([]VpnConnInfo, len(sasList)) 171 | for i, sa := range sasList { 172 | info := VpnConnInfo{} 173 | if len(sa) != 1 { 174 | fmt.Printf("[vici.ListAllVpnConnInfo] warning: len(sa)[%d]!=1\n", len(sa)) 175 | } 176 | for ikeSaName, ikeSa := range sa { 177 | info.IkeSaName = ikeSaName 178 | info.IkeSa = ikeSa 179 | //if len(ikeSa.Child_sas) != 1 { 180 | // fmt.Println("[vici.ListAllVpnConnInfo] warning: len(ikeSa.Child_sas)[%d]!=1", len(ikeSa.Child_sas)) 181 | //} 182 | for childSaName, childSa := range ikeSa.Child_sas { 183 | info.ChildSaName = childSaName 184 | info.Child_sas = childSa 185 | break 186 | } 187 | break 188 | } 189 | if len(info.IkeSa.Child_sas) == 1 { 190 | info.IkeSa.Child_sas = nil 191 | } 192 | list[i] = info 193 | } 194 | return 195 | } 196 | -------------------------------------------------------------------------------- /loadCert.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type certPayload struct { 8 | Typ string `json:"type"` // (X509|X509_AC|X509_CRL) 9 | Flag string `json:"flag"` // (CA|AA|OCSP|NONE) 10 | Data string `json:"data"` 11 | } 12 | 13 | func (c *ClientConn) LoadCertificate(s string, typ string, flag string) (err error) { 14 | requestMap := &map[string]interface{}{} 15 | 16 | var k = certPayload{ 17 | Typ: typ, 18 | Flag: flag, 19 | Data: s, 20 | } 21 | 22 | if err = ConvertToGeneral(k, requestMap); err != nil { 23 | return fmt.Errorf("error creating request: %v", err) 24 | } 25 | 26 | msg, err := c.Request("load-cert", *requestMap) 27 | 28 | if err != nil { 29 | return fmt.Errorf("unsuccessful loadCert: %v", err.Error()) 30 | } 31 | 32 | if msg["success"] != "yes" { 33 | return fmt.Errorf("unsuccessful loadCert: %v", msg["errmsg"]) 34 | } 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /loadConn.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "crypto" 5 | "crypto/x509" 6 | "encoding/pem" 7 | "fmt" 8 | ) 9 | 10 | type Connection struct { 11 | ConnConf map[string]IKEConf `json:"connections"` 12 | } 13 | 14 | type IKEConf struct { 15 | LocalAddrs []string `json:"local_addrs"` 16 | RemoteAddrs []string `json:"remote_addrs,omitempty"` 17 | LocalPort string `json:"local_port,omitempty"` 18 | RemotePort string `json:"remote_port,omitempty"` 19 | Proposals []string `json:"proposals,omitempty"` 20 | Vips []string `json:"vips,omitempty"` 21 | Version string `json:"version"` //1 for ikev1, 0 for ikev1 & ikev2 22 | Encap string `json:"encap"` //yes,no 23 | KeyingTries string `json:"keyingtries"` 24 | RekeyTime string `json:"rekey_time"` 25 | DPDDelay string `json:"dpd_delay,omitempty"` 26 | LocalAuth AuthConf `json:"local"` 27 | RemoteAuth AuthConf `json:"remote"` 28 | Pools []string `json:"pools,omitempty"` 29 | Children map[string]ChildSAConf `json:"children"` 30 | Mobike string `json:"mobike,omitempty"` 31 | } 32 | 33 | type AuthConf struct { 34 | ID string `json:"id"` 35 | Round string `json:"round,omitempty"` 36 | AuthMethod string `json:"auth"` // (psk|pubkey) 37 | EAP_ID string `json:"eap_id,omitempty"` 38 | PubKeys []string `json:"pubkeys,omitempty"` // PEM encoded public keys 39 | } 40 | 41 | type ChildSAConf struct { 42 | Local_ts []string `json:"local_ts"` 43 | Remote_ts []string `json:"remote_ts"` 44 | ESPProposals []string `json:"esp_proposals,omitempty"` //aes128-sha1_modp1024 45 | StartAction string `json:"start_action"` //none,trap,start 46 | CloseAction string `json:"close_action"` 47 | ReqID string `json:"reqid,omitempty"` 48 | RekeyTime string `json:"rekey_time"` 49 | ReplayWindow string `json:"replay_window,omitempty"` 50 | Mode string `json:"mode"` 51 | InstallPolicy string `json:"policies"` 52 | UpDown string `json:"updown,omitempty"` 53 | Priority string `json:"priority,omitempty"` 54 | MarkIn string `json:"mark_in,omitempty"` 55 | MarkOut string `json:"mark_out,omitempty"` 56 | DpdAction string `json:"dpd_action,omitempty"` 57 | LifeTime string `json:"life_time,omitempty"` 58 | } 59 | 60 | // SetPublicKeys is a helper method that converts Public Keys to x509 PKIX PEM format 61 | // Supported formats are those implemented by x509.MarshalPKIXPublicKey 62 | func (a *AuthConf) SetPublicKeys(keys []crypto.PublicKey) error { 63 | var newKeys []string 64 | 65 | for _, key := range keys { 66 | asn1Bytes, err := x509.MarshalPKIXPublicKey(key) 67 | if err != nil { 68 | return fmt.Errorf("Error marshaling key: %v", err) 69 | } 70 | pemKey := pem.Block{ 71 | Type: "PUBLIC KEY", 72 | Bytes: asn1Bytes, 73 | } 74 | pemBytes := pem.EncodeToMemory(&pemKey) 75 | newKeys = append(newKeys, string(pemBytes)) 76 | } 77 | 78 | a.PubKeys = newKeys 79 | return nil 80 | } 81 | 82 | func (c *ClientConn) LoadConn(conn *map[string]IKEConf) error { 83 | requestMap := &map[string]interface{}{} 84 | 85 | err := ConvertToGeneral(conn, requestMap) 86 | 87 | if err != nil { 88 | return fmt.Errorf("error creating request: %v", err) 89 | } 90 | 91 | msg, err := c.Request("load-conn", *requestMap) 92 | 93 | if msg["success"] != "yes" { 94 | return fmt.Errorf("unsuccessful LoadConn: %v", msg["errmsg"]) 95 | } 96 | 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /loadPrivateKey.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "encoding/pem" 8 | "fmt" 9 | ) 10 | 11 | type keyPayload struct { 12 | Typ string `json:"type"` 13 | Data string `json:"data"` 14 | } 15 | 16 | // LoadECDSAPrivateKey encodes a *ecdsa.PrivateKey as a PEM block before sending 17 | // it to the Vici interface 18 | func (c *ClientConn) LoadECDSAPrivateKey(key *ecdsa.PrivateKey) error { 19 | mk, err := x509.MarshalECPrivateKey(key) 20 | 21 | if err != nil { 22 | return err 23 | } 24 | 25 | var pemData = pem.EncodeToMemory(&pem.Block{ 26 | Type: "ECDSA PRIVATE KEY", 27 | Bytes: mk, 28 | }) 29 | 30 | return c.loadPrivateKey("ECDSA", string(pemData)) 31 | } 32 | 33 | // LoadRSAPrivateKey encodes a *rsa.PrivateKey as a PEM block before sending 34 | // it to the Vici interface 35 | func (c *ClientConn) LoadRSAPrivateKey(key *rsa.PrivateKey) error { 36 | var mk = x509.MarshalPKCS1PrivateKey(key) 37 | 38 | var pemData = pem.EncodeToMemory(&pem.Block{ 39 | Type: "RSA PRIVATE KEY", 40 | Bytes: mk, 41 | }) 42 | 43 | return c.loadPrivateKey("RSA", string(pemData)) 44 | } 45 | 46 | // loadPrivateKey expects typ to be (RSA|ECDSA) and a PEM encoded data as a 47 | // string 48 | func (c *ClientConn) loadPrivateKey(typ, data string) (err error) { 49 | requestMap := &map[string]interface{}{} 50 | 51 | var k = keyPayload{ 52 | Typ: typ, 53 | Data: data, 54 | } 55 | 56 | if err = ConvertToGeneral(k, requestMap); err != nil { 57 | return fmt.Errorf("error creating request: %v", err) 58 | } 59 | 60 | msg, err := c.Request("load-key", *requestMap) 61 | if msg["success"] != "yes" { 62 | return fmt.Errorf("unsuccessful loadPrivateKey: %v", msg["errmsg"]) 63 | } 64 | 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /marshal.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | //concrete data type to general data type 8 | // concrete data type like *Version 9 | // general data type include map[string]interface{} []string string 10 | // TODO make it faster 11 | func ConvertToGeneral(concrete interface{}, general interface{}) (err error) { 12 | b, err := json.Marshal(concrete) 13 | if err != nil { 14 | return 15 | } 16 | return json.Unmarshal(b, general) 17 | } 18 | 19 | // general data type to concrete data type 20 | // concrete data type like *Version 21 | // general data type include map[string]interface{} []string string 22 | // TODO make it faster 23 | func ConvertFromGeneral(general interface{}, concrete interface{}) (err error) { 24 | b, err := json.Marshal(general) 25 | if err != nil { 26 | return 27 | } 28 | return json.Unmarshal(b, concrete) 29 | } 30 | -------------------------------------------------------------------------------- /monitorSA.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "time" 7 | ) 8 | 9 | const ( 10 | EVENT_IKE_UPDOWN = "ike-updown" 11 | EVENT_IKE_REKEY = "ike-rekey" 12 | EVENT_CHILD_UPDOWN = "child-updown" 13 | EVENT_CHILD_REKEY = "child-rekey" 14 | ) 15 | 16 | type EventIkeSAUpDown struct { 17 | Child_sas map[string]*EventChildSAUpDown `json:"child-sas"` 18 | Dh_group string `json:"dh-group"` 19 | Encr_keysize string `json:"encr-keysize"` 20 | Encr_alg string `json:"encr-alg"` 21 | Established string `json:"established"` 22 | Initiator_spi string `json:"initiator-spi"` 23 | Integ_alg string `json:"integ-alg"` 24 | Local_id string `json:"local-id"` 25 | Local_host string `json:"local-host"` 26 | Local_port string `json:"local-port"` 27 | Nat_any string `json:"nat-any"` 28 | Nat_remote string `json:"nat-remote"` 29 | Prf_alg string `json:"prf-alg"` 30 | Rekey_time string `json:"rekey-time"` 31 | Remote_id string `json:"remote-id"` 32 | Remote_host string `json:"remote-host"` 33 | Remote_port string `json:"remote-port"` 34 | Remote_vips []string `json:"remote-vips"` 35 | Responder_spi string `json:"responder-spi"` 36 | State string `json:"state"` 37 | Task_Active []string `json:"tasks-active"` 38 | Task_Passive []string `json:"tasks-passive"` 39 | Uniqueid string `json:"uniqueid"` 40 | Version string `json:"version"` 41 | Remote_eap_id string `json:"remote-eap-id"` // client user name 42 | } 43 | 44 | type EventChildSAUpDown struct { 45 | Bytes_in string `json:"bytes-in"` 46 | Bytes_out string `json:"bytes-out"` 47 | Encap string `json:"encap"` 48 | Encr_alg string `json:"encr-alg"` 49 | Encr_keysize string `json:"encr-keysize"` 50 | Integ_alg string `json:"integ-alg"` 51 | Install_time string `json:"install-time"` 52 | Life_time string `json:"life-time"` 53 | Local_ts []string `json:"local-ts"` 54 | Mode string `json:"mode"` 55 | Name string `json:"name"` 56 | Protocol string `json:"protocol"` 57 | Packets_out string `json:"packets-out"` 58 | Packets_in string `json:"packets-in"` 59 | Rekey_time string `json:"rekey-time"` 60 | Remote_ts []string `json:"remote-ts"` 61 | Reqid string `json:"reqid"` 62 | Spi_in string `json:"spi-in"` 63 | Spi_out string `json:"spi-out"` 64 | State string `json:"state"` 65 | UniqueId string `json:"uniqueid"` 66 | Remote_eap_id string `json:"remote-eap-id"` // client user name 67 | } 68 | 69 | type EventIkeRekeyPair struct { 70 | New EventIkeRekeySA `json:"new"` 71 | Old EventIkeRekeySA `json:"old"` 72 | } 73 | 74 | type EventIkeRekeySA struct { 75 | Child_sas map[string]*EventChildRekeyPair `json:"child-sas"` 76 | Dh_group string `json:"dh-group"` 77 | Encr_alg string `json:"encr-alg"` 78 | Encr_keysize string `json:"encr-keysize"` 79 | Established string `json:"established"` 80 | Initiator_spi string `json:"initiator-spi"` 81 | Integ_alg string `json:"integ-alg"` 82 | Local_host string `json:"local-host"` 83 | Local_port string `json:"local-port"` 84 | Local_id string `json:"local-id"` 85 | Nat_any string `json:"nat-any"` 86 | Nat_remote string `json:"nat-remote"` 87 | Prf_alg string `json:"prf-alg"` 88 | Rekey_time string `json:"rekey-time"` 89 | Remote_id string `json:"remote-id"` 90 | Remote_host string `json:"remote-host"` 91 | Remote_port string `json:"remote-port"` 92 | Remote_vips []string `json:"remote-vips"` 93 | Responder_spi string `json:"responder-spi"` 94 | State string `json:"state"` 95 | Task_Active []string `json:"tasks-active"` 96 | Task_Passive []string `json:"tasks-passive"` 97 | Uniqueid string `json:"uniqueid"` 98 | Version string `json:"version"` 99 | Remote_eap_id string `json:"remote-eap-id"` // client user name 100 | } 101 | 102 | type EventChildRekeyPair struct { 103 | New EventChildRekeySA `json:"new"` 104 | Old EventChildRekeySA `json:"old"` 105 | } 106 | 107 | type EventChildRekeySA struct { 108 | Bytes_in string `json:"bytes-in"` 109 | Bytes_out string `json:"bytes-out"` 110 | Encap string `json:"encap"` 111 | Encr_alg string `json:"encr-alg"` 112 | Encr_keysize string `json:"encr-keysize"` 113 | Integ_alg string `json:"integ-alg"` 114 | Install_time string `json:"install-time"` 115 | Life_time string `json:"life-time"` 116 | Local_ts []string `json:"local-ts"` 117 | Mode string `json:"mode"` 118 | Name string `json:"name"` 119 | Packets_in string `json:"packets-in"` 120 | Packets_out string `json:"packets-out"` 121 | Protocol string `json:"protocol"` 122 | Remote_ts []string `json:"remote-ts"` 123 | Rekey_time string `json:"rekey-time"` 124 | Reqid string `json:"reqid"` 125 | Spi_in string `json:"spi-in"` 126 | Spi_out string `json:"spi-out"` 127 | State string `json:"state"` 128 | Use_in string `json:"use-in"` 129 | Use_out string `json:"use-out"` 130 | UniqueId string `json:"uniqueid"` 131 | } 132 | 133 | type EventIkeUpDown struct { 134 | Up bool 135 | Ike map[string]*EventIkeSAUpDown 136 | } 137 | 138 | type EventIkeRekey struct { 139 | Ike map[string]*EventIkeRekeyPair 140 | } 141 | 142 | type EventChildRekey struct { 143 | Ike map[string]*EventIkeRekeySA 144 | } 145 | 146 | type EventChildUpDown struct { 147 | Up bool 148 | Ike map[string]*EventIkeSAUpDown 149 | } 150 | 151 | type EventIkeSa struct { 152 | IkeSa 153 | TasksActive []string `json:"tasks-active"` 154 | } 155 | 156 | type EventInfo struct { 157 | Up bool 158 | Ike map[string]*EventIkeSa 159 | } 160 | 161 | func prettyprint(b []byte) string { 162 | var out bytes.Buffer 163 | json.Indent(&out, b, "", " ") 164 | return string(out.Bytes()) 165 | } 166 | 167 | type monitorCallBack func(event string, info interface{}) 168 | 169 | func handleIkeUpDown(eventName string, callback monitorCallBack, response map[string]interface{}) { 170 | event := &EventIkeUpDown{} 171 | event.Ike = map[string]*EventIkeSAUpDown{} 172 | //we need to marshall all ikes manual because json uses connections names as key 173 | for name := range response { 174 | value := response[name] 175 | if name == "up" { 176 | event.Up = true 177 | } else { 178 | sa := &EventIkeSAUpDown{} 179 | ConvertFromGeneral(value, sa) 180 | event.Ike[name] = sa 181 | } 182 | } 183 | callback(eventName, event) 184 | } 185 | 186 | func handleIkeRekey(eventName string, callback monitorCallBack, response map[string]interface{}) { 187 | event := &EventIkeRekey{} 188 | event.Ike = map[string]*EventIkeRekeyPair{} 189 | //we need to marshall all ikes manual because json uses connections names as key 190 | for name := range response { 191 | value := response[name] 192 | sa := &EventIkeRekeyPair{} 193 | ConvertFromGeneral(value, sa) 194 | event.Ike[name] = sa 195 | } 196 | callback(eventName, event) 197 | } 198 | 199 | func handleChildUpDown(eventName string, callback monitorCallBack, response map[string]interface{}) { 200 | event := &EventChildUpDown{} 201 | event.Ike = map[string]*EventIkeSAUpDown{} 202 | //we need to marshall all ikes manual because json uses connections names as key 203 | for name := range response { 204 | value := response[name] 205 | if name == "up" { 206 | event.Up = true 207 | } else { 208 | sa := &EventIkeSAUpDown{} 209 | ConvertFromGeneral(value, sa) 210 | event.Ike[name] = sa 211 | } 212 | } 213 | callback(eventName, event) 214 | } 215 | 216 | func handleChildRekey(eventName string, callback monitorCallBack, response map[string]interface{}) { 217 | event := &EventChildRekey{} 218 | event.Ike = map[string]*EventIkeRekeySA{} 219 | //we need to marshall all ikes manual because json uses connections names as key 220 | for name := range response { 221 | value := response[name] 222 | sa := &EventIkeRekeySA{} 223 | ConvertFromGeneral(value, sa) 224 | event.Ike[name] = sa 225 | } 226 | callback(eventName, event) 227 | } 228 | 229 | func (c *ClientConn) MonitorSA(callback monitorCallBack, watchdog time.Duration) (err error) { 230 | //register event 231 | c.RegisterEvent(EVENT_CHILD_UPDOWN, func(response map[string]interface{}) { 232 | //dumpResponse(response) 233 | handleChildUpDown(EVENT_CHILD_UPDOWN, callback, response) 234 | }) 235 | c.RegisterEvent(EVENT_CHILD_REKEY, func(response map[string]interface{}) { 236 | //dumpResponse(response) 237 | handleChildRekey(EVENT_CHILD_REKEY, callback, response) 238 | }) 239 | c.RegisterEvent(EVENT_IKE_UPDOWN, func(response map[string]interface{}) { 240 | //dumpResponse(response) 241 | handleIkeUpDown(EVENT_IKE_UPDOWN, callback, response) 242 | }) 243 | c.RegisterEvent(EVENT_IKE_REKEY, func(response map[string]interface{}) { 244 | //dumpResponse(response) 245 | handleIkeRekey(EVENT_IKE_REKEY, callback, response) 246 | }) 247 | 248 | for { 249 | time.Sleep(watchdog) 250 | //collect some daemon stats to see if connection is alive 251 | if _, err := c.Stats(); err != nil { 252 | return err 253 | } 254 | } 255 | return nil 256 | } 257 | -------------------------------------------------------------------------------- /msg.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "fmt" 8 | "io" 9 | "strconv" 10 | ) 11 | 12 | type segmentType byte 13 | 14 | const ( 15 | stCMD_REQUEST segmentType = 0 16 | stCMD_RESPONSE = 1 17 | stCMD_UNKNOWN = 2 18 | stEVENT_REGISTER = 3 19 | stEVENT_UNREGISTER = 4 20 | stEVENT_CONFIRM = 5 21 | stEVENT_UNKNOWN = 6 22 | stEVENT = 7 23 | ) 24 | 25 | func (t segmentType) hasName() bool { 26 | switch t { 27 | case stCMD_REQUEST, stEVENT_REGISTER, stEVENT_UNREGISTER, stEVENT: 28 | return true 29 | } 30 | return false 31 | } 32 | func (t segmentType) isValid() bool { 33 | switch t { 34 | case stCMD_REQUEST, stCMD_RESPONSE, stCMD_UNKNOWN, stEVENT_REGISTER, 35 | stEVENT_UNREGISTER, stEVENT_CONFIRM, stEVENT_UNKNOWN, stEVENT: 36 | return true 37 | } 38 | return false 39 | } 40 | 41 | func (t segmentType) hasMsg() bool { 42 | switch t { 43 | case stCMD_REQUEST, stCMD_RESPONSE, stEVENT: 44 | return true 45 | } 46 | return false 47 | } 48 | 49 | type elementType byte 50 | 51 | const ( 52 | etSECTION_START elementType = 1 53 | etSECTION_END = 2 54 | etKEY_VALUE = 3 55 | etLIST_START = 4 56 | etLIST_ITEM = 5 57 | etLIST_END = 6 58 | ) 59 | 60 | type segment struct { 61 | typ segmentType 62 | name string 63 | msg map[string]interface{} 64 | } 65 | 66 | //msg 在内部以下列3种类型表示(降低复杂度) 67 | // string 68 | // map[string]interface{} 69 | // []string 70 | func writeSegment(w io.Writer, msg segment) (err error) { 71 | if !msg.typ.isValid() { 72 | return fmt.Errorf("[writeSegment] msg.typ %d not defined", msg.typ) 73 | } 74 | buf := &bytes.Buffer{} 75 | buf.WriteByte(byte(msg.typ)) 76 | //name 77 | if msg.typ.hasName() { 78 | err = writeString1(buf, msg.name) 79 | if err != nil { 80 | fmt.Printf("error returned from writeString1i \n") 81 | return 82 | } 83 | } 84 | 85 | if msg.typ.hasMsg() { 86 | err = writeMap(buf, msg.msg) 87 | if err != nil { 88 | fmt.Printf("error retruned from writeMap \n") 89 | return 90 | } 91 | } 92 | 93 | //写长度 94 | err = binary.Write(w, binary.BigEndian, uint32(buf.Len())) 95 | if err != nil { 96 | fmt.Printf("[writeSegment] error writing to binary \n") 97 | return 98 | } 99 | 100 | _, err = buf.WriteTo(w) 101 | if err != nil { 102 | fmt.Printf("[writeSegment] error writing to buffer \n") 103 | return 104 | } 105 | 106 | return nil 107 | } 108 | 109 | func readSegment(inR io.Reader) (msg segment, err error) { 110 | //长度 111 | var length uint32 112 | err = binary.Read(inR, binary.BigEndian, &length) 113 | if err != nil { 114 | return 115 | } 116 | r := bufio.NewReader(&io.LimitedReader{ 117 | R: inR, 118 | N: int64(length), 119 | }) 120 | //类型 121 | c, err := r.ReadByte() 122 | if err != nil { 123 | return 124 | } 125 | msg.typ = segmentType(c) 126 | if !msg.typ.isValid() { 127 | return msg, fmt.Errorf("[readSegment] msg.typ %d not defined", msg.typ) 128 | } 129 | if msg.typ.hasName() { 130 | msg.name, err = readString1(r) 131 | if err != nil { 132 | return 133 | } 134 | } 135 | if msg.typ.hasMsg() { 136 | msg.msg, err = readMap(r, true) 137 | if err != nil { 138 | return 139 | } 140 | } 141 | return 142 | } 143 | 144 | //一个字节长度的字符串 145 | func writeString1(w *bytes.Buffer, s string) (err error) { 146 | length := len(s) 147 | if length > 255 { 148 | return fmt.Errorf("[writeString1] length>255") 149 | } 150 | w.WriteByte(byte(length)) 151 | w.WriteString(s) 152 | return 153 | } 154 | 155 | func readString1(r *bufio.Reader) (s string, err error) { 156 | length, err := r.ReadByte() 157 | if err != nil { 158 | return 159 | } 160 | buf := make([]byte, length) 161 | _, err = io.ReadFull(r, buf) 162 | if err != nil { 163 | return 164 | } 165 | return string(buf), nil 166 | } 167 | 168 | //两个字节长度的字符串 169 | func writeString2(w *bytes.Buffer, s string) (err error) { 170 | length := len(s) 171 | if length > 65535 { 172 | return fmt.Errorf("[writeString2] length>65535") 173 | } 174 | binary.Write(w, binary.BigEndian, uint16(length)) 175 | w.WriteString(s) 176 | return 177 | } 178 | 179 | func readString2(r io.Reader) (s string, err error) { 180 | var length uint16 181 | err = binary.Read(r, binary.BigEndian, &length) 182 | if err != nil { 183 | return 184 | } 185 | buf := make([]byte, length) 186 | _, err = io.ReadFull(r, buf) 187 | if err != nil { 188 | return 189 | } 190 | return string(buf), nil 191 | } 192 | 193 | func writeKeyMap(w *bytes.Buffer, name string, msg map[string]interface{}) (err error) { 194 | w.WriteByte(byte(etSECTION_START)) 195 | err = writeString1(w, name) 196 | if err != nil { 197 | return 198 | } 199 | writeMap(w, msg) 200 | w.WriteByte(byte(etSECTION_END)) 201 | return nil 202 | } 203 | 204 | func writeKeyList(w *bytes.Buffer, name string, msg []string) (err error) { 205 | w.WriteByte(byte(etLIST_START)) 206 | err = writeString1(w, name) 207 | if err != nil { 208 | return 209 | } 210 | for _, s := range msg { 211 | w.WriteByte(byte(etLIST_ITEM)) 212 | err = writeString2(w, s) 213 | if err != nil { 214 | return 215 | } 216 | } 217 | w.WriteByte(byte(etLIST_END)) 218 | return nil 219 | } 220 | 221 | func writeKeyString(w *bytes.Buffer, name string, msg string) (err error) { 222 | w.WriteByte(byte(etKEY_VALUE)) 223 | err = writeString1(w, name) 224 | if err != nil { 225 | return 226 | } 227 | err = writeString2(w, msg) 228 | return 229 | } 230 | 231 | func writeMap(w *bytes.Buffer, msg map[string]interface{}) (err error) { 232 | for k, v := range msg { 233 | switch t := v.(type) { 234 | case map[string]interface{}: 235 | writeKeyMap(w, k, t) 236 | case []string: 237 | writeKeyList(w, k, t) 238 | case string: 239 | writeKeyString(w, k, t) 240 | case []interface{}: 241 | str := make([]string, len(t)) 242 | for i := range t { 243 | str[i] = t[i].(string) 244 | } 245 | writeKeyList(w, k, str) 246 | default: 247 | return fmt.Errorf("[writeMap] can not write type %T right now", msg) 248 | } 249 | } 250 | return nil 251 | } 252 | 253 | //SECTION_START has been read already. 254 | func readKeyMap(r *bufio.Reader) (key string, msg map[string]interface{}, err error) { 255 | key, err = readString1(r) 256 | if err != nil { 257 | return 258 | } 259 | msg, err = readMap(r, false) 260 | return 261 | } 262 | 263 | //LIST_START has been read already. 264 | func readKeyList(r *bufio.Reader) (key string, msg []string, err error) { 265 | key, err = readString1(r) 266 | if err != nil { 267 | return 268 | } 269 | msg = []string{} 270 | for { 271 | var c byte 272 | c, err = r.ReadByte() 273 | if err != nil { 274 | return 275 | } 276 | switch elementType(c) { 277 | case etLIST_ITEM: 278 | value, err := readString2(r) 279 | if err != nil { 280 | return "", nil, err 281 | } 282 | msg = append(msg, value) 283 | case etLIST_END: //end of outer list 284 | return key, msg, nil 285 | default: 286 | return "", nil, fmt.Errorf("[readKeyList] protocol error 2") 287 | } 288 | } 289 | return 290 | } 291 | 292 | //KEY_VALUE has been read already. 293 | func readKeyString(r *bufio.Reader) (key string, msg string, err error) { 294 | key, err = readString1(r) 295 | if err != nil { 296 | return 297 | } 298 | msg, err = readString2(r) 299 | if err != nil { 300 | return 301 | } 302 | return 303 | } 304 | 305 | // Since the original key chosen can have duplicates, 306 | // this function is used to map the original key to a new one 307 | // to make them unique. 308 | func getNewKeyToHandleDuplicates(key string, msg map[string]interface{}) string { 309 | if _, ok := msg[key]; !ok { 310 | return key 311 | } 312 | 313 | for i := 0; ; i++ { 314 | newKey := key + "##" + strconv.Itoa(i) 315 | if _, ok := msg[newKey]; !ok { 316 | return newKey 317 | } 318 | } 319 | } 320 | 321 | //SECTION_START has been read already. 322 | func readMap(r *bufio.Reader, isRoot bool) (msg map[string]interface{}, err error) { 323 | msg = map[string]interface{}{} 324 | for { 325 | c, err := r.ReadByte() 326 | if err == io.EOF && isRoot { //may be root section 327 | return msg, nil 328 | } 329 | if err != nil { 330 | return nil, err 331 | } 332 | switch elementType(c) { 333 | case etSECTION_START: 334 | key, value, err := readKeyMap(r) 335 | if err != nil { 336 | return nil, err 337 | } 338 | msg[getNewKeyToHandleDuplicates(key, msg)] = value 339 | case etLIST_START: 340 | key, value, err := readKeyList(r) 341 | if err != nil { 342 | return nil, err 343 | } 344 | msg[getNewKeyToHandleDuplicates(key, msg)] = value 345 | case etKEY_VALUE: 346 | key, value, err := readKeyString(r) 347 | if err != nil { 348 | return nil, err 349 | } 350 | msg[getNewKeyToHandleDuplicates(key, msg)] = value 351 | case etSECTION_END: //end of outer section 352 | return msg, nil 353 | default: 354 | panic(fmt.Errorf("[readMap] protocol error 1, %d %#v", c, msg)) 355 | //return nil, fmt.Errorf("[readMap] protocol error 1, %d",c) 356 | } 357 | } 358 | return 359 | } 360 | -------------------------------------------------------------------------------- /msg_test.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func TestMsg(ot *testing.T) { 12 | for _, msg := range []map[string]interface{}{ 13 | map[string]interface{}{ 14 | "a": "1", 15 | }, 16 | map[string]interface{}{ 17 | "a": []string{ 18 | "1", "2", 19 | }, 20 | }, 21 | map[string]interface{}{ 22 | "a": map[string]interface{}{ 23 | "d": "e", 24 | "e": []string{ 25 | "1", "2", 26 | }, 27 | }, 28 | }, 29 | map[string]interface{}{ 30 | "a": []string{ 31 | "1", "2", 32 | }, 33 | "b": "a", 34 | "c": map[string]interface{}{ 35 | "d": "e", 36 | "e": []string{ 37 | "1", "2", 38 | }, 39 | }, 40 | }, 41 | map[string]interface{}{ 42 | "key1": "value1", 43 | "section1": map[string]interface{}{ 44 | "sub-section": map[string]interface{}{ 45 | "key2": "value2", 46 | }, 47 | "list1": []string{"item1", "item2"}, 48 | }, 49 | }, 50 | } { 51 | buf := &bytes.Buffer{} 52 | in := segment{ 53 | typ: stCMD_REQUEST, 54 | name: "good", 55 | msg: msg, 56 | } 57 | err := writeSegment(buf, in) 58 | mustNotError(err) 59 | content := buf.Bytes() 60 | out, err := readSegment(buf) 61 | mustNotError(err) 62 | //fmt.Println(content) 63 | if !reflect.DeepEqual(in, out) { 64 | in1, err := json.Marshal(in.msg) 65 | mustNotError(err) 66 | out1, err := json.Marshal(out.msg) 67 | mustNotError(err) 68 | fmt.Println(content) 69 | panic("!reflect.DeepEqual(in,out)\n" + string(in1) + "\n" + string(out1)) 70 | } 71 | } 72 | 73 | content := []byte{ 74 | 0x0, 0x0, 0x0, 0x5e, //length 94 75 | 0x1, //CMD_RESPONSE 76 | 0x3, //KEY_VALUE 77 | 0x6, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, //daemon 78 | 0x0, 0x6, 0x63, 0x68, 0x61, 0x72, 0x6f, 0x6e, //charon 79 | 0x3, 0x7, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x0, 0x5, 0x35, 0x2e, 0x32, 0x2e, 0x32, 80 | 0x3, 0x7, 0x73, 0x79, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x0, 0x5, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x3, 0x7, 0x72, 81 | 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x0, 0x11, 0x33, 0x2e, 0x31, 0x33, 0x2e, 0x30, 0x2d, 0x34, 0x34, 0x2d, 82 | 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x3, 0x7, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x0, 0x6, 0x78, 83 | 0x38, 0x36, 0x5f, 0x36, 0x34} 84 | buf := bytes.NewBuffer(content) 85 | out, err := readSegment(buf) 86 | mustNotError(err) 87 | 88 | in := segment{ 89 | typ: stCMD_RESPONSE, 90 | msg: map[string]interface{}{ 91 | "daemon": "charon", 92 | "machine": "x86_64", 93 | "release": "3.13.0-44-generic", 94 | "sysname": "Linux", 95 | "version": "5.2.2", 96 | }, 97 | } 98 | if !reflect.DeepEqual(in, out) { 99 | in1, err := json.Marshal(in.msg) 100 | mustNotError(err) 101 | out1, err := json.Marshal(out.msg) 102 | mustNotError(err) 103 | panic("!reflect.DeepEqual(in,out)\n" + string(in1) + "\n" + string(out1)) 104 | } 105 | } 106 | func mustNotError(err error) { 107 | if err != nil { 108 | panic(err) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /pools.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Pool struct { 8 | PoolMapping map[string]interface{} `json:"pools"` 9 | } 10 | 11 | type PoolMapping struct { 12 | Addrs string `json:"addrs"` 13 | DNS []string `json:"dns,omitempty"` 14 | NBNS []string `json:"nbns,omitempty"` 15 | ApplicationVersion []string `json:"7,omitempty"` 16 | InternalIPv6Prefix []string `json:"18,omitempty"` 17 | } 18 | 19 | func (c *ClientConn) LoadPool(ph Pool) error { 20 | requestMap := map[string]interface{}{} 21 | 22 | err := ConvertToGeneral(ph.PoolMapping, &requestMap) 23 | 24 | if err != nil { 25 | fmt.Println(err) 26 | return fmt.Errorf("error creating request: %v", err) 27 | } 28 | 29 | msg, err := c.Request("load-pool", requestMap) 30 | 31 | if msg["success"] != "yes" { 32 | return fmt.Errorf("unsuccessful LoadPool: %v", msg["errmsg"]) 33 | } 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /shared.go: -------------------------------------------------------------------------------- 1 | // this file contains the functions for managing shared secrets 2 | 3 | package goStrongswanVici 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | type Key struct { 10 | ID string `json:"id,omitempty"` 11 | Typ string `json:"type"` 12 | Data string `json:"data"` 13 | Owners []string `json:"owners"` 14 | } 15 | 16 | type UnloadKeyRequest struct { 17 | ID string `json:"id"` 18 | } 19 | 20 | type keyList struct { 21 | Keys []string `json:"keys"` 22 | } 23 | 24 | // load a shared secret into the IKE daemon 25 | func (c *ClientConn) LoadShared(key *Key) error { 26 | requestMap := &map[string]interface{}{} 27 | 28 | err := ConvertToGeneral(key, requestMap) 29 | 30 | if err != nil { 31 | return fmt.Errorf("error creating request: %v", err) 32 | } 33 | 34 | msg, err := c.Request("load-shared", *requestMap) 35 | if msg["success"] != "yes" { 36 | return fmt.Errorf("unsuccessful loadSharedKey: %v", msg["errmsg"]) 37 | } 38 | 39 | return nil 40 | } 41 | 42 | // unload (delete) a shared secret from the IKE daemon 43 | func (c *ClientConn) UnloadShared(key *UnloadKeyRequest) error { 44 | requestMap := &map[string]interface{}{} 45 | 46 | err := ConvertToGeneral(key, requestMap) 47 | 48 | if err != nil { 49 | return fmt.Errorf("error creating request: %v", err) 50 | } 51 | 52 | msg, err := c.Request("unload-shared", *requestMap) 53 | if msg["success"] != "yes" { 54 | return fmt.Errorf("unsuccessful loadSharedKey: %v", msg["errmsg"]) 55 | } 56 | 57 | return nil 58 | } 59 | 60 | // get a the names of the shared secrets currently loaded 61 | func (c *ClientConn) GetShared() ([]string, error) { 62 | msg, err := c.Request("get-shared", nil) 63 | if err != nil { 64 | fmt.Errorf("Error making request: %v", err) 65 | return nil, err 66 | } 67 | 68 | keys := &keyList{} 69 | 70 | err = ConvertFromGeneral(msg, keys) 71 | if err != nil { 72 | fmt.Errorf("Error converting data: %v", err) 73 | } 74 | 75 | return keys.Keys, err 76 | } 77 | -------------------------------------------------------------------------------- /stats.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | // Stats returns IKE daemon statistics and load information. 4 | func (c *ClientConn) Stats() (msg map[string]interface{}, err error) { 5 | msg, err = c.Request("stats", nil) 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /terminate.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type TerminateRequest struct { 8 | Child string `json:"child,omitempty"` 9 | Ike string `json:"ike,omitempty"` 10 | Child_id string `json:"child-id,omitempty"` 11 | Ike_id string `json:"ike-id,omitempty"` 12 | Force string `json:"force,omitempty"` 13 | Timeout string `json:"timeout,omitempty"` 14 | Loglevel string `json:"loglevel,omitempty"` 15 | } 16 | 17 | // To be simple, kill a client that is connecting to this server. A client is a sa. 18 | //Terminates an SA while streaming control-log events. 19 | func (c *ClientConn) Terminate(r *TerminateRequest) (err error) { 20 | err = handlePanic(func() (err error) { 21 | reqMap := &map[string]interface{}{} 22 | ConvertToGeneral(r, reqMap) 23 | msg, err := c.Request("terminate", *reqMap) 24 | if err != nil { 25 | return 26 | } 27 | if msg["success"] != "yes" { 28 | return fmt.Errorf("[Terminate] %s", msg["errmsg"]) 29 | } 30 | return 31 | }) 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /unloadConn.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type UnloadConnRequest struct { 8 | Name string `json:"name"` 9 | } 10 | 11 | func (c *ClientConn) UnloadConn(r *UnloadConnRequest) error { 12 | reqMap := &map[string]interface{}{} 13 | ConvertToGeneral(r, reqMap) 14 | msg, err := c.Request("unload-conn", *reqMap) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | if msg["success"] != "yes" { 20 | return fmt.Errorf("[Unload-Connection] %s", msg["errmsg"]) 21 | } 22 | 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package goStrongswanVici 2 | 3 | type Version struct { 4 | Daemon string `json:"daemon"` 5 | Version string `json:"version"` 6 | Sysname string `json:"sysname"` 7 | Release string `json:"release"` 8 | Machine string `json:"machine"` 9 | } 10 | 11 | func (c *ClientConn) Version() (out *Version, err error) { 12 | msg, err := c.Request("version", nil) 13 | if err != nil { 14 | return 15 | } 16 | out = &Version{} 17 | err = ConvertFromGeneral(msg, out) 18 | return 19 | } 20 | --------------------------------------------------------------------------------