Skip to content

Tunnel state is not returned correctly using govici api's #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
isha-369 opened this issue Apr 10, 2025 · 11 comments
Open

Tunnel state is not returned correctly using govici api's #49

isha-369 opened this issue Apr 10, 2025 · 11 comments
Assignees

Comments

@isha-369
Copy link

Hi Team
We are using version strongSwan swanctl 5.9.6

We are trying to fetch state of ipsec tunnel periodically in go routine after every 60 seconds through govici api using streamedCommandRequest of list-sas. But encountering one issue wherein state is returned empty randomly after few days..it is an intermittent issue
we tried running bash script along with go program and that bash script prints tunnel status in a file using swanctl --list-sas command. we observed that at same time tunnel status was actually ESTABLISHED but govici api returned empty at same time.

{
<IKE_SA config name> = {
uniqueid = <IKE_SA unique identifier>
version = <IKE version, 1 or 2>
state = <IKE_SA state name>
local-host =
local-port =
local-id =
remote-host =
remote-port =
remote-id =
remote-xauth-id = <remote XAuth identity, if XAuth-authenticated>
remote-eap-id = <remote EAP identity, if EAP-authenticated>
initiator = <yes, if initiator of IKE_SA>
initiator-spi = <hex encoded initiator SPI / cookie>
responder-spi = <hex encoded responder SPI / cookie>
nat-local = <yes, if local endpoint is behind a NAT>
nat-remote = <yes, if remote endpoint is behind a NAT>
nat-fake = <yes, if NAT situation has been faked as responder>
nat-any = <yes, if any endpoint is behind a NAT (also if faked)>
if-id-in =
if-id-out =
encr-alg =
encr-keysize = <key size for encr-alg, if applicable>
integ-alg =
integ-keysize = <key size for encr-alg, if applicable>
prf-alg =
dh-group =
established =
rekey-time =
reauth-time =
local-vips = [
<list of virtual IPs assigned by the remote peer, installed locally>
]
remote-vips = [

]
tasks-queued = [

]
tasks-active = [

]
tasks-passive = [

]
child-sas = {

  • = {
    name =
    uniqueid =
    reqid =
    state =
    mode = <IPsec mode, tunnel|transport|beet>
    protocol = <IPsec protocol AH|ESP>
    encap =
    spi-in =
    spi-out =
    cpi-in = <hex encoded inbound CPI, if using compression>
    cpi-out = <hex encoded outbound CPI, if using compression>
    mark-in =
    mark-mask-in =
    mark-out =
    mark-mask-out =
    if-id-in =
    if-id-out =
    label =
    encr-alg = <ESP encryption algorithm name, if any>
    encr-keysize = <ESP encryption key size, if applicable>
    integ-alg = <ESP or AH integrity algorithm name, if any>
    integ-keysize = <ESP or AH integrity key size, if applicable>
    prf-alg = <CHILD_SA pseudo random function name>
    dh-group = <CHILD_SA PFS rekeying DH group name, if any>
    esn = <1 if using extended sequence numbers>
    bytes-in =
    packets-in =
    use-in = <seconds since last inbound packet, if any>
    bytes-out =
    packets-out =
    use-out = <seconds since last outbound packet, if any>
    rekey-time =
    life-time =
    install-time =
    local-ts = [

]
remote-ts = [

]
}
}
}
}

To Reproduce
Bug can be reproduced by running go routine which periodically prints state of tunnel for few days..and after some time state is printed as empty

Expected behavior
as swanctl client returns correct behavior at the same time..wondering why govici is not able to fetch correct status

Can someone please help me. Any pointers are highly appreciated.
Thanks!

@enr0n
Copy link
Collaborator

enr0n commented Apr 10, 2025

Can you share the version of the Go module? Is it v0.7.0 or something older?

Can you also share a snippet of the relevant Go code?

{
<IKE_SA config name> = {
uniqueid = <IKE_SA unique identifier>
...

Is this output from something you ran? Or just a rough copy of https://github.com/strongswan/strongswan/blob/master/src/libcharon/plugins/vici/README.md#list-sa?

@isha-369
Copy link
Author

I am using go version 1.23.2

Yes list-sa was just copy of link you shared
Code snippet:

package main
import (
        "fmt"
        "github.com/strongswan/govici/vici"
        "log"
        "time"

)
type SA struct {
        NoBlock bool   `vici:"noblock"`
        Ike     string `vici:"ike"`
        Ikeid   int    `vici:"ike-id"`
        Child   string `vici:"child"`
        Childid int    `vici:"child-id"`
}

func main(){
session, err := vici.NewSession(vici.WithSocketPath("/var/run/charon.vici"))
if err != nil {
                fmt.Println("session not created......")
        }
         for {
                sa := SA{
                NoBlock: false,
                Ike:     "home1",
                Ikeid:   0,
                Child:   "",
                Childid: 0,
        }
        msg, err := vici.MarshalMessage(&sa)
        if err != nil {
                fmt.Println("unable to unmarshal")
                continue
        }

        ans, err := session.StreamedCommandRequest("list-sas", "list-sa", msg)
        if err != nil {
         fmt.Println("unable to send request")
         continue

        }
        data := Parse(ans)
        if data != "ESTABLISHED" && data != "CONNECTING" {
                        fmt.Println("######ISSUE#####", data)
                }

                 time.Sleep(60 * time.Second)
        }

}
func Parse(data []*vici.Message) string {
        var status string
        for _, msg := range data {
                for _, key := range msg.Keys() {
                        data := msg.Get(key)
                        if m, ok := data.(*vici.Message); ok {
                                status = fmt.Sprintf("%s", m.Get("state"))
                                if status == "" {
                                        log.Fatalf("status is empty. Aborting...........")
                                }
                                timestamp := time.Now().Format("2006-01-02 15:04:05")
                                fmt.Printf("[%s] TUNNEL STATE: %s\n", timestamp, status)
                        }
                }
        }
        return status
}


@isha-369
Copy link
Author

yes i am using vici version 0.7.0

@enr0n
Copy link
Collaborator

enr0n commented Apr 11, 2025

Okay, thanks. You have a lot of debugging print statements in there. Do you have output from that program which demonstrates the issue you are describing? It would help me trace your sample code a bit.

@isha-369
Copy link
Author

yes
fmt.Printf("[%s] TUNNEL STATE: %s\n", timestamp, status)
here the status is printed as empty
although at same time tunnel was in ESTABLISHED state

@enr0n
Copy link
Collaborator

enr0n commented Apr 11, 2025

Oh, you're always doing the request with ike-id=0 and child-id=0. This will change e.g. when a client re-connects, rekeys, etc. Is that what you intend to be doing?

You probably don't want that, in which case I would recommend removing Ikeid and Childid from the SA struct.

Can you see if that fixes the issue?

@enr0n
Copy link
Collaborator

enr0n commented Apr 11, 2025

Maybe the same for child too. In general, I would recommend leaving fields unset unless you have a non-empty value for them, or you know you want to explicitly give the empty value.

There are many fields where if the charon daemon sees unset, it will apply a reasonable default. But explicitly setting to the empty value can clobber that.

@tobiasbrunner
Copy link
Member

Thanks Nick for looking into this.

Oh, you're always doing the request with ike-id=0 and child-id=0. This will change e.g. when a client re-connects, rekeys, etc. Is that what you intend to be doing?

Note that the unique IDs of IKE or Child SAs will never be 0. And 0 is also the value that's assumed as default if either key is omitted in the request and then causes no filtering.

Maybe the same for child too.

That could be more of an issue as the empty string is obviously not NULL, which the C code assumes as default for these string arguments if they are omitted. However, it only affects the child-sas sub-section (which would always be empty), not the top-level list of IKE SAs.

Neither should affect whether state is empty or not. If an IKE SA is returned, the state key-value pair is filled in and it can't be empty.

@isha-369
Copy link
Author

Thanks for the explanation

That could be more of an issue as the empty string is obviously not NULL, which the C code assumes as default for these string arguments if they are omitted. However, it only affects the child-sas sub-section (which would always be empty), not the top-level list of IKE SAs.

@tobiasbrunner when above thing happens then in that case it will indeed return state parameter in child-sas as empty right?..and as i am passing child as empty does that explains that it might return state as empty in some cases...?( can i say that?)

@tobiasbrunner
Copy link
Member

when above thing happens then in that case it will indeed return state parameter in child-sas as empty right?

No, the complete child-sas section will just be empty. But that is always the case if you pass an empty string in child, not just in some cases. And as I said, it won't affect the information about the IKE SA, including its state.

@isha-369
Copy link
Author

Hi
i recently observed when through govici call of list-sas it returned empty information about IKE SA.. but at same point of time swanctl -l returned ESTABLISHED status

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants