-
Notifications
You must be signed in to change notification settings - Fork 5
fix: stop selecting direct connections with too-small MTU #85
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
Changes from 6 commits
bede4fb
1bab46f
ef3f81d
c89c6b7
4686156
6923084
b824a9c
4ec7ef5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
|
|
||
| import ( | ||
| "fmt" | ||
| "golang.org/x/crypto/nacl/box" | ||
| "net/netip" | ||
| "reflect" | ||
| "strings" | ||
|
|
@@ -25,31 +26,31 @@ | |
| m: &Ping{ | ||
| TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, | ||
| }, | ||
| want: "01 00 01 02 03 04 05 06 07 08 09 0a 0b 0c", | ||
| want: "01 01 01 02 03 04 05 06 07 08 09 0a 0b 0c", | ||
| }, | ||
| { | ||
| name: "ping_with_nodekey_src", | ||
| m: &Ping{ | ||
| TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, | ||
| NodeKey: key.NodePublicFromRaw32(mem.B([]byte{1: 1, 2: 2, 30: 30, 31: 31})), | ||
| }, | ||
| want: "01 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 00 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 1f", | ||
| want: "01 01 01 02 03 04 05 06 07 08 09 0a 0b 0c 00 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 1f", | ||
| }, | ||
| { | ||
| name: "pong", | ||
| m: &Pong{ | ||
| TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, | ||
| Src: mustIPPort("2.3.4.5:1234"), | ||
| }, | ||
| want: "02 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 00 00 00 00 00 00 00 00 00 00 ff ff 02 03 04 05 04 d2", | ||
| want: "02 01 01 02 03 04 05 06 07 08 09 0a 0b 0c 00 00 00 00 00 00 00 00 00 00 ff ff 02 03 04 05 04 d2", | ||
| }, | ||
| { | ||
| name: "pongv6", | ||
| m: &Pong{ | ||
| TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, | ||
| Src: mustIPPort("[fed0::12]:6666"), | ||
| }, | ||
| want: "02 00 01 02 03 04 05 06 07 08 09 0a 0b 0c fe d0 00 00 00 00 00 00 00 00 00 00 00 00 00 12 1a 0a", | ||
| want: "02 01 01 02 03 04 05 06 07 08 09 0a 0b 0c fe d0 00 00 00 00 00 00 00 00 00 00 00 00 00 12 1a 0a", | ||
| }, | ||
| { | ||
| name: "call_me_maybe", | ||
|
|
@@ -75,10 +76,15 @@ | |
| if !ok { | ||
| t.Fatalf("didn't start with foo: got %q", got) | ||
| } | ||
| // CODER: 1310 is max size of a Wireguard packet we will send. | ||
| expectedLen := 1310 - len(Magic) - keyLen - NonceLen - box.Overhead | ||
| if _, ok := tt.m.(*CallMeMaybe); !ok && len(got) != expectedLen { | ||
spikecurtis marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| t.Fatalf("Ping/Pong not padded: got len %d, want len %d", len(got), expectedLen) | ||
| } | ||
|
|
||
| gotHex := fmt.Sprintf("% x", got) | ||
| if gotHex != tt.want { | ||
| t.Fatalf("wrong marshal\n got: %s\nwant: %s\n", gotHex, tt.want) | ||
| if !strings.HasPrefix(gotHex, tt.want) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we expect the end of the ping payload to be a bunch of 00s we should probably test that, either by changing the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't care that it's zeros per se, but I'll add a check that it's the padded length. |
||
| t.Fatalf("wrong marshal\n got: %s\nwant prefix: %s\n", gotHex, tt.want) | ||
| } | ||
|
|
||
| back, err := Parse([]byte(got)) | ||
|
|
@@ -92,6 +98,69 @@ | |
| } | ||
| } | ||
|
|
||
| func TestParsePingPongV0(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| payload []byte | ||
| m Message | ||
| }{ | ||
| { | ||
| name: "ping", | ||
| m: &Ping{ | ||
| TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, | ||
| }, | ||
| payload: []byte{0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}, | ||
| }, | ||
| { | ||
| name: "ping_with_nodekey_src", | ||
| m: &Ping{ | ||
| TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, | ||
| NodeKey: key.NodePublicFromRaw32(mem.B([]byte{1: 1, 2: 2, 30: 30, 31: 31})), | ||
| }, | ||
| payload: []byte{ | ||
| 0x01, 0x00, | ||
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, | ||
| 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1f}, | ||
| }, | ||
| { | ||
| name: "pong", | ||
| m: &Pong{ | ||
| TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, | ||
| Src: mustIPPort("2.3.4.5:1234"), | ||
| }, | ||
| payload: []byte{ | ||
| 0x02, 0x00, | ||
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, | ||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x03, 0x04, 0x05, | ||
| 0x04, 0xd2}, | ||
| }, | ||
| { | ||
| name: "pongv6", | ||
| m: &Pong{ | ||
| TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, | ||
| Src: mustIPPort("[fed0::12]:6666"), | ||
| }, | ||
| payload: []byte{ | ||
| 0x02, 0x00, | ||
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, | ||
| 0xfe, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, | ||
| 0x1a, 0x0a}, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| back, err := Parse(tt.payload) | ||
| if err != nil { | ||
| t.Fatalf("parse back: %v", err) | ||
| } | ||
| if !reflect.DeepEqual(back, tt.m) { | ||
| t.Errorf("message in %+v doesn't match Parse result %+v", tt.m, back) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func mustIPPort(s string) netip.AddrPort { | ||
| ipp, err := netip.ParseAddrPort(s) | ||
| if err != nil { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package magicsock | ||
|
|
||
| import ( | ||
| "net" | ||
|
|
||
| "golang.org/x/sys/unix" | ||
| "tailscale.com/types/logger" | ||
| "tailscale.com/types/nettype" | ||
| ) | ||
|
|
||
| func tryPreventFragmentation(pconn nettype.PacketConn, logf logger.Logf, network string) { | ||
| if c, ok := pconn.(*net.UDPConn); ok { | ||
| s, err := c.SyscallConn() | ||
| if err != nil { | ||
| logf("magicsock: dontfrag: failed to get syscall conn: %v", err) | ||
| } | ||
| level := unix.IPPROTO_IP | ||
| option := unix.IP_DONTFRAG | ||
| if network == "udp6" { | ||
| level = unix.IPPROTO_IPV6 | ||
| option = unix.IPV6_DONTFRAG | ||
| } | ||
| err = s.Control(func(fd uintptr) { | ||
| err := unix.SetsockoptInt(int(fd), level, option, 1) | ||
| if err != nil { | ||
| logf("magicsock: dontfrag: SetsockoptInt failed: %v", err) | ||
| } | ||
| }) | ||
| if err != nil { | ||
| logf("magicsock: dontfrag: control connection failed: %v", err) | ||
| } | ||
| logf("magicsock: dontfrag: success on %s", pconn.LocalAddr().String()) | ||
| return | ||
| } | ||
| logf("magicsock: dontfrag: failed because it was not a UDPConn") | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package magicsock | ||
|
|
||
| import ( | ||
| "net" | ||
|
|
||
| "golang.org/x/sys/windows" | ||
| "tailscale.com/types/logger" | ||
| "tailscale.com/types/nettype" | ||
| ) | ||
|
|
||
| // https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/shared/ws2ipdef.h | ||
| const ( | ||
| IP_MTU_DISCOVER = 71 // IPV6_MTU_DISCOVER has the same value, which is nice. | ||
| IP_PMTUDISC_DO = 1 | ||
| ) | ||
|
|
||
| func tryPreventFragmentation(pconn nettype.PacketConn, logf logger.Logf, network string) { | ||
| if c, ok := pconn.(*net.UDPConn); ok { | ||
| s, err := c.SyscallConn() | ||
| if err != nil { | ||
| logf("magicsock: dontfrag: failed to get syscall conn: %v", err) | ||
| } | ||
| level := windows.IPPROTO_IP | ||
| if network == "udp6" { | ||
| level = windows.IPPROTO_IPV6 | ||
| } | ||
| err = s.Control(func(fd uintptr) { | ||
| err := windows.SetsockoptInt(windows.Handle(fd), level, IP_MTU_DISCOVER, IP_PMTUDISC_DO) | ||
| if err != nil { | ||
| logf("magicsock: dontfrag: SetsockoptInt failed: %v", err) | ||
| } | ||
| }) | ||
| if err != nil { | ||
| logf("magicsock: dontfrag: control connection failed: %v", err) | ||
| } | ||
| logf("magicsock: dontfrag: success on %s", pconn.LocalAddr().String()) | ||
| return | ||
| } | ||
| logf("magicsock: dontfrag: failed because it was not a UDPConn") | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.