Skip to content

Commit 5c6d858

Browse files
author
Mario Macias
authored
Merge IPv4 and IPv6 addresses into a single type (#32)
1 parent 3fd4695 commit 5c6d858

File tree

9 files changed

+64
-68
lines changed

9 files changed

+64
-68
lines changed

bpf/flow.h

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,11 @@ struct data_link {
1717
} __attribute__((packed));
1818

1919
// L3 network layer
20-
struct v4ip {
21-
u32 src_ip;
22-
u32 dst_ip;
23-
} __attribute__((packed));
24-
25-
struct v6ip {
26-
struct in6_addr src_ip6;
27-
struct in6_addr dst_ip6;
28-
} __attribute__((packed));
29-
20+
// IPv4 addresses are encoded as IPv6 addresses with prefix ::ffff/96
21+
// as described in https://datatracker.ietf.org/doc/html/rfc4038#section-4.2
3022
struct network {
31-
struct v4ip v4ip;
32-
struct v6ip v6ip;
23+
struct in6_addr src_ip;
24+
struct in6_addr dst_ip;
3325
} __attribute__((packed));
3426

3527
// L4 transport layer

bpf/flows.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#include <linux/bpf.h>
77
#include <linux/types.h>
88
#include <linux/if_ether.h>
9+
910
#include <bpf_helpers.h>
1011
#include <bpf_endian.h>
12+
1113
#include "flow.h"
1214

1315
#define DISCARD 1
@@ -27,14 +29,18 @@ struct {
2729
// Constant definitions, to be overridden by the invoker
2830
volatile const u32 sampling = 0;
2931

32+
const u8 ip4in6[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
33+
3034
// sets flow fields from IPv4 header information
3135
static inline int fill_iphdr(struct iphdr *ip, void *data_end, struct flow *flow) {
3236
if ((void *)ip + sizeof(*ip) > data_end) {
3337
return DISCARD;
3438
}
3539

36-
flow->network.v4ip.src_ip = __bpf_ntohl(ip->saddr);
37-
flow->network.v4ip.dst_ip = __bpf_ntohl(ip->daddr);
40+
__builtin_memcpy(flow->network.src_ip.s6_addr, ip4in6, sizeof(ip4in6));
41+
__builtin_memcpy(flow->network.dst_ip.s6_addr, ip4in6, sizeof(ip4in6));
42+
__builtin_memcpy(flow->network.src_ip.s6_addr + sizeof(ip4in6), &ip->saddr, sizeof(ip->saddr));
43+
__builtin_memcpy(flow->network.dst_ip.s6_addr + sizeof(ip4in6), &ip->daddr, sizeof(ip->daddr));
3844
flow->transport.protocol = ip->protocol;
3945

4046
switch (ip->protocol) {
@@ -64,8 +70,8 @@ static inline int fill_ip6hdr(struct ipv6hdr *ip, void *data_end, struct flow *f
6470
return DISCARD;
6571
}
6672

67-
flow->network.v6ip.src_ip6 = ip->saddr;
68-
flow->network.v6ip.dst_ip6 = ip->daddr;
73+
flow->network.src_ip = ip->saddr;
74+
flow->network.dst_ip = ip->daddr;
6975
flow->transport.protocol = ip->nexthdr;
7076

7177
switch (ip->nexthdr) {

pkg/agent/agent_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ func TestFlowsAgent(t *testing.T) {
7070
fr1.Direction = 1 // egress
7171
fr1.DataLink.SrcMac = flow.MacAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}
7272
fr1.DataLink.DstMac = flow.MacAddr{0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC}
73-
fr1.Network.SrcAddr = 0x11223344
74-
fr1.Network.DstAddr = 0xaabbccdd
73+
fr1.Network.SrcAddr = flow.IPAddr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff,
74+
0x11, 0x22, 0x33, 0x44}
75+
fr1.Network.DstAddr = flow.IPAddr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff,
76+
0xaa, 0xbb, 0xcc, 0xdd}
7577
fr1.Transport.Protocol = 123
7678
fr1.Transport.SrcPort = 456
7779
fr1.Transport.DstPort = 789

pkg/ebpf/bpf_bpfeb.o

656 Bytes
Binary file not shown.

pkg/ebpf/bpf_bpfel.o

352 Bytes
Binary file not shown.

pkg/exporter/grpc_proto.go

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ func v4FlowToPB(fr *flow.Record) *pbflow.Record {
6363
DstMac: macToUint64(&fr.DataLink.DstMac),
6464
},
6565
Network: &pbflow.Network{
66-
SrcAddr: &pbflow.IP{IpFamily: &pbflow.IP_Ipv4{Ipv4: uint32(fr.Network.SrcAddr)}},
67-
DstAddr: &pbflow.IP{IpFamily: &pbflow.IP_Ipv4{Ipv4: uint32(fr.Network.DstAddr)}},
66+
SrcAddr: &pbflow.IP{IpFamily: &pbflow.IP_Ipv4{Ipv4: fr.Network.SrcAddr.IntEncodeV4()}},
67+
DstAddr: &pbflow.IP{IpFamily: &pbflow.IP_Ipv4{Ipv4: fr.Network.DstAddr.IntEncodeV4()}},
6868
},
6969
Transport: &pbflow.Transport{
7070
Protocol: uint32(fr.Transport.Protocol),
@@ -94,18 +94,8 @@ func v6FlowToPB(fr *flow.Record) *pbflow.Record {
9494
DstMac: macToUint64(&fr.DataLink.DstMac),
9595
},
9696
Network: &pbflow.Network{
97-
SrcAddr: &pbflow.IP{IpFamily: &pbflow.IP_Ipv6{Ipv6: []byte{fr.NetworkV6.SrcAddr[0],
98-
fr.NetworkV6.SrcAddr[1], fr.NetworkV6.SrcAddr[2], fr.NetworkV6.SrcAddr[3],
99-
fr.NetworkV6.SrcAddr[4], fr.NetworkV6.SrcAddr[5], fr.NetworkV6.SrcAddr[6],
100-
fr.NetworkV6.SrcAddr[7], fr.NetworkV6.SrcAddr[8], fr.NetworkV6.SrcAddr[9],
101-
fr.NetworkV6.SrcAddr[10], fr.NetworkV6.SrcAddr[11], fr.NetworkV6.SrcAddr[12],
102-
fr.NetworkV6.SrcAddr[13], fr.NetworkV6.SrcAddr[14], fr.NetworkV6.SrcAddr[15]}}},
103-
DstAddr: &pbflow.IP{IpFamily: &pbflow.IP_Ipv6{Ipv6: []byte{fr.NetworkV6.DstAddr[0],
104-
fr.NetworkV6.DstAddr[1], fr.NetworkV6.DstAddr[2], fr.NetworkV6.DstAddr[3],
105-
fr.NetworkV6.DstAddr[4], fr.NetworkV6.DstAddr[5], fr.NetworkV6.DstAddr[6],
106-
fr.NetworkV6.DstAddr[7], fr.NetworkV6.DstAddr[8], fr.NetworkV6.DstAddr[9],
107-
fr.NetworkV6.DstAddr[10], fr.NetworkV6.DstAddr[11], fr.NetworkV6.DstAddr[12],
108-
fr.NetworkV6.DstAddr[13], fr.NetworkV6.DstAddr[14], fr.NetworkV6.DstAddr[15]}}},
97+
SrcAddr: &pbflow.IP{IpFamily: &pbflow.IP_Ipv6{Ipv6: fr.Network.SrcAddr[:]}},
98+
DstAddr: &pbflow.IP{IpFamily: &pbflow.IP_Ipv6{Ipv6: fr.Network.DstAddr[:]}},
10999
},
110100
Transport: &pbflow.Transport{
111101
Protocol: uint32(fr.Transport.Protocol),

pkg/flow/account_test.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,28 @@ import (
1010

1111
const timeout = 5 * time.Second
1212

13+
var (
14+
srcAddr1 = IPAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
15+
0x12, 0x34, 0x56, 0x78}
16+
srcAddr2 = IPAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
17+
0xaa, 0xbb, 0xcc, 0xdd}
18+
dstAddr1 = IPAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
19+
0x43, 0x21, 0x00, 0xff}
20+
dstAddr2 = IPAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
21+
0x11, 0x22, 0x33, 0x44}
22+
)
23+
1324
var k1 = key{
1425
Transport: Transport{SrcPort: 333, DstPort: 8080},
15-
Network: Network{SrcAddr: 0x12345678, DstAddr: 0x432100ff},
26+
Network: Network{SrcAddr: srcAddr1, DstAddr: dstAddr1},
1627
}
1728
var k2 = key{
1829
Transport: Transport{SrcPort: 12, DstPort: 8080},
19-
Network: Network{SrcAddr: 0xaabbccdd, DstAddr: 0x432100ff},
30+
Network: Network{SrcAddr: srcAddr2, DstAddr: dstAddr1},
2031
}
2132
var k3 = key{
2233
Transport: Transport{SrcPort: 333, DstPort: 443},
23-
Network: Network{SrcAddr: 0x12345678, DstAddr: 0x11223344},
34+
Network: Network{SrcAddr: srcAddr1, DstAddr: dstAddr2},
2435
}
2536

2637
func TestEvict_MaxEntries(t *testing.T) {

pkg/flow/record.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,29 @@ import (
1010
)
1111

1212
const MacLen = 6
13-
const IP6Len = 16
14-
const IPv6Type = 0x86DD
1513

1614
// IPv6Type value as defined in IEEE 802: https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
15+
const IPv6Type = 0x86DD
1716

18-
type RawIP uint32
1917
type HumanBytes uint64
2018
type MacAddr [MacLen]uint8
2119
type Direction uint8
2220
type TransportProtocol uint8
23-
type IP6Addr [IP6Len]uint8
21+
22+
// IPAddr encodes v4 and v6 IPs with a fixed length.
23+
// IPv4 addresses are encoded as IPv6 addresses with prefix ::ffff/96
24+
// as described in https://datatracker.ietf.org/doc/html/rfc4038#section-4.2
25+
// (same behavior as Go's net.IP type)
26+
type IPAddr [net.IPv6len]uint8
2427

2528
type DataLink struct {
2629
SrcMac MacAddr
2730
DstMac MacAddr
2831
}
2932

3033
type Network struct {
31-
SrcAddr RawIP
32-
DstAddr RawIP
33-
}
34-
35-
type NetworkV6 struct {
36-
SrcAddr IP6Addr
37-
DstAddr IP6Addr
34+
SrcAddr IPAddr
35+
DstAddr IPAddr
3836
}
3937

4038
type Transport struct {
@@ -49,7 +47,6 @@ type key struct {
4947
Direction Direction
5048
DataLink DataLink
5149
Network Network
52-
NetworkV6 NetworkV6
5350
Transport Transport
5451
// TODO: add TOS field
5552
}
@@ -79,6 +76,18 @@ func (r *Record) Accumulate(src *Record) {
7976
r.Packets += src.Packets
8077
}
8178

79+
// IP returns the net.IP equivalent object
80+
func (ip *IPAddr) IP() net.IP {
81+
return ip[:]
82+
}
83+
84+
// IntEncodeV4 encodes an IPv4 address as an integer (in network encoding, big endian).
85+
// It assumes that the passed IP is already IPv4. Otherwise it would just encode the
86+
// last 4 bytes of an IPv6 address
87+
func (ip *IPAddr) IntEncodeV4() uint32 {
88+
return binary.BigEndian.Uint32(ip[net.IPv6len-net.IPv4len : net.IPv6len])
89+
}
90+
8291
func (p TransportProtocol) String() string {
8392
switch p {
8493
case 0:
@@ -118,16 +127,6 @@ func (p TransportProtocol) MarshalJSON() ([]byte, error) {
118127
return []byte("\"" + p.String() + "\""), nil
119128
}
120129

121-
func (i RawIP) String() string {
122-
ip := make(net.IP, 4)
123-
binary.BigEndian.PutUint32(ip, uint32(i))
124-
return ip.String()
125-
}
126-
127-
func (i RawIP) MarshalJSON() ([]byte, error) {
128-
return []byte("\"" + i.String() + "\""), nil
129-
}
130-
131130
const (
132131
kibi = 1024
133132
mibi = kibi * 1024

pkg/flow/record_test.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@ func TestRecordBinaryEncoding(t *testing.T) {
1616
0x03, // u16 direction
1717
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, // data_link: u8[6] src_mac
1818
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // data_link: u8[6] dst_mac
19-
0x06, 0x07, 0x08, 0x09, // network: u32 src_ip
20-
0x0a, 0x0b, 0x0c, 0x0d, // network: u32 dst_ip
21-
0x06, 0x07, 0x08, 0x09, 0x06, 0x07, 0x08, 0x09, 0x06, 0x07, 0x08, 0x09, 0x06, 0x07, 0x08, 0x09, // network6: u8[16] src_ip
22-
0x0a, 0x0b, 0x0c, 0x0d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0a, 0x0b, 0x0c, 0x0d, // network6: u8[16] dst_ip
19+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x06, 0x07, 0x08, 0x09, // network: u8[16] src_ip
20+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, // network: u32 dst_ip
2321
0x0e, 0x0f, // transport: u16 src_port
2422
0x10, 0x11, // transport: u16 dst_port
2523
0x12, // transport: u8protocol
@@ -37,12 +35,8 @@ func TestRecordBinaryEncoding(t *testing.T) {
3735
DstMac: MacAddr{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f},
3836
},
3937
Network: Network{
40-
SrcAddr: 0x09080706,
41-
DstAddr: 0x0d0c0b0a,
42-
},
43-
NetworkV6: NetworkV6{
44-
SrcAddr: IP6Addr{0x06, 0x07, 0x08, 0x09, 0x06, 0x07, 0x08, 0x09, 0x06, 0x07, 0x08, 0x09, 0x06, 0x07, 0x08, 0x09},
45-
DstAddr: IP6Addr{0x0a, 0x0b, 0x0c, 0x0d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0a, 0x0b, 0x0c, 0x0d},
38+
SrcAddr: IPAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x06, 0x07, 0x08, 0x09},
39+
DstAddr: IPAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d},
4640
},
4741
Transport: Transport{
4842
SrcPort: 0x0f0e,
@@ -53,5 +47,7 @@ func TestRecordBinaryEncoding(t *testing.T) {
5347
Bytes: 0x1a19181716151413,
5448
},
5549
}, *fr)
56-
50+
// assert that IP addresses are interpreted as IPv4 addresses
51+
assert.Equal(t, "6.7.8.9", fr.Network.SrcAddr.IP().String())
52+
assert.Equal(t, "10.11.12.13", fr.Network.DstAddr.IP().String())
5753
}

0 commit comments

Comments
 (0)