Skip to content

Commit e43b784

Browse files
committed
wip: Adjust portForwarder
1 parent cdc22e9 commit e43b784

File tree

5 files changed

+78
-27
lines changed

5 files changed

+78
-27
lines changed

hack/test-port-forwarding.pl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,8 @@ sub JoinHostPort {
241241
# forward: 127.0.0.2 3020 → 127.0.0.1 2020
242242
# forward: 127.0.0.1 3021 → 127.0.0.1 2021
243243
# forward: 0.0.0.0 3022 → 127.0.0.1 2022
244-
# forward: :: 3023 → 127.0.0.1 2023
245-
# forward: ::1 3024 → 127.0.0.1 2024
244+
# forward: :: 3023 → ::1 2023
245+
# forward: ::1 3024 → ::1 2024
246246
247247
- guestPortRange: [3030, 3039]
248248
hostPortRange: [2030, 2039]
@@ -309,7 +309,7 @@ sub JoinHostPort {
309309
ignore: true
310310
311311
# forward: 0.0.0.0 4040 → 127.0.0.1 4040
312-
# forward: :: 4041 → 127.0.0.1 4041
312+
# forward: :: 4041 → ::1 4041
313313
# ignore: 127.0.0.1 4043 → 127.0.0.1 4043
314314
# ignore: 192.168.5.15 4044 → 127.0.0.1 4044
315315
@@ -318,4 +318,4 @@ sub JoinHostPort {
318318
# The actual test code is in test-example.sh in the "port-forwarding" block.
319319
- guestIPMustBeZero: true
320320
guestPort: 8888
321-
hostIP: 0.0.0.0
321+
hostIP: 0.0.0.0

pkg/hostagent/hostagent.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt
120120
AdditionalArgs: sshutil.SSHArgsFromOpts(sshOpts),
121121
}
122122

123-
rules := make([]limayaml.PortForward, 0, 3+len(y.PortForwards))
123+
rules := make([]limayaml.PortForward, 0, 4+len(y.PortForwards))
124124
// Block ports 22 and sshLocalPort on all IPs
125125
for _, port := range []int{sshGuestPort, sshLocalPort} {
126126
rule := limayaml.PortForward{GuestIP: net.IPv4zero, GuestPort: port, Ignore: true}

pkg/hostagent/port.go

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,38 +40,62 @@ func hostAddress(rule limayaml.PortForward, guest api.IPPort) string {
4040
return host.String()
4141
}
4242

43-
func tcpRuleIsWithinBounds(guest api.IPPort, rule limayaml.PortForward) bool {
44-
if rule.GuestSocket != "" {
45-
// Not TCP
46-
return false
47-
}
43+
func (pf *portForwarder) forwardingAddresses(guest api.IPPort) (hostAddr string, guestAddr string) {
44+
// Some rules will require a small patch to the HostIP in order to bind to the
45+
// correct IP family.
46+
mustAdjustHostIP := false
4847

49-
// Check if `guest.Port` is within `rule.GuestPortRange`
50-
return guest.Port >= rule.GuestPortRange[0] && guest.Port <= rule.GuestPortRange[1]
51-
}
48+
// This holds an optional rule that was rejected, but is now stored here to preserve backward
49+
// compatibility, and will be used at the bottom of this function if set. See the case
50+
// rule.GuestIPMustBeZero && guest.IP.IsUnspecified() for further info.
51+
var unspecifiedRuleFallback *limayaml.PortForward
5252

53-
func (pf *portForwarder) forwardingAddresses(guest api.IPPort) (hostAddr string, guestAddr string) {
54-
// Check if it matches new IPv6 rule, otherwise fallback to the pre-IPv6 checks to maintain
55-
// compatibility
5653
for _, rule := range pf.rules {
57-
if !tcpRuleIsWithinBounds(guest, rule) {
54+
if rule.GuestSocket != "" {
55+
// Not TCP
5856
continue
5957
}
6058

61-
// guest.IP and HostIP must be either :: or 0.0.0.0, and be forwarded to the same family on HostIP
62-
if guest.IP.IsUnspecified() && rule.HostIP.IsUnspecified() && ((guest.IP.To4() == nil) == (rule.HostIP.To4() == nil)) {
63-
return hostAddress(rule, guest), guest.String()
64-
}
65-
}
66-
67-
// Before giving up, try to match against the old rule set
68-
for _, rule := range pf.rules {
69-
if !tcpRuleIsWithinBounds(guest, rule) {
59+
// Check if `guest.Port` is within `rule.GuestPortRange`
60+
if guest.Port < rule.GuestPortRange[0] || guest.Port > rule.GuestPortRange[1] {
7061
continue
7162
}
7263

7364
switch {
74-
case guest.IP.IsUnspecified():
65+
// Early-continue in case rule's IP is not zero while it is required.
66+
case rule.GuestIPMustBeZero && !guest.IP.IsUnspecified():
67+
continue
68+
69+
// Rule lacks a preferred GuestIP, so guest may be binding to wherever it wants. The rule matches the port range,
70+
// so we can continue processing it. However, make sure to correct the rule to use a correct address family if
71+
// not specified by the rule.
72+
case rule.GuestIPWasUndefined && !rule.GuestIPMustBeZero:
73+
mustAdjustHostIP = rule.HostIPWasUndefined
74+
75+
// if GuestIP and family matches, move along.
76+
case rule.GuestIPMustBeZero && guest.IP.IsUnspecified():
77+
// This is a breaking change. Here we will keep a backup of the rule, so we can still reuse it
78+
// in case everything fails. The idea here is to move a copy of the current rule to outside this
79+
// loop, so we can reuse it in case nothing else matches.
80+
if !rule.GuestIPWasUndefined && !guest.IP.Equal(rule.GuestIP) {
81+
if unspecifiedRuleFallback == nil {
82+
// Move the rule to obtain a copy
83+
func(p limayaml.PortForward) { unspecifiedRuleFallback = &p }(rule)
84+
}
85+
continue
86+
}
87+
88+
mustAdjustHostIP = true
89+
90+
// Rule lack's HostIP, and guest is binding to '0.0.0.0' or '::'. Bind to the same address family.
91+
case rule.HostIPWasUndefined && guest.IP.IsUnspecified():
92+
mustAdjustHostIP = true
93+
94+
// We don't have a preferred HostIP in the rule, and guest wants to bind to a loopback
95+
// address. In that case, use the same address family.
96+
case rule.HostIPWasUndefined && (guest.IP.Equal(net.IPv6loopback) || guest.IP.Equal(api.IPv4loopback1)):
97+
mustAdjustHostIP = true
98+
7599
case guest.IP.Equal(rule.GuestIP):
76100
case guest.IP.Equal(net.IPv6loopback) && rule.GuestIP.Equal(api.IPv4loopback1):
77101
case rule.GuestIP.IsUnspecified() && !rule.GuestIPMustBeZero:
@@ -80,15 +104,32 @@ func (pf *portForwarder) forwardingAddresses(guest api.IPPort) (hostAddr string,
80104
default:
81105
continue
82106
}
107+
83108
if rule.Ignore {
84109
if guest.IP.IsUnspecified() && !rule.GuestIP.IsUnspecified() {
85110
continue
86111
}
112+
87113
break
88114
}
115+
116+
if mustAdjustHostIP {
117+
if guest.IP.To4() != nil {
118+
rule.HostIP = api.IPv4loopback1
119+
} else {
120+
rule.HostIP = net.IPv6loopback
121+
}
122+
}
123+
89124
return hostAddress(rule, guest), guest.String()
90125
}
91126

127+
// At this point, no other rule matched. So check if this is being impacted by our
128+
// breaking change, and return the fallback rule. Otherwise, just ignore it.
129+
if unspecifiedRuleFallback != nil {
130+
return hostAddress(*unspecifiedRuleFallback, guest), guest.String()
131+
}
132+
92133
return "", guest.String()
93134
}
94135

pkg/limayaml/defaults.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,10 +646,14 @@ func FillPortForwardDefaults(rule *PortForward, instDir string) {
646646
} else {
647647
rule.GuestIP = api.IPv4loopback1
648648
}
649+
rule.GuestIPWasUndefined = true
649650
}
651+
650652
if rule.HostIP == nil {
651653
rule.HostIP = api.IPv4loopback1
654+
rule.HostIPWasUndefined = true
652655
}
656+
653657
if rule.GuestPortRange[0] == 0 && rule.GuestPortRange[1] == 0 {
654658
if rule.GuestPort == 0 {
655659
rule.GuestPortRange[0] = 1

pkg/limayaml/limayaml.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ type PortForward struct {
189189
Proto Proto `yaml:"proto,omitempty" json:"proto,omitempty"`
190190
Reverse bool `yaml:"reverse,omitempty" json:"reverse,omitempty"`
191191
Ignore bool `yaml:"ignore,omitempty" json:"ignore,omitempty"`
192+
193+
// Set in case the HostIP field was automatically filled by FillPortForwardDefaults
194+
HostIPWasUndefined bool `yaml:"-" json:"-"`
195+
196+
// Set in case the GuestIP field was automatically filled by FillPortForwardDefaults
197+
GuestIPWasUndefined bool `yaml:"-" json:"-"`
192198
}
193199

194200
type CopyToHost struct {

0 commit comments

Comments
 (0)