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

stillya / wg-relay / 21185683316

20 Jan 2026 08:13PM UTC coverage: 28.463% (-1.4%) from 29.832%
21185683316

push

github

web-flow
feat: add ebpf config declaration (#10)

5 of 122 new or added lines in 7 files covered. (4.1%)

11 existing lines in 4 files now uncovered.

263 of 924 relevant lines covered (28.46%)

0.3 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 eBPF program and attaches it to the configured interfaces.
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.attachToInterfaces(); err != nil {
×
39
                fp.Close()
×
40
                return errors.Wrap(err, "failed to attach to interfaces")
×
41
        }
×
42

43
        log.Info("Forward proxy loaded and attached",
×
44
                "enabled", cfg.Enabled,
×
45
                "target_server_ip", cfg.Forward.TargetServerIP)
×
46

×
47
        return nil
×
48
}
49

NEW
50
func (fp *ForwardLoader) configureStaticVars(spec *ebpf.CollectionSpec) error {
×
NEW
51
        xorEnabled := fp.cfg.Instrumentations.XOR != nil && fp.cfg.Instrumentations.XOR.Enabled
×
NEW
52
        if err := spec.Variables["__cfg_xor_enabled"].Set(xorEnabled); err != nil {
×
NEW
53
                return errors.Wrap(err, "failed to set xor_enabled")
×
UNCOV
54
        }
×
55

NEW
56
        if xorEnabled {
×
NEW
57
                keyBytes := fp.cfg.GetXORKey()
×
NEW
58
                if keyBytes != nil {
×
NEW
59
                        var keyArray [32]byte
×
NEW
60
                        copy(keyArray[:], keyBytes)
×
61

×
NEW
62
                        if err := spec.Variables["__cfg_xor_key"].Set(keyArray); err != nil {
×
NEW
63
                                return errors.Wrap(err, "failed to set xor_key")
×
NEW
64
                        }
×
NEW
65
                        keyLen := len(keyBytes)
×
NEW
66
                        if keyLen > 255 {
×
NEW
67
                                keyLen = 255
×
NEW
68
                        }
×
NEW
69
                        if err := spec.Variables["__cfg_xor_key_len"].Set(uint8(keyLen)); err != nil { //nolint:gosec // key length is bounded
×
NEW
70
                                return errors.Wrap(err, "failed to set xor_key_len")
×
NEW
71
                        }
×
72
                }
NEW
73
        } else {
×
NEW
74
                if err := spec.Variables["__cfg_xor_key_len"].Set(uint8(0)); err != nil {
×
NEW
75
                        return errors.Wrap(err, "failed to set xor_key_len to 0")
×
NEW
76
                }
×
77
        }
78

NEW
79
        if err := spec.Variables["__cfg_wg_port"].Set(fp.cfg.WGPort); err != nil {
×
NEW
80
                return errors.Wrap(err, "failed to set wg_port")
×
UNCOV
81
        }
×
82

83
        return nil
×
84
}
85

UNCOV
86
func (fp *ForwardLoader) loadEBPF() error {
×
NEW
87
        spec, err := wgebpf.LoadWgForwardProxy()
×
NEW
88
        if err != nil {
×
NEW
89
                return errors.Wrap(err, "failed to load forward proxy spec")
×
NEW
90
        }
×
91

NEW
92
        if err := fp.configureStaticVars(spec); err != nil {
×
NEW
93
                return errors.Wrap(err, "failed to configure static variables")
×
NEW
94
        }
×
95

NEW
96
        fp.objs = &wgebpf.WgForwardProxyObjects{}
×
97
        opts := &ebpf.CollectionOptions{
×
98
                Programs: ebpf.ProgramOptions{
×
99
                        LogLevel:     2,
×
NEW
100
                        LogSizeStart: 16777216,
×
101
                },
×
102
        }
×
103

×
NEW
104
        if err := spec.LoadAndAssign(fp.objs, opts); err != nil {
×
105
                return errors.Wrap(err, "failed to load forward proxy eBPF objects")
×
106
        }
×
107

NEW
108
        if err := fp.configureBackendMap(); err != nil {
×
NEW
109
                return errors.Wrap(err, "failed to configure backend map")
×
NEW
110
        }
×
111

112
        log.Info("Forward proxy eBPF program loaded")
×
113
        return nil
×
114
}
115

NEW
116
func (fp *ForwardLoader) configureBackendMap() error {
×
NEW
117
        targetServerIP, err := fp.cfg.GetTargetServerIP()
×
NEW
118
        if err != nil {
×
NEW
119
                return errors.Wrap(err, "failed to get target server IP")
×
NEW
120
        }
×
121

NEW
122
        key := uint32(0)
×
NEW
123
        _ = key
×
NEW
124
        _ = targetServerIP
×
NEW
125

×
NEW
126
        log.Info("Backend map configured", "target_server_ip", fp.cfg.Forward.TargetServerIP)
×
NEW
127
        return nil
×
128
}
129

130
// attachToInterfaces attaches the XDP program to configured interfaces
131
func (fp *ForwardLoader) attachToInterfaces() error {
×
132
        for _, interfaceName := range fp.cfg.Interfaces {
×
133
                if err := fp.attachToInterface(interfaceName); err != nil {
×
134
                        fp.cleanupLinks()
×
135
                        return errors.Wrapf(err, "failed to attach to interface %s", interfaceName)
×
136
                }
×
137
        }
138
        return nil
×
139
}
140

141
// attachToInterface attaches XDP program to a single interface
142
func (fp *ForwardLoader) attachToInterface(interfaceName string) error {
×
143
        iface, err := net.InterfaceByName(interfaceName)
×
144
        if err != nil {
×
145
                return errors.Wrapf(err, "failed to get interface %s", interfaceName)
×
146
        }
×
147

148
        var flags link.XDPAttachFlags
×
149
        var mode string
×
150
        switch fp.cfg.DriverMode {
×
151
        case "generic":
×
152
                flags = link.XDPGenericMode
×
153
                mode = "generic"
×
154
        case "driver":
×
155
                flags = link.XDPDriverMode
×
156
                mode = "driver"
×
157
        case "offload":
×
158
                flags = link.XDPOffloadMode
×
159
                mode = "offload"
×
160
        default:
×
161
                // Default to driver mode
×
162
                flags = link.XDPDriverMode
×
163
                mode = "driver"
×
164
        }
165

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

×
168
        xdpLink, err := link.AttachXDP(link.XDPOptions{
×
169
                Program:   fp.objs.WgForwardProxy,
×
170
                Interface: iface.Index,
×
171
                Flags:     flags,
×
172
        })
×
173
        if err != nil {
×
174
                return errors.Wrapf(err, "failed to attach XDP program to interface %s in %s mode", interfaceName, mode)
×
175
        }
×
176

177
        log.Info("XDP forward proxy attached successfully", "interface", interfaceName, "mode", mode)
×
178
        fp.links = append(fp.links, xdpLink)
×
179
        return nil
×
180
}
181

182
// cleanupLinks cleans up all attached links
183
func (fp *ForwardLoader) cleanupLinks() {
×
184
        for _, l := range fp.links {
×
185
                if l != nil {
×
186
                        err := l.Close()
×
187
                        if err != nil {
×
188
                                log.Error("Failed to close XDP link", "error", err)
×
189
                        } else {
×
190
                                log.Info("XDP link closed successfully")
×
191
                        }
×
192
                }
193
        }
194
        fp.links = nil
×
195
}
196

197
// Close cleans up all resources
198
func (fp *ForwardLoader) Close() error {
×
199
        var errs []error
×
200

×
201
        for i, l := range fp.links {
×
202
                if l != nil {
×
203
                        if err := l.Close(); err != nil {
×
204
                                errs = append(errs, errors.Wrapf(err, "failed to close XDP l %d", i))
×
205
                        }
×
206
                }
207
        }
208

209
        if fp.objs != nil {
×
210
                if err := fp.objs.Close(); err != nil {
×
211
                        errs = append(errs, errors.Wrap(err, "failed to close forward proxy eBPF objects"))
×
212
                }
×
213
        }
214

215
        if len(errs) > 0 {
×
216
                return errs[0]
×
217
        }
×
218

219
        return nil
×
220
}
221

222
// Maps returns all eBPF maps used by the forward proxy
223
func (fp *ForwardLoader) Maps() *maps.Maps {
×
224
        var metricsMap *ebpf.Map
×
225
        if fp.objs != nil {
×
226
                metricsMap = fp.objs.MetricsMap
×
227
        }
×
228

229
        mapsCollection := maps.NewMaps(metricsMap)
×
230

×
231
        if fp.objs != nil {
×
232
                if fp.objs.ConnectionMap != nil {
×
233
                        mapsCollection.AddOtherMap("ConnectionMap", fp.objs.ConnectionMap)
×
234
                }
×
235

236
                if fp.objs.NatReverseMap != nil {
×
237
                        mapsCollection.AddOtherMap("NatReverseMap", fp.objs.NatReverseMap)
×
238
                }
×
239
        }
240

241
        return mapsCollection
×
242
}
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