Skip to content

Commit b5a90dc

Browse files
authored
Support IPv6 for egress metrics (#333)
* Support IPv6 for egress metrics * Update the document Signed-off-by: terashima <tomoya-terashima@cybozu.co.jp>
1 parent ea2ce3d commit b5a90dc

File tree

4 files changed

+99
-43
lines changed

4 files changed

+99
-43
lines changed

docs/cmd-coil-egress.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ This value is from the result of `iptables -t nat -L POSTROUTING -vn`.
8484
| `namespace` | The egress resource namespace |
8585
| `egress` | The egress resource name |
8686
| `pod` | The pod name |
87+
| `protocol` | The protocol name |
8788

8889
### `coil_egress_nftables_masqueraded_bytes_total`
8990

@@ -95,6 +96,7 @@ This value is from the result of `iptables -t nat -L POSTROUTING -vn`.
9596
| `namespace` | The egress resource namespace |
9697
| `egress` | The egress resource name |
9798
| `pod` | The pod name |
99+
| `protocol` | The protocol name |
98100

99101
### `coil_egress_nftables_invalid_packets_total`
100102

@@ -106,6 +108,7 @@ This value is from the result of `iptables -t filter -L -vn`.
106108
| `namespace` | The egress resource namespace |
107109
| `egress` | The egress resource name |
108110
| `pod` | The pod name |
111+
| `protocol` | The protocol name |
109112

110113
### `coil_egress_nftables_invalid_bytes_total`
111114

@@ -117,4 +120,5 @@ This value is from the result of `iptables -t filter -L -vn`.
117120
| `namespace` | The egress resource namespace |
118121
| `egress` | The egress resource name |
119122
| `pod` | The pod name |
123+
| `protocol` | The protocol name |
120124

v2/cmd/coil-egress/sub/run.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ func init() {
3737

3838
// +kubebuilder:scaffold:scheme
3939

40-
metrics.Registry.MustRegister(egressMetrics.NfConnctackCount)
41-
metrics.Registry.MustRegister(egressMetrics.NfConnctackLimit)
40+
metrics.Registry.MustRegister(egressMetrics.NfConntrackCount)
41+
metrics.Registry.MustRegister(egressMetrics.NfConntrackLimit)
4242
metrics.Registry.MustRegister(egressMetrics.NfTableMasqueradeBytes)
4343
metrics.Registry.MustRegister(egressMetrics.NfTableMasqueradePackets)
4444
metrics.Registry.MustRegister(egressMetrics.NfTableInvalidBytes)
@@ -63,18 +63,26 @@ func subMain() error {
6363
return errors.New(constants.EnvAddresses + " environment variable must be set")
6464
}
6565
var ipv4, ipv6 net.IP
66+
protocolMap := make(map[string]struct{})
6667
for _, addr := range myAddresses {
6768
n := net.ParseIP(addr)
6869
if n == nil {
6970
return errors.New(constants.EnvAddresses + "contains invalid address: " + addr)
7071
}
7172
if n4 := n.To4(); n4 != nil {
7273
ipv4 = n4
74+
protocolMap[constants.FamilyIPv4] = struct{}{}
7375
} else {
7476
ipv6 = n
77+
protocolMap[constants.FamilyIPv6] = struct{}{}
7578
}
7679
}
7780

81+
protocols := make([]string, 0)
82+
for protocol := range protocolMap {
83+
protocols = append(protocols, protocol)
84+
}
85+
7886
setupLog.Info("detected local IP addresses", "ipv4", ipv4.String(), "ipv6", ipv6.String())
7987

8088
timeout := gracefulTimeout
@@ -117,7 +125,7 @@ func subMain() error {
117125

118126
setupLog.Info("setup egress metrics collector")
119127
runner := egressMetrics.NewRunner()
120-
egressCollector, err := egressMetrics.NewEgressCollector(myNS, os.Getenv("HOSTNAME"), myName)
128+
egressCollector, err := egressMetrics.NewEgressCollector(myNS, os.Getenv("HOSTNAME"), myName, protocols)
121129
if err != nil {
122130
return err
123131
}

v2/pkg/constants/constants.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,9 @@ const MetricsNS = "coil"
9090
const (
9191
DefaultPool = "default"
9292
)
93+
94+
// Layer 3 families
95+
const (
96+
FamilyIPv4 = "ipv4"
97+
FamilyIPv6 = "ipv6"
98+
)

v2/pkg/metrics/egress.go

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package metrics
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"os"
78
"strconv"
89
"strings"
@@ -19,14 +20,14 @@ const (
1920
)
2021

2122
var (
22-
NfConnctackCount = prometheus.NewGaugeVec(prometheus.GaugeOpts{
23+
NfConntrackCount = prometheus.NewGaugeVec(prometheus.GaugeOpts{
2324
Namespace: constants.MetricsNS,
2425
Subsystem: "egress",
2526
Name: "nf_conntrack_entries_count",
2627
Help: "the number of entries in the nf_conntrack table",
2728
}, []string{"namespace", "pod", "egress"})
2829

29-
NfConnctackLimit = prometheus.NewGaugeVec(prometheus.GaugeOpts{
30+
NfConntrackLimit = prometheus.NewGaugeVec(prometheus.GaugeOpts{
3031
Namespace: constants.MetricsNS,
3132
Subsystem: "egress",
3233
Name: "nf_conntrack_entries_limit",
@@ -38,43 +39,56 @@ var (
3839
Subsystem: "egress",
3940
Name: "nftables_masqueraded_packets_total",
4041
Help: "the number of packets that are masqueraded by nftables",
41-
}, []string{"namespace", "pod", "egress"})
42+
}, []string{"namespace", "pod", "egress", "protocol"})
4243

4344
NfTableMasqueradeBytes = prometheus.NewGaugeVec(prometheus.GaugeOpts{
4445
Namespace: constants.MetricsNS,
4546
Subsystem: "egress",
4647
Name: "nftables_masqueraded_bytes_total",
4748
Help: "the number of bytes that are masqueraded by nftables",
48-
}, []string{"namespace", "pod", "egress"})
49+
}, []string{"namespace", "pod", "egress", "protocol"})
4950

5051
NfTableInvalidPackets = prometheus.NewGaugeVec(prometheus.GaugeOpts{
5152
Namespace: constants.MetricsNS,
5253
Subsystem: "egress",
5354
Name: "nftables_invalid_packets_total",
5455
Help: "the number of packets that are dropped as invalid packets by nftables",
55-
}, []string{"namespace", "pod", "egress"})
56+
}, []string{"namespace", "pod", "egress", "protocol"})
5657

5758
NfTableInvalidBytes = prometheus.NewGaugeVec(prometheus.GaugeOpts{
5859
Namespace: constants.MetricsNS,
5960
Subsystem: "egress",
6061
Name: "nftables_invalid_bytes_total",
6162
Help: "the number of bytes that are dropped as invalid packets by nftables",
62-
}, []string{"namespace", "pod", "egress"})
63+
}, []string{"namespace", "pod", "egress", "protocol"})
6364
)
6465

6566
type egressCollector struct {
66-
conn *nftables.Conn
67-
nfConnctackCount prometheus.Gauge
68-
nfConnctackLimit prometheus.Gauge
69-
nfTablesNATPackets prometheus.Gauge
70-
nfTablesNATBytes prometheus.Gauge
71-
nfTablesInvalidPackets prometheus.Gauge
72-
nfTablesInvalidBytes prometheus.Gauge
67+
conn *nftables.Conn
68+
nfConntrackCount prometheus.Gauge
69+
nfConntrackLimit prometheus.Gauge
70+
perProtocol map[string]*nfTablesPerProtocolMetrics
71+
}
72+
73+
type nfTablesPerProtocolMetrics struct {
74+
NATPackets prometheus.Gauge
75+
NATBytes prometheus.Gauge
76+
InvalidPackets prometheus.Gauge
77+
InvalidBytes prometheus.Gauge
78+
}
79+
80+
func newNfTablesPerProtocolMetrics(ns, pod, egress, protocol string) *nfTablesPerProtocolMetrics {
81+
return &nfTablesPerProtocolMetrics{
82+
NATPackets: NfTableMasqueradePackets.WithLabelValues(ns, pod, egress, protocol),
83+
NATBytes: NfTableMasqueradeBytes.WithLabelValues(ns, pod, egress, protocol),
84+
InvalidPackets: NfTableInvalidPackets.WithLabelValues(ns, pod, egress, protocol),
85+
InvalidBytes: NfTableInvalidBytes.WithLabelValues(ns, pod, egress, protocol),
86+
}
7387
}
7488

75-
func NewEgressCollector(ns, pod, egress string) (Collector, error) {
76-
NfConnctackCount.Reset()
77-
NfConnctackLimit.Reset()
89+
func NewEgressCollector(ns, pod, egress string, protocols []string) (Collector, error) {
90+
NfConntrackCount.Reset()
91+
NfConntrackLimit.Reset()
7892
NfTableMasqueradeBytes.Reset()
7993
NfTableMasqueradePackets.Reset()
8094
NfTableInvalidPackets.Reset()
@@ -85,14 +99,16 @@ func NewEgressCollector(ns, pod, egress string) (Collector, error) {
8599
return nil, err
86100
}
87101

102+
perProtocols := make(map[string]*nfTablesPerProtocolMetrics)
103+
for _, protocol := range protocols {
104+
perProtocols[protocol] = newNfTablesPerProtocolMetrics(ns, pod, egress, protocol)
105+
}
106+
88107
return &egressCollector{
89-
conn: c,
90-
nfConnctackCount: NfConnctackCount.WithLabelValues(ns, pod, egress),
91-
nfConnctackLimit: NfConnctackLimit.WithLabelValues(ns, pod, egress),
92-
nfTablesNATPackets: NfTableMasqueradePackets.WithLabelValues(ns, pod, egress),
93-
nfTablesNATBytes: NfTableMasqueradeBytes.WithLabelValues(ns, pod, egress),
94-
nfTablesInvalidPackets: NfTableInvalidPackets.WithLabelValues(ns, pod, egress),
95-
nfTablesInvalidBytes: NfTableInvalidBytes.WithLabelValues(ns, pod, egress),
108+
conn: c,
109+
nfConntrackCount: NfConntrackCount.WithLabelValues(ns, pod, egress),
110+
nfConntrackLimit: NfConntrackLimit.WithLabelValues(ns, pod, egress),
111+
perProtocol: perProtocols,
96112
}, nil
97113
}
98114

@@ -106,33 +122,40 @@ func (c *egressCollector) Update(ctx context.Context) error {
106122
if err != nil {
107123
return err
108124
}
109-
c.nfConnctackCount.Set(float64(val))
125+
c.nfConntrackCount.Set(float64(val))
110126

111127
val, err = readUintFromFile(NF_CONNTRACK_LIMIT_PATH)
112128
if err != nil {
113129
return err
114130
}
115-
c.nfConnctackLimit.Set(float64(val))
131+
c.nfConntrackLimit.Set(float64(val))
116132

117-
natPackets, natBytes, err := c.getNfTablesNATCounter()
118-
if err != nil {
119-
return err
120-
}
121-
c.nfTablesNATPackets.Set(float64(natPackets))
122-
c.nfTablesNATBytes.Set(float64(natBytes))
133+
for protocol, nfTablesMetrics := range c.perProtocol {
134+
natPackets, natBytes, err := c.getNfTablesNATCounter(protocol)
135+
if err != nil {
136+
return err
137+
}
138+
nfTablesMetrics.NATPackets.Set(float64(natPackets))
139+
nfTablesMetrics.NATBytes.Set(float64(natBytes))
140+
141+
invalidPackets, invalidBytes, err := c.getNfTablesInvalidCounter(protocol)
142+
if err != nil {
143+
return err
144+
}
145+
nfTablesMetrics.InvalidPackets.Set(float64(invalidPackets))
146+
nfTablesMetrics.InvalidBytes.Set(float64(invalidBytes))
123147

124-
invalidPackets, invalidBytes, err := c.getNfTablesInvalidCounter()
125-
if err != nil {
126-
return err
127148
}
128-
c.nfTablesInvalidPackets.Set(float64(invalidPackets))
129-
c.nfTablesInvalidBytes.Set(float64(invalidBytes))
130149

131150
return nil
132151
}
133152

134-
func (c *egressCollector) getNfTablesNATCounter() (uint64, uint64, error) {
135-
table := &nftables.Table{Family: nftables.TableFamilyIPv4, Name: "nat"}
153+
func (c *egressCollector) getNfTablesNATCounter(protocol string) (uint64, uint64, error) {
154+
family, err := stringToTableFamily(protocol)
155+
if err != nil {
156+
return 0, 0, err
157+
}
158+
table := &nftables.Table{Family: family, Name: "nat"}
136159
rules, err := c.conn.GetRules(table, &nftables.Chain{
137160
Name: "POSTROUTING",
138161
Type: nftables.ChainTypeNAT,
@@ -153,8 +176,12 @@ func (c *egressCollector) getNfTablesNATCounter() (uint64, uint64, error) {
153176
return 0, 0, errors.New("a masquerade rule is not found")
154177
}
155178

156-
func (c *egressCollector) getNfTablesInvalidCounter() (uint64, uint64, error) {
157-
table := &nftables.Table{Family: nftables.TableFamilyIPv4, Name: "filter"}
179+
func (c *egressCollector) getNfTablesInvalidCounter(protocol string) (uint64, uint64, error) {
180+
family, err := stringToTableFamily(protocol)
181+
if err != nil {
182+
return 0, 0, err
183+
}
184+
table := &nftables.Table{Family: family, Name: "filter"}
158185
rules, err := c.conn.GetRules(table, &nftables.Chain{
159186
Name: "FORWARD",
160187
Type: nftables.ChainTypeFilter,
@@ -186,3 +213,14 @@ func readUintFromFile(path string) (uint64, error) {
186213
}
187214
return val, nil
188215
}
216+
217+
func stringToTableFamily(protocol string) (nftables.TableFamily, error) {
218+
switch protocol {
219+
case constants.FamilyIPv4:
220+
return nftables.TableFamilyIPv4, nil
221+
case constants.FamilyIPv6:
222+
return nftables.TableFamilyIPv6, nil
223+
default:
224+
return nftables.TableFamilyUnspecified, fmt.Errorf("unsupported family type: %s", protocol)
225+
}
226+
}

0 commit comments

Comments
 (0)