Skip to content

Commit deefeca

Browse files
authored
Merge pull request #55 from eko/rework-network
Rework network to allow IP generation range from 127.1.2.1 to 127.255.255.255
2 parents dbc3a85 + 887bdeb commit deefeca

File tree

3 files changed

+158
-56
lines changed

3 files changed

+158
-56
lines changed

pkg/proxy/network.go

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,17 @@ func init() {
2424
func getNetworkInterface() (string, error) {
2525
ifaces, err := net.Interfaces()
2626
if err != nil {
27-
fmt.Println("Cannot retrieve interfaces list: ", err)
28-
return "", err
27+
return "", fmt.Errorf("cannot retrieve interfaces list: %v", err)
2928
}
3029

31-
for _, i := range ifaces {
32-
ifaceFlags := i.Flags.String()
30+
for _, iface := range ifaces {
31+
ifaceFlags := iface.Flags.String()
3332
if strings.Contains(ifaceFlags, "loopback") {
34-
return i.Name, nil
33+
return iface.Name, nil
3534
}
3635
}
3736

38-
return "", errors.New("Unable to find loopback network interface")
37+
return "", errors.New("unable to find 'loopback' network interface")
3938
}
4039

4140
// getAddIPCommandWithArgs returns the command (ifconfig, ip, ...) that will be used for the current OS
@@ -67,52 +66,64 @@ func getAddIPCommandWithArgs(ip string) (string, []string) {
6766
return command, args
6867
}
6968

70-
func generateIP(a byte, b byte, c byte, d int, port string) (net.IP, error) {
71-
ip := net.IPv4(a, b, c, byte(d))
72-
69+
func assignIpToPort(a, b, c, d byte, port string) (byte, byte, byte, byte, error) {
7370
// Retrieve network interface
7471
iface, err := net.InterfaceByName(networkInterface)
7572
if err != nil {
76-
return net.IP{}, err
73+
return a, b, c, d, err
7774
}
7875

79-
command, args := getAddIPCommandWithArgs(ip.String())
76+
for {
77+
// Maximum IP bytes reached
78+
if b == 255 && c == 255 && d == 255 {
79+
break
80+
}
8081

81-
for i := d; i < 255; i++ {
82-
ip = net.IPv4(a, b, c, byte(i))
82+
ip := net.IPv4(a, b, c, d)
8383

8484
addrs, err := iface.Addrs()
8585
if err != nil {
86-
return net.IP{}, err
86+
return a, b, c, d, err
8787
}
8888

89-
// Try to assign port to the IP addresses already assigned to the interface
90-
for _, addr := range addrs {
91-
if addr.String() == ip.String()+"/8" {
92-
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", ip.String(), port))
93-
if err != nil {
94-
return net.IPv4(a, b, c, byte(i)), nil
95-
}
96-
if conn != nil {
97-
conn.Close()
98-
}
89+
// In case IP is already assigned to network interface, don't try to create it again
90+
if !isAlreadyAssigned(ip, addrs) {
91+
command, args := getAddIPCommandWithArgs(ip.String())
92+
93+
if err := exec.Command(command, args...).Run(); err != nil {
94+
return a, b, c, d, fmt.Errorf("error while trying to run ifconfig/ip command to add new IP address (%s) on network interface '%s': %v", ip.String(), networkInterface, err)
9995
}
10096
}
10197

102-
// No already assigned IP/Port available, add IP address to the network interface
103-
if err := exec.Command(command, args...).Run(); err != nil {
104-
return net.IP{}, fmt.Errorf("Cannot run ifconfig command to add new IP address (%s) on network interface '%s': %v", ip.String(), networkInterface, err)
98+
// Can't be contacted on ip/port? it means this couple is free to be used
99+
if !canDial(ip.String(), port) {
100+
return a, b, c, d, nil
105101
}
106102

107-
// Try to assign port to the newly assigned IP
108-
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", ip.String(), port))
109-
if err != nil {
110-
return net.IPv4(a, b, c, byte(i)), nil
111-
}
112-
if conn != nil {
113-
conn.Close()
103+
a, b, c, d = getNextIPAddress(a, b, c, d)
104+
}
105+
106+
return a, b, c, d, fmt.Errorf("unable to find an available IP/Port (ip: %d.%d.%d.%d:%s)", a, b, c, d, port)
107+
}
108+
109+
func isAlreadyAssigned(ip net.IP, addrs []net.Addr) bool {
110+
for _, addr := range addrs {
111+
if addr.String() == ip.String()+"/8" {
112+
return true
114113
}
115114
}
116115

117-
return net.IP{}, fmt.Errorf("Unable to find an available IP/Port (ip: %d.%d.%d.%d:%s)", a, b, c, d, port)
116+
return false
117+
}
118+
119+
func canDial(ip, port string) bool {
120+
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", ip, port))
121+
if conn != nil {
122+
conn.Close()
123+
}
124+
if err != nil {
125+
return false
126+
}
127+
128+
return true
118129
}

pkg/proxy/proxy.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ type proxy struct {
3232
addProxyForwardMux sync.Mutex
3333
listenerMux sync.Mutex
3434
latestPort string
35-
attributedIPs map[string]net.IP
36-
ipLastByte int
35+
lastIpByteA byte
36+
lastIpByteB byte
37+
lastIpByteC byte
38+
lastIpByteD byte
3739
view ui.View
3840
}
3941

@@ -45,8 +47,10 @@ func NewProxy(view ui.View, hostfile hostfile.Hostfile) *proxy {
4547
listeners: make(map[string]net.Listener),
4648
listening: true,
4749
latestPort: ProxyPortStart,
48-
attributedIPs: make(map[string]net.IP, 0),
49-
ipLastByte: 1,
50+
lastIpByteA: 127,
51+
lastIpByteB: 0,
52+
lastIpByteC: 1,
53+
lastIpByteD: 0,
5054
view: view,
5155
}
5256
}
@@ -160,27 +164,43 @@ func (p *proxy) AddProxyForward(name string, proxyForward *ProxyForward) {
160164
}
161165

162166
func (p *proxy) generateIP(pf *ProxyForward) error {
163-
// Create new listener on a dedicated IP address if the service
164-
// does not already have an IP address attributed, elsewhere give the already
165-
// attributed address
166-
var localIP net.IP
167167
var err error
168-
if v, ok := p.attributedIPs[pf.Name]; ok {
169-
localIP = v
170-
} else {
171-
localIP, err = generateIP(127, 1, 2, p.ipLastByte, pf.LocalPort)
172-
p.ipLastByte = p.ipLastByte + 1
173-
if err != nil {
174-
return err
175-
}
168+
169+
a, b, c, d := getNextIPAddress(p.lastIpByteA, p.lastIpByteB, p.lastIpByteC, p.lastIpByteD)
170+
171+
p.lastIpByteA = a
172+
p.lastIpByteB = b
173+
p.lastIpByteC = c
174+
p.lastIpByteD = d
175+
176+
a, b, c, d, err = assignIpToPort(a, b, c, d, pf.LocalPort)
177+
if err != nil {
178+
return err
176179
}
177180

178-
p.attributedIPs[pf.Name] = localIP
179-
pf.SetLocalIP(localIP.String())
181+
ip := net.IPv4(a, b, c, d)
182+
pf.SetLocalIP(ip.String())
180183

181184
return nil
182185
}
183186

187+
func getNextIPAddress(a, b, c, d byte) (byte, byte, byte, byte) {
188+
if b == 255 && c == 255 && d == 255 {
189+
return a, b, c, d
190+
} else if c == 255 && d == 255 {
191+
b++
192+
c = 1
193+
d = 1
194+
} else if d == 255 {
195+
c++
196+
d = 1
197+
} else {
198+
d++
199+
}
200+
201+
return a, b, c, d
202+
}
203+
184204
func (p *proxy) generateProxyPort(proxyForward *ProxyForward) {
185205
integerPort, _ := strconv.Atoi(p.latestPort)
186206
p.latestPort = strconv.Itoa(integerPort + 1)

pkg/proxy/proxy_test.go

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package proxy
44

55
import (
6+
"fmt"
67
"testing"
78

89
"github.com/eko/monday/pkg/hostfile"
@@ -29,7 +30,7 @@ func TestNewProxy(t *testing.T) {
2930

3031
assert.Len(t, p.ProxyForwards, 0)
3132
assert.Equal(t, p.latestPort, "9400")
32-
assert.Equal(t, p.ipLastByte, 1)
33+
assert.Equal(t, p.ipLastByte, byte(1))
3334
}
3435

3536
func TestAddProxyForward(t *testing.T) {
@@ -53,7 +54,7 @@ func TestAddProxyForward(t *testing.T) {
5354
// Then
5455
assert.Len(t, proxy.ProxyForwards, 1)
5556
assert.Equal(t, proxy.latestPort, "9401")
56-
assert.Equal(t, proxy.ipLastByte, 2)
57+
assert.Equal(t, proxy.ipLastByte, byte(2))
5758
}
5859

5960
func TestAddProxyForwardWhenMultiple(t *testing.T) {
@@ -120,5 +121,75 @@ func TestListen(t *testing.T) {
120121

121122
assert.Len(t, proxy.ProxyForwards, 1)
122123
assert.Equal(t, proxy.latestPort, "9401")
123-
assert.Equal(t, proxy.ipLastByte, 2)
124+
assert.Equal(t, proxy.ipLastByte, byte(2))
125+
}
126+
127+
func TestGetNextIPAddress(t *testing.T) {
128+
testCases := []struct {
129+
a byte
130+
b byte
131+
c byte
132+
d byte
133+
134+
expectedA byte
135+
expectedB byte
136+
expectedC byte
137+
expectedD byte
138+
}{
139+
{ // Case incrementing d
140+
a: 127, b: 0, c: 0, d: 1,
141+
expectedA: 127, expectedB: 0, expectedC: 0, expectedD: 2,
142+
},
143+
{ // Case incrementing d to last byte
144+
a: 127, b: 0, c: 0, d: 254,
145+
expectedA: 127, expectedB: 0, expectedC: 0, expectedD: 255,
146+
},
147+
{ // Case incrementing c and reset d to 1
148+
a: 127, b: 0, c: 0, d: 255,
149+
expectedA: 127, expectedB: 0, expectedC: 1, expectedD: 1,
150+
},
151+
{ // Case incrementing d to last byte
152+
a: 127, b: 0, c: 1, d: 254,
153+
expectedA: 127, expectedB: 0, expectedC: 1, expectedD: 255,
154+
},
155+
{ // Case incrementing c and reset d to 1
156+
a: 127, b: 0, c: 1, d: 255,
157+
expectedA: 127, expectedB: 0, expectedC: 2, expectedD: 1,
158+
},
159+
{ // Case incrementing d to last byte
160+
a: 127, b: 0, c: 254, d: 254,
161+
expectedA: 127, expectedB: 0, expectedC: 254, expectedD: 255,
162+
},
163+
{ // Case incrementing c and reset d to 1
164+
a: 127, b: 0, c: 254, d: 255,
165+
expectedA: 127, expectedB: 0, expectedC: 255, expectedD: 1,
166+
},
167+
{ // Case incrementing d to last byte when c is already on latest byte
168+
a: 127, b: 0, c: 255, d: 254,
169+
expectedA: 127, expectedB: 0, expectedC: 255, expectedD: 255,
170+
},
171+
{ // Case incrementing b and reset c and d to last byte
172+
a: 127, b: 0, c: 255, d: 255,
173+
expectedA: 127, expectedB: 1, expectedC: 1, expectedD: 1,
174+
},
175+
{ // Case incrementing d to last byte when b and c are already on latest byte
176+
a: 127, b: 255, c: 255, d: 254,
177+
expectedA: 127, expectedB: 255, expectedC: 255, expectedD: 255,
178+
},
179+
{ // Reached max level
180+
a: 127, b: 255, c: 255, d: 255,
181+
expectedA: 127, expectedB: 255, expectedC: 255, expectedD: 255,
182+
},
183+
}
184+
185+
for i, testCase := range testCases {
186+
t.Run(fmt.Sprintf("Case %d", i), func(t *testing.T) {
187+
a, b, c, d := getNextIPAddress(testCase.a, testCase.b, testCase.c, testCase.d)
188+
189+
assert.Equal(t, testCase.expectedA, a)
190+
assert.Equal(t, testCase.expectedB, b)
191+
assert.Equal(t, testCase.expectedC, c)
192+
assert.Equal(t, testCase.expectedD, d)
193+
})
194+
}
124195
}

0 commit comments

Comments
 (0)