Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ This adds `PersistentKeepalive` to the profile's `[Peer]` section. The setting i
#### Maximum transmission unit (MTU)
To ensure maximum compatibility, the generated profile will have a MTU of 1280, just like the official Android app. If you are experiencing performance issues, you may be able to improve your speed by increasing this value. For more information, please check [#40](https://github.com/ViRb3/wgcf/issues/40).

You can override the default MTU or omit it from the generated profile:

```bash
wgcf generate --mtu 1420 # sets a custom MTU
wgcf generate --no-mtu # omits the MTU setting
```

### Add a license key

If you have an existing Warp+ subscription, for example on your phone, you can bind the account generated by this tool to your phone's account, sharing its Warp+ status. Please note that there is a limit of 5 maximum devices linked at a time. You can remove linked devices from the 1.1.1.1 app on your phone.
Expand Down
27 changes: 26 additions & 1 deletion cmd/generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ import (

var profileFile string
var keepalive int
var mtu int
var noMTU bool
var shortMsg = "Generates a WireGuard profile from the current Cloudflare Warp account"

const maxKeepalive = 65535
const (
maxKeepalive = 65535
defaultMTU = 1280
)

var Cmd = &cobra.Command{
Use: "generate",
Expand All @@ -31,17 +36,29 @@ func init() {
Cmd.PersistentFlags().StringVarP(&profileFile, "profile", "p", "wgcf-profile.conf", "WireGuard profile file")
Cmd.PersistentFlags().IntVarP(&keepalive, "keepalive", "k", 0, "Persistent keepalive interval in seconds (default 25)")
Cmd.PersistentFlags().Lookup("keepalive").NoOptDefVal = "25"
Cmd.PersistentFlags().IntVarP(&mtu, "mtu", "m", defaultMTU, "WireGuard interface MTU")
Cmd.PersistentFlags().BoolVar(&noMTU, "no-mtu", false, "Omit MTU from the generated profile")
Cmd.MarkFlagsMutuallyExclusive("mtu", "no-mtu")
}

func generateProfile() error {
if err := validateKeepalive(keepalive); err != nil {
return err
}

if err := validateMTU(mtu); err != nil {
return err
}

if err := EnsureConfigValidAccount(); err != nil {
return errors.WithStack(err)
}

profileMTU := mtu
if noMTU {
profileMTU = 0
}

ctx := CreateContext()
thisDevice, err := cloudflare.GetSourceDevice(ctx)
if err != nil {
Expand All @@ -64,6 +81,7 @@ func generateProfile() error {
PublicKey: peer.PublicKey,
Endpoint: endpoint,
Keepalive: keepalive,
MTU: profileMTU,
})
if err != nil {
return errors.WithStack(err)
Expand All @@ -82,3 +100,10 @@ func validateKeepalive(interval int) error {
}
return nil
}

func validateMTU(mtuValue int) error {
if mtuValue <= 0 {
return errors.Errorf("mtu value must be greater than 0, got %d", mtuValue)
}
return nil
}
89 changes: 89 additions & 0 deletions cmd/generate/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@ func TestValidateKeepalive(t *testing.T) {
}
}

func TestValidateMTU(t *testing.T) {
tests := []struct {
name string
mtu int
wantErr bool
}{
{name: "negative", mtu: -1, wantErr: true},
{name: "zero", mtu: 0, wantErr: true},
{name: "minimum accepted", mtu: 1},
{name: "default", mtu: 1280},
{name: "custom", mtu: 1500},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := validateMTU(test.mtu)
if (err != nil) != test.wantErr {
t.Fatalf("validateMTU(%d) error = %v, wantErr %v", test.mtu, err, test.wantErr)
}
})
}
}

func TestKeepaliveFlag(t *testing.T) {
flags := Cmd.PersistentFlags()
flag := flags.Lookup("keepalive")
Expand Down Expand Up @@ -57,3 +80,69 @@ func TestKeepaliveFlag(t *testing.T) {
t.Error()
}
}

func TestMTUFlags(t *testing.T) {
tests := []struct {
name string
args []string
wantMTU int
wantNoMTU bool
wantParseErr bool
wantGroupErr bool
}{
{name: "omitted", wantMTU: 1280},
{name: "custom", args: []string{"--mtu=1500"}, wantMTU: 1500},
{name: "short", args: []string{"-m", "1400"}, wantMTU: 1400},
{name: "no MTU", args: []string{"--no-mtu"}, wantMTU: 1280, wantNoMTU: true},
{name: "no MTU explicitly false", args: []string{"--no-mtu=false"}, wantMTU: 1280},
{name: "missing MTU value", args: []string{"--mtu"}, wantParseErr: true},
{name: "invalid MTU value", args: []string{"--mtu=invalid"}, wantParseErr: true},
{name: "conflicting flags", args: []string{"--mtu=1500", "--no-mtu"}, wantMTU: 1500, wantNoMTU: true, wantGroupErr: true},
{name: "conflicting flags reversed", args: []string{"--no-mtu", "--mtu=1500"}, wantMTU: 1500, wantNoMTU: true, wantGroupErr: true},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
resetFlag(t, "mtu")
resetFlag(t, "no-mtu")

err := Cmd.ParseFlags(test.args)
if (err != nil) != test.wantParseErr {
t.Fatalf("ParseFlags(%q) error = %v, wantParseErr %v", test.args, err, test.wantParseErr)
}
if test.wantParseErr {
return
}

err = Cmd.ValidateFlagGroups()
if (err != nil) != test.wantGroupErr {
t.Fatalf("ValidateFlagGroups() error = %v, wantGroupErr %v", err, test.wantGroupErr)
}
if mtu != test.wantMTU {
t.Errorf("mtu = %d, want %d", mtu, test.wantMTU)
}
if noMTU != test.wantNoMTU {
t.Errorf("noMTU = %t, want %t", noMTU, test.wantNoMTU)
}
})
}
}

func resetFlag(t *testing.T, name string) {
t.Helper()
flag := Cmd.PersistentFlags().Lookup(name)
if flag == nil {
t.Fatalf("flag %q not found", name)
}
value, changed := flag.Value.String(), flag.Changed
t.Cleanup(func() {
if err := flag.Value.Set(value); err != nil {
t.Errorf("restore flag %q: %v", name, err)
}
flag.Changed = changed
})
if err := flag.Value.Set(flag.DefValue); err != nil {
t.Fatalf("reset flag %q: %v", name, err)
}
flag.Changed = false
}
5 changes: 4 additions & 1 deletion wireguard/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ var profileTemplate = `[Interface]
PrivateKey = {{ .PrivateKey }}
Address = {{ .Address1 }}/32, {{ .Address2 }}/128
DNS = 1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001
MTU = 1280
{{- if .MTU }}
MTU = {{ .MTU }}
{{- end }}
[Peer]
PublicKey = {{ .PublicKey }}
AllowedIPs = 0.0.0.0/0, ::/0
Expand All @@ -33,6 +35,7 @@ type ProfileData struct {
PublicKey string
Endpoint string
Keepalive int
MTU int
}

func NewProfile(data *ProfileData) (*Profile, error) {
Expand Down
59 changes: 59 additions & 0 deletions wireguard/profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Endpoint = 5
Address2: "3",
PublicKey: "4",
Endpoint: "5",
MTU: 1280,
})
if err != nil {
t.Error(err)
Expand Down Expand Up @@ -53,6 +54,7 @@ PersistentKeepalive = 25
PublicKey: "4",
Endpoint: "5",
Keepalive: 25,
MTU: 1280,
})
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -81,3 +83,60 @@ func TestGenerateProfileWithCustomKeepalive(t *testing.T) {
t.Errorf("expected profile to end with %q, got:\n%s", expected, result)
}
}

func TestGenerateProfileWithoutMTU(t *testing.T) {
var expectedResult = `[Interface]
PrivateKey = 1
Address = 2/32, 3/128
DNS = 1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001
[Peer]
PublicKey = 4
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 5
`

result, err := generateProfile(&ProfileData{
PrivateKey: "1",
Address1: "2",
Address2: "3",
PublicKey: "4",
Endpoint: "5",
MTU: 0,
})
if err != nil {
t.Fatal(err)
}

if expectedResult != result {
t.Errorf("unexpected profile:\n%s", result)
}
}

func TestGenerateProfileWithCustomMTU(t *testing.T) {
var expectedResult = `[Interface]
PrivateKey = 1
Address = 2/32, 3/128
DNS = 1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001
MTU = 1500
[Peer]
PublicKey = 4
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 5
`

result, err := generateProfile(&ProfileData{
PrivateKey: "1",
Address1: "2",
Address2: "3",
PublicKey: "4",
Endpoint: "5",
MTU: 1500,
})
if err != nil {
t.Fatal(err)
}

if expectedResult != result {
t.Errorf("unexpected profile:\n%s", result)
}
}