@@ -48,8 +48,7 @@ type templateValue struct {
48
48
// 3. Supports only TCP and UDP; one session at a time. SCTP is not supported.
49
49
// 4. UDP needs to send PMTU size packets as per RFC7011. In order to guarantee
50
50
// this, maxMsgSize should be set correctly. maxMsgSize is the maximum
51
- // payload (IPFIX message) size, not the maximum packet size. You need to
52
- // compute maxMsgSize based on the desired maximum packet size. If
51
+ // payload (IPFIX message) size, not the maximum packet size. If
53
52
// maxMsgSize is not set correctly, the message may be fragmented.
54
53
type ExportingProcess struct {
55
54
connToCollector net.Conn
@@ -94,17 +93,68 @@ type ExporterInput struct {
94
93
// JSONBufferLen is recommended for sending json records. If not given a
95
94
// valid value, we use a default of 5000B
96
95
JSONBufferLen int
97
- // For UDP, this should be set by taking into account the PMTU and
98
- // header sizes.
99
- MaxMsgSize int
96
+ // MaxMsgSize can be used to provide a custom maximum IPFIX message
97
+ // size. If it is omitted, we will use an appropriate default based on
98
+ // the configured protocol. For UDP, we want to avoid fragmentation, so
99
+ // the MaxMsgSize should be set by taking into account the PMTU and
100
+ // header sizes. The recommended approach is to keep MaxMsgSize unset
101
+ // and provide the correct PMTU value.
102
+ MaxMsgSize int
103
+ // PathMTU is used to calculate the maximum message size when the
104
+ // protocol is UDP. It is ignored for TCP. If both MaxMsgSize and
105
+ // PathMTU are set, and MaxMsgSize is incompatible with the provided
106
+ // PathMTU, exporter initialization will fail.
107
+ PathMTU int
100
108
CheckConnInterval time.Duration
101
109
}
102
110
111
+ func calculateMaxMsgSize (proto string , requestedSize int , pathMTU int , isIPv6 bool ) (int , error ) {
112
+ if requestedSize > 0 && (requestedSize < entities .MinSupportedMsgSize || requestedSize > entities .MaxSocketMsgSize ) {
113
+ return 0 , fmt .Errorf ("requested message size should be between %d and %d" , entities .MinSupportedMsgSize , entities .MaxSocketMsgSize )
114
+ }
115
+ if proto == "tcp" {
116
+ if requestedSize == 0 {
117
+ return entities .MaxSocketMsgSize , nil
118
+ } else {
119
+ return requestedSize , nil
120
+ }
121
+ }
122
+ // UDP protocol
123
+ if pathMTU == 0 {
124
+ if requestedSize == 0 {
125
+ klog .InfoS ("Neither max IPFIX message size nor PMTU were provided, defaulting to min message size" , "messageSize" , entities .MinSupportedMsgSize )
126
+ return entities .MinSupportedMsgSize , nil
127
+ }
128
+ klog .InfoS ("PMTU was not provided, configured message size may cause fragmentation" , "messageSize" , requestedSize )
129
+ return requestedSize , nil
130
+ }
131
+ // 20-byte IPv4, 8-byte UDP header
132
+ mtuDeduction := 28
133
+ if isIPv6 {
134
+ // An extra 20 bytes for IPv6
135
+ mtuDeduction += 20
136
+ }
137
+ maxMsgSize := pathMTU - mtuDeduction
138
+ if maxMsgSize < entities .MinSupportedMsgSize {
139
+ return 0 , fmt .Errorf ("provided PMTU %d is not large enough to accommodate min message size %d" , pathMTU , entities .MinSupportedMsgSize )
140
+ }
141
+ if requestedSize > maxMsgSize {
142
+ return 0 , fmt .Errorf ("requested message size %d exceeds max message size %d calculated from provided PMTU" , requestedSize , maxMsgSize )
143
+ }
144
+ if requestedSize > 0 {
145
+ return requestedSize , nil
146
+ }
147
+ return maxMsgSize , nil
148
+ }
149
+
103
150
// InitExportingProcess takes in collector address(net.Addr format), obsID(observation ID)
104
151
// and tempRefTimeout(template refresh timeout). tempRefTimeout is applicable only
105
152
// for collectors listening over UDP; unit is seconds. For TCP, you can pass any
106
153
// value and it will be ignored. For UDP, if 0 is passed, 600s is used as the default.
107
154
func InitExportingProcess (input ExporterInput ) (* ExportingProcess , error ) {
155
+ if input .CollectorProtocol != "tcp" && input .CollectorProtocol != "udp" {
156
+ return nil , fmt .Errorf ("unsupported collector protocol: %s" , input .CollectorProtocol )
157
+ }
108
158
var conn net.Conn
109
159
var err error
110
160
if input .TLSClientConfig != nil {
@@ -151,6 +201,15 @@ func InitExportingProcess(input ExporterInput) (*ExportingProcess, error) {
151
201
return nil , err
152
202
}
153
203
}
204
+ var isIPv6 bool
205
+ switch addr := conn .RemoteAddr ().(type ) {
206
+ case * net.TCPAddr :
207
+ isIPv6 = addr .IP .To4 () == nil
208
+ case * net.UDPAddr :
209
+ isIPv6 = addr .IP .To4 () == nil
210
+ default :
211
+ return nil , fmt .Errorf ("unsupported net.Addr type %T" , addr )
212
+ }
154
213
expProc := & ExportingProcess {
155
214
connToCollector : conn ,
156
215
obsDomainID : input .ObservationDomainID ,
@@ -169,13 +228,12 @@ func InitExportingProcess(input ExporterInput) (*ExportingProcess, error) {
169
228
expProc .jsonBufferLen = input .JSONBufferLen
170
229
}
171
230
} else {
172
- if input .MaxMsgSize == 0 {
173
- expProc .maxMsgSize = entities .MaxSocketMsgSize
174
- } else if input .MaxMsgSize < entities .MinSupportedMsgSize {
175
- return nil , fmt .Errorf ("maxMsgSize cannot be less than 512B" )
176
- } else {
177
- expProc .maxMsgSize = input .MaxMsgSize
231
+ maxMsgSize , err := calculateMaxMsgSize (input .CollectorProtocol , input .MaxMsgSize , input .PathMTU , isIPv6 )
232
+ if err != nil {
233
+ return nil , err
178
234
}
235
+ klog .InfoS ("Calculated max IPFIX message size" , "size" , maxMsgSize )
236
+ expProc .maxMsgSize = maxMsgSize
179
237
}
180
238
181
239
// Start a goroutine to check whether the collector has already closed the TCP connection.
0 commit comments