Skip to content

Commit f5adc3b

Browse files
authored
add ipcidr support (#177)
* Add support for ipcidr protocol * Move test * Fix gocheck * PR comments * Check byte slice len
1 parent 7830bb7 commit f5adc3b

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

multiaddr_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ func TestConstructFails(t *testing.T) {
2727
"/ip4",
2828
"/ip4/::1",
2929
"/ip4/fdpsofodsajfdoisa",
30+
"/ip4/::/ipcidr/256",
31+
"/ip6/::/ipcidr/1026",
3032
"/ip6",
3133
"/ip6zone",
3234
"/ip6zone/",
@@ -101,9 +103,11 @@ func TestConstructSucceeds(t *testing.T) {
101103
cases := []string{
102104
"/ip4/1.2.3.4",
103105
"/ip4/0.0.0.0",
106+
"/ip4/192.0.2.0/ipcidr/24",
104107
"/ip6/::1",
105108
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
106109
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/udp/1234/quic",
110+
"/ip6/2001:db8::/ipcidr/32",
107111
"/ip6zone/x/ip6/fe80::1",
108112
"/ip6zone/x%y/ip6/fe80::1",
109113
"/ip6zone/x%y/ip6/::",

net/convert.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package manet
22

33
import (
4+
"errors"
45
"fmt"
56
"net"
67
"path/filepath"
@@ -51,6 +52,33 @@ func (cm *CodecMap) ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
5152
return p(maddr)
5253
}
5354

55+
// MultiaddrToIPNet converts a multiaddr to an IPNet. Useful for seeing if another IP address is contained within this multiaddr network+mask
56+
func MultiaddrToIPNet(m ma.Multiaddr) (*net.IPNet, error) {
57+
var ipString string
58+
var mask string
59+
60+
ma.ForEach(m, func(c ma.Component) bool {
61+
if c.Protocol().Code == ma.P_IP4 || c.Protocol().Code == ma.P_IP6 {
62+
ipString = c.Value()
63+
}
64+
if c.Protocol().Code == ma.P_IPCIDR {
65+
mask = c.Value()
66+
}
67+
return ipString == "" || mask == ""
68+
})
69+
70+
if ipString == "" {
71+
return nil, errors.New("no ip protocol found")
72+
}
73+
74+
if mask == "" {
75+
return nil, errors.New("no mask found")
76+
}
77+
78+
_, ipnet, err := net.ParseCIDR(ipString + "/" + string(mask))
79+
return ipnet, err
80+
}
81+
5482
func parseBasicNetMaddr(maddr ma.Multiaddr) (net.Addr, error) {
5583
network, host, err := DialArgs(maddr)
5684
if err != nil {

net/convert_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,65 @@ func TestDialArgs(t *testing.T) {
202202
test("/dns6/abc.com/udp/1234", "udp6", "abc.com:1234") // DNS6:port
203203
test("/dns6/abc.com", "ip6", "abc.com") // Just DNS6
204204
}
205+
206+
func TestMultiaddrToIPNet(t *testing.T) {
207+
type testCase struct {
208+
name string
209+
ma string
210+
ips []string
211+
contained []bool
212+
}
213+
214+
testCases := []testCase{
215+
{
216+
name: "basic",
217+
ma: "/ip4/1.2.3.0/ipcidr/24",
218+
ips: []string{"1.2.3.4", "1.2.3.9", "2.1.1.1"},
219+
contained: []bool{true, true, false},
220+
},
221+
}
222+
223+
for _, tc := range testCases {
224+
t.Run(tc.name, func(t *testing.T) {
225+
ma := ma.StringCast(tc.ma)
226+
227+
ipnet, err := MultiaddrToIPNet(ma)
228+
if err != nil {
229+
t.Fatalf("failed to parse multiaddr %v into ipnet", ma)
230+
}
231+
for i, ipString := range tc.ips {
232+
ip := net.ParseIP(ipString)
233+
if ip == nil {
234+
t.Fatalf("failed to parse IP %s", ipString)
235+
}
236+
if ipnet.Contains(ip) != tc.contained[i] {
237+
t.Fatalf("Contains check failed. Expected %v got %v", tc.contained[i], ipnet.Contains(ip))
238+
}
239+
}
240+
})
241+
}
242+
}
243+
244+
func TestFailMultiaddrToIPNet(t *testing.T) {
245+
type testCase struct {
246+
name string
247+
ma string
248+
}
249+
250+
testCases := []testCase{
251+
{name: "missing ip addr", ma: "/ipcidr/24"},
252+
{name: "wrong mask", ma: "/ip4/1.2.3.0/ipcidr/128"},
253+
{name: "wrong mask", ma: "/ip6/::/ipcidr/255"},
254+
}
255+
256+
for _, tc := range testCases {
257+
t.Run(tc.name, func(t *testing.T) {
258+
ma := ma.StringCast(tc.ma)
259+
260+
_, err := MultiaddrToIPNet(ma)
261+
if err == nil {
262+
t.Fatalf("Expected error when parsing: %s", tc.ma)
263+
}
264+
})
265+
}
266+
}

protocols.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const (
1313
P_DCCP = 0x0021
1414
P_IP6 = 0x0029
1515
P_IP6ZONE = 0x002A
16+
P_IPCIDR = 0x002B
1617
P_QUIC = 0x01CC
1718
P_SCTP = 0x0084
1819
P_CIRCUIT = 0x0122
@@ -103,6 +104,13 @@ var (
103104
Size: 128,
104105
Transcoder: TranscoderIP6,
105106
}
107+
protoIPCIDR = Protocol{
108+
Name: "ipcidr",
109+
Code: P_IPCIDR,
110+
VCode: CodeToVarint(P_IPCIDR),
111+
Size: 8,
112+
Transcoder: TranscoderIPCIDR,
113+
}
106114
// these require varint
107115
protoIP6ZONE = Protocol{
108116
Name: "ip6zone",
@@ -239,6 +247,7 @@ func init() {
239247
protoDCCP,
240248
protoIP6,
241249
protoIP6ZONE,
250+
protoIPCIDR,
242251
protoSCTP,
243252
protoCIRCUIT,
244253
protoONION2,

transcoders.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ func (t twrp) ValidateBytes(b []byte) error {
5454
var TranscoderIP4 = NewTranscoderFromFunctions(ip4StB, ip4BtS, nil)
5555
var TranscoderIP6 = NewTranscoderFromFunctions(ip6StB, ip6BtS, nil)
5656
var TranscoderIP6Zone = NewTranscoderFromFunctions(ip6zoneStB, ip6zoneBtS, ip6zoneVal)
57+
var TranscoderIPCIDR = NewTranscoderFromFunctions(ipcidrStB, ipcidrBtS, nil)
58+
59+
func ipcidrBtS(b []byte) (string, error) {
60+
if len(b) != 1 {
61+
return "", fmt.Errorf("invalid length (should be == 1)")
62+
}
63+
return strconv.Itoa(int(b[0])), nil
64+
}
65+
66+
func ipcidrStB(s string) ([]byte, error) {
67+
ipMask, err := strconv.ParseUint(s, 10, 8)
68+
if err != nil {
69+
return nil, err
70+
}
71+
return []byte{byte(uint8(ipMask))}, nil
72+
}
5773

5874
func ip4StB(s string) ([]byte, error) {
5975
i := net.ParseIP(s).To4()

0 commit comments

Comments
 (0)