• 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/forward.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
// ForwardLoader manages XDP-based forward proxy
19
type ForwardLoader struct {
20
        cfg   config.ProxyConfig
21
        objs  *wgebpf.WgForwardProxyObjects
22
        links []link.Link
23
}
24

25
// NewForwardLoader creates a new forward proxy loader
26
func NewForwardLoader() (*ForwardLoader, error) {
×
27
        return &ForwardLoader{}, nil
×
28
}
×
29

30
// LoadAndAttach loads the forward proxy program and attaches it to interfaces
NEW
31
func (fp *ForwardLoader) LoadAndAttach(ctx context.Context, cfg config.ProxyConfig) error {
×
32
        fp.cfg = cfg
×
33

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

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

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

47
        log.Info("Forward proxy loaded and attached",
×
48
                "enabled", cfg.Enabled,
×
NEW
49
                "method", cfg.Method,
×
NEW
50
                "target_server_ip", cfg.Forward.TargetServerIP)
×
51

×
52
        return nil
×
53
}
54

55
// configure configures the eBPF maps with the provided configuration
NEW
56
func (fp *ForwardLoader) configure(cfg config.ProxyConfig) error {
×
57
        if fp.objs == nil || fp.objs.ObfuscationConfigMap == nil {
×
58
                return errors.New("eBPF objects not loaded")
×
59
        }
×
60

61
        targetServerIP, err := cfg.GetTargetServerIP()
×
62
        if err != nil {
×
63
                return errors.Wrap(err, "failed to get target server IP")
×
64
        }
×
65

66
        keyBytes := cfg.GetKeyBytes()
×
67
        epbfConfig := wgebpf.WgForwardProxyObfuscationConfig{
×
68
                Enabled:        1,
×
69
                Method:         uint32(cfg.GetMethod()),
×
70
                KeyLen:         uint32(len(keyBytes)),
×
71
                TargetServerIp: targetServerIP,
×
72
        }
×
73

×
74
        if !cfg.Enabled {
×
75
                epbfConfig.Enabled = 0
×
76
        }
×
77

78
        if len(keyBytes) > len(epbfConfig.Key) {
×
79
                return errors.Errorf("key too long: %d bytes, max %d", len(keyBytes), len(epbfConfig.Key))
×
80
        }
×
81
        copy(epbfConfig.Key[:], keyBytes)
×
82

×
83
        configKey := uint32(0)
×
84
        if err := fp.objs.ObfuscationConfigMap.Put(&configKey, &epbfConfig); err != nil {
×
85
                return errors.Wrap(err, "failed to update forward epbfConfig map")
×
86
        }
×
87

88
        return nil
×
89
}
90

91
// loadEBPF loads the forward proxy eBPF program
92
func (fp *ForwardLoader) loadEBPF() error {
×
93
        fp.objs = &wgebpf.WgForwardProxyObjects{}
×
94

×
95
        opts := &ebpf.CollectionOptions{
×
96
                Programs: ebpf.ProgramOptions{
×
97
                        LogLevel:     2,
×
98
                        LogSizeStart: 16777216, // 16 MB log size
×
99
                },
×
100
        }
×
101

×
102
        if err := wgebpf.LoadWgForwardProxyObjects(fp.objs, opts); err != nil {
×
103
                return errors.Wrap(err, "failed to load forward proxy eBPF objects")
×
104
        }
×
105

106
        log.Info("Forward proxy eBPF program loaded")
×
107
        return nil
×
108
}
109

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

121
// attachToInterface attaches XDP program to a single interface
122
func (fp *ForwardLoader) attachToInterface(interfaceName string) error {
×
123
        iface, err := net.InterfaceByName(interfaceName)
×
124
        if err != nil {
×
125
                return errors.Wrapf(err, "failed to get interface %s", interfaceName)
×
126
        }
×
127

128
        var flags link.XDPAttachFlags
×
129
        var mode string
×
NEW
130
        switch fp.cfg.DriverMode {
×
131
        case "generic":
×
132
                flags = link.XDPGenericMode
×
133
                mode = "generic"
×
134
        case "driver":
×
135
                flags = link.XDPDriverMode
×
136
                mode = "driver"
×
137
        default:
×
138
                // Default to driver mode
×
139
                flags = link.XDPDriverMode
×
140
                mode = "driver"
×
141
        }
142

143
        log.Info("Attaching XDP forward proxy", "interface", interfaceName, "index", iface.Index, "mode", mode)
×
144

×
145
        xdpLink, err := link.AttachXDP(link.XDPOptions{
×
146
                Program:   fp.objs.WgForwardProxy,
×
147
                Interface: iface.Index,
×
148
                Flags:     flags,
×
149
        })
×
150
        if err != nil {
×
151
                return errors.Wrapf(err, "failed to attach XDP program to interface %s in %s mode", interfaceName, mode)
×
152
        }
×
153

154
        log.Info("XDP forward proxy attached successfully", "interface", interfaceName, "mode", mode)
×
155
        fp.links = append(fp.links, xdpLink)
×
156
        return nil
×
157
}
158

159
// cleanupLinks cleans up all attached links
160
func (fp *ForwardLoader) cleanupLinks() {
×
161
        for _, l := range fp.links {
×
162
                if l != nil {
×
163
                        err := l.Close()
×
164
                        if err != nil {
×
165
                                log.Error("Failed to close XDP link", "error", err)
×
166
                        } else {
×
167
                                log.Info("XDP link closed successfully")
×
168
                        }
×
169
                }
170
        }
171
        fp.links = nil
×
172
}
173

174
// Close cleans up all resources
175
func (fp *ForwardLoader) Close() error {
×
176
        var errs []error
×
177

×
178
        for i, l := range fp.links {
×
179
                if l != nil {
×
180
                        if err := l.Close(); err != nil {
×
181
                                errs = append(errs, errors.Wrapf(err, "failed to close XDP l %d", i))
×
182
                        }
×
183
                }
184
        }
185

186
        if fp.objs != nil {
×
187
                if err := fp.objs.Close(); err != nil {
×
188
                        errs = append(errs, errors.Wrap(err, "failed to close forward proxy eBPF objects"))
×
189
                }
×
190
        }
191

192
        if len(errs) > 0 {
×
193
                return errs[0]
×
194
        }
×
195

196
        return nil
×
197
}
198

199
// Maps returns all eBPF maps used by the forward proxy
200
func (fp *ForwardLoader) Maps() *maps.Maps {
×
201
        mapsCollection := maps.NewMaps()
×
202

×
203
        if fp.objs != nil {
×
NEW
204
                if fp.objs.MetricsMap != nil {
×
NEW
205
                        mapsCollection.SetMetricsMap(fp.objs.MetricsMap)
×
UNCOV
206
                }
×
207

208
                if fp.objs.ConnectionMap != nil {
×
209
                        mapsCollection.AddOtherMap("ConnectionMap", fp.objs.ConnectionMap)
×
210
                }
×
211

212
                if fp.objs.NatReverseMap != nil {
×
213
                        mapsCollection.AddOtherMap("NatReverseMap", fp.objs.NatReverseMap)
×
214
                }
×
215
        }
216

217
        return mapsCollection
×
218
}
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