• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stillya / wg-relay / 17307101384

28 Aug 2025 08:25PM UTC coverage: 12.934% (+8.1%) from 4.874%
17307101384

push

github

web-flow
Added metrics support (#3)

* Added metrics support

- added vnstat-like printing
- added prometheus integration

75 of 290 new or added lines in 11 files covered. (25.86%)

5 existing lines in 4 files now uncovered.

108 of 835 relevant lines covered (12.93%)

0.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pkg/dataplane/proxy/reverse.go
1
package proxy
2

3
import (
4
        "context"
5
        "net"
6

7
        log "log/slog"
8

9
        "github.com/cilium/ebpf"
10
        "github.com/cilium/ebpf/link"
11
        "github.com/pkg/errors"
12
        "github.com/stillya/wg-relay/pkg/dataplane/config"
13

14
        wgebpf "github.com/stillya/wg-relay/ebpf"
15
        "github.com/stillya/wg-relay/pkg/dataplane/maps"
16
)
17

18
// ReverseLoader manages TC-based reverse proxy
19
type ReverseLoader struct {
20
        cfg          config.ProxyConfig
21
        objs         *wgebpf.WgReverseProxyObjects
22
        ingressLinks []link.Link
23
        egressLinks  []link.Link
24
}
25

26
// NewReverseLoader creates a new reverse proxy loader
27
func NewReverseLoader() (*ReverseLoader, error) {
×
28
        return &ReverseLoader{}, nil
×
29
}
×
30

31
// LoadAndAttach loads the reverse proxy program and attaches it to interfaces
NEW
32
func (rp *ReverseLoader) LoadAndAttach(ctx context.Context, cfg config.ProxyConfig) error {
×
33
        rp.cfg = cfg
×
34

×
35
        if err := rp.loadEBPF(); err != nil {
×
36
                return errors.Wrap(err, "failed to load eBPF objects")
×
37
        }
×
38

39
        if err := rp.configure(cfg); err != nil {
×
40
                return errors.Wrap(err, "failed to configure eBPF maps")
×
41
        }
×
42

43
        if err := rp.attachToInterfaces(); err != nil {
×
44
                rp.Close()
×
45
                return errors.Wrap(err, "failed to attach to interfaces")
×
46
        }
×
47

NEW
48
        log.Info("Reverse proxy loaded and attached", "interfaces", cfg.Interfaces)
×
49
        return nil
×
50
}
51

52
// configure configures the eBPF maps with the provided configuration
NEW
53
func (rp *ReverseLoader) configure(cfg config.ProxyConfig) error {
×
54
        if rp.objs == nil || rp.objs.ObfuscationConfigMap == nil {
×
55
                return errors.New("eBPF objects not loaded")
×
56
        }
×
57

58
        keyBytes := cfg.GetKeyBytes()
×
59
        ebpfConfig := wgebpf.WgReverseProxyObfuscationConfig{
×
60
                Enabled: 1,
×
61
                Method:  uint32(cfg.GetMethod()),
×
62
                KeyLen:  uint32(len(keyBytes)),
×
63
        }
×
64

×
65
        if !cfg.Enabled {
×
66
                ebpfConfig.Enabled = 0
×
67
        }
×
68

69
        if len(keyBytes) > len(ebpfConfig.Key) {
×
70
                return errors.Errorf("key too long: %d bytes, max %d", len(keyBytes), len(ebpfConfig.Key))
×
71
        }
×
72
        copy(ebpfConfig.Key[:], keyBytes)
×
73

×
74
        configKey := uint32(0)
×
75
        if err := rp.objs.ObfuscationConfigMap.Put(&configKey, &ebpfConfig); err != nil {
×
76
                return errors.Wrap(err, "failed to update reverse ebpfConfig map")
×
77
        }
×
78

79
        log.Info("Reverse mode configuration updated",
×
80
                "enabled", cfg.Enabled,
×
NEW
81
                "method", cfg.Method,
×
82
                "key_len", len(keyBytes))
×
83

×
84
        return nil
×
85
}
86

87
// loadEBPF loads the reverse proxy eBPF program
88
func (rp *ReverseLoader) loadEBPF() error {
×
89
        rp.objs = &wgebpf.WgReverseProxyObjects{}
×
90

×
91
        // Enable verbose eBPF verifier logging
×
92
        opts := &ebpf.CollectionOptions{
×
93
                Programs: ebpf.ProgramOptions{
×
94
                        LogLevel:     2,
×
95
                        LogSizeStart: 16777216, // 16 MB log size
×
96
                },
×
97
        }
×
98

×
99
        if err := wgebpf.LoadWgReverseProxyObjects(rp.objs, opts); err != nil {
×
100
                return errors.Wrap(err, "failed to load reverse proxy eBPF objects")
×
101
        }
×
102

103
        log.Info("Reverse proxy eBPF program loaded")
×
104
        return nil
×
105
}
106

107
// attachToInterfaces attaches the TC program to configured interfaces
108
func (rp *ReverseLoader) attachToInterfaces() error {
×
NEW
109
        for _, interfaceName := range rp.cfg.Interfaces {
×
110
                if err := rp.attachToInterface(interfaceName); err != nil {
×
111
                        rp.cleanupLinks()
×
112
                        return errors.Wrapf(err, "failed to attach to interface %s", interfaceName)
×
113
                }
×
114
        }
115
        return nil
×
116
}
117

118
// attachToInterface attaches TC program to a single interface (both ingress and egress)
119
func (rp *ReverseLoader) attachToInterface(interfaceName string) error {
×
120
        iface, err := net.InterfaceByName(interfaceName)
×
121
        if err != nil {
×
122
                return errors.Wrapf(err, "failed to get interface %s", interfaceName)
×
123
        }
×
124

125
        log.Info("Attaching TC reverse proxy", "interface", interfaceName, "index", iface.Index)
×
126

×
127
        ingressLink, err := rp.attachTCProgram(iface, true, false)
×
128
        if err != nil {
×
129
                return errors.Wrapf(err, "failed to attach TC program on ingress for interface %s", interfaceName)
×
130
        }
×
131
        rp.ingressLinks = append(rp.ingressLinks, ingressLink)
×
132

×
133
        egressLink, err := rp.attachTCProgram(iface, false, true)
×
134
        if err != nil {
×
135
                return errors.Wrapf(err, "failed to attach TC program on egress for interface %s", interfaceName)
×
136
        }
×
137
        rp.egressLinks = append(rp.egressLinks, egressLink)
×
138

×
139
        return nil
×
140
}
141

142
// attachTCProgram attaches the TC program to the specified interface for either ingress or egress
143
func (rp *ReverseLoader) attachTCProgram(iface *net.Interface, ingress, egress bool) (link.Link, error) {
×
144
        if !ingress && !egress {
×
145
                return nil, errors.New("must specify either ingress or egress attachment")
×
146
        }
×
147

148
        var attach ebpf.AttachType
×
149
        var direction string
×
150

×
151
        if ingress {
×
152
                attach = ebpf.AttachTCXIngress
×
153
                direction = "ingress"
×
154
        } else {
×
155
                attach = ebpf.AttachTCXEgress
×
156
                direction = "egress"
×
157
        }
×
158

159
        log.Debug("Attempting TCX attachment", "interface", iface.Name, "direction", direction)
×
160
        tcxLink, err := link.AttachTCX(link.TCXOptions{
×
161
                Program:   rp.objs.WgReverseProxy,
×
162
                Attach:    attach,
×
163
                Interface: iface.Index,
×
164
        })
×
165

×
166
        if err != nil {
×
167
                log.Warn("TCX attachment failed", "interface", iface.Name, "direction", direction, "error", err)
×
168

×
169
                return nil, errors.Wrapf(err, "failed to attach TCX program on %s for interface %s", direction, iface.Name)
×
170
        }
×
171

172
        log.Info("Successfully attached program using TCX", "interface", iface.Name, "direction", direction)
×
173
        return tcxLink, nil
×
174
}
175

176
// cleanupLinks cleans up all attached links
177
func (rp *ReverseLoader) cleanupLinks() {
×
178
        for _, l := range rp.ingressLinks {
×
179
                if l != nil {
×
180
                        l.Close()
×
181
                }
×
182
        }
183
        for _, l := range rp.egressLinks {
×
184
                if l != nil {
×
185
                        l.Close()
×
186
                }
×
187
        }
188
        rp.ingressLinks = nil
×
189
        rp.egressLinks = nil
×
190
}
191

192
// Close cleans up all resources
193
func (rp *ReverseLoader) Close() error {
×
194
        var errs []error
×
195

×
196
        for i, l := range rp.ingressLinks {
×
197
                if l != nil {
×
198
                        if err := l.Close(); err != nil {
×
199
                                errs = append(errs, errors.Wrapf(err, "failed to close ingress TC l %d", i))
×
200
                        }
×
201
                }
202
        }
203

204
        for i, l := range rp.egressLinks {
×
205
                if l != nil {
×
206
                        if err := l.Close(); err != nil {
×
207
                                errs = append(errs, errors.Wrapf(err, "failed to close egress TC l %d", i))
×
208
                        }
×
209
                }
210
        }
211

212
        if rp.objs != nil {
×
213
                if err := rp.objs.Close(); err != nil {
×
214
                        errs = append(errs, errors.Wrap(err, "failed to close reverse proxy eBPF objects"))
×
215
                }
×
216
        }
217

218
        if len(errs) > 0 {
×
219
                return errs[0]
×
220
        }
×
221

222
        return nil
×
223
}
224

225
// Maps returns all eBPF maps used by the reverse proxy
226
func (rp *ReverseLoader) Maps() *maps.Maps {
×
227
        mapsCollection := maps.NewMaps()
×
228

×
229
        if rp.objs != nil {
×
NEW
230
                if rp.objs.MetricsMap != nil {
×
NEW
231
                        mapsCollection.SetMetricsMap(rp.objs.MetricsMap)
×
UNCOV
232
                }
×
233
        }
234

235
        return mapsCollection
×
236
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc