Skip to content

Commit 8b82a9d

Browse files
committed
[WIP] probe: conntrack: fix output parsing
TODO: - [ ] don't recompile the regexp for each line - [ ] test under different configuration (SELinux, nf_conntrack_acct) - [ ] check performances compared to sscanf and xml Fixes #2117
1 parent b4e1fc7 commit 8b82a9d

File tree

1 file changed

+105
-63
lines changed

1 file changed

+105
-63
lines changed

probe/endpoint/conntrack.go

Lines changed: 105 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io"
88
"io/ioutil"
99
"path/filepath"
10+
"regexp"
1011
"strconv"
1112
"sync"
1213
"time"
@@ -251,12 +252,7 @@ func removeInplace(s, sep []byte) []byte {
251252
}
252253

253254
func decodeStreamedFlow(scanner *bufio.Scanner) (flow, error) {
254-
var (
255-
// Use ints for parsing unused fields since their allocations
256-
// are almost for free
257-
unused [2]int
258-
f flow
259-
)
255+
var f flow
260256

261257
// Examples:
262258
// " [UPDATE] udp 17 29 src=192.168.2.100 dst=192.168.2.1 sport=57767 dport=53 src=192.168.2.1 dst=192.168.2.100 sport=53 dport=57767"
@@ -271,44 +267,82 @@ func decodeStreamedFlow(scanner *bufio.Scanner) (flow, error) {
271267
}
272268

273269
line = bytes.TrimLeft(line, " ")
270+
var regExpBegin string
271+
var regExpEnd string = `src=([^\s]+).*\sdst=([^\s]+).*\ssport=([^\s]+).*\sdport=([^\s]+).*\ssrc=([^\s]+).*\sdst=([^\s]+).*\ssport=([^\s]+).*\sdport=([^\s]+).*\sid=([^\s]+)`
272+
273+
// Destroy events don't have a timeout or state field
274274
if bytes.HasPrefix(line, destroyTypeB) {
275-
// Destroy events don't have a timeout or state field
276-
_, err = fmt.Sscanf(string(line), "%s %s %d src=%s dst=%s sport=%d dport=%d src=%s dst=%s sport=%d dport=%d id=%d",
277-
&f.Type,
278-
&f.Original.Layer4.Proto,
279-
&unused[0],
280-
&f.Original.Layer3.SrcIP,
281-
&f.Original.Layer3.DstIP,
282-
&f.Original.Layer4.SrcPort,
283-
&f.Original.Layer4.DstPort,
284-
&f.Reply.Layer3.SrcIP,
285-
&f.Reply.Layer3.DstIP,
286-
&f.Reply.Layer4.SrcPort,
287-
&f.Reply.Layer4.DstPort,
288-
&f.Independent.ID,
289-
)
275+
regExpBegin = `(\[[[:alpha:]]*\])\s+([[:alpha:]]*)\s+\d+\s+`
276+
var lineMatch = regexp.MustCompile(regExpBegin + regExpEnd)
277+
matches := lineMatch.FindSubmatch(line)
278+
if len(matches) != 12 {
279+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: matches count: %v ", line, len(matches))
280+
}
281+
282+
f.Type = string(matches[1])
283+
f.Original.Layer4.Proto = string(matches[2])
284+
f.Original.Layer3.SrcIP = string(matches[3])
285+
f.Original.Layer3.DstIP = string(matches[4])
286+
f.Original.Layer4.SrcPort, err = strconv.Atoi(string(matches[5]))
287+
if err != nil {
288+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
289+
}
290+
f.Original.Layer4.DstPort, err = strconv.Atoi(string(matches[6]))
291+
if err != nil {
292+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
293+
}
294+
f.Reply.Layer3.SrcIP = string(matches[7])
295+
f.Reply.Layer3.DstIP = string(matches[8])
296+
f.Reply.Layer4.SrcPort, err = strconv.Atoi(string(matches[9]))
297+
if err != nil {
298+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
299+
}
300+
f.Reply.Layer4.DstPort, err = strconv.Atoi(string(matches[10]))
301+
if err != nil {
302+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
303+
}
304+
f.Independent.ID, err = strconv.ParseInt(string(matches[11]), 10, 64)
305+
if err != nil {
306+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
307+
}
308+
290309
} else {
291-
_, err = fmt.Sscanf(string(line), "%s %s %d %d %s src=%s dst=%s sport=%d dport=%d src=%s dst=%s sport=%d dport=%d id=%d",
292-
&f.Type,
293-
&f.Original.Layer4.Proto,
294-
&unused[0],
295-
&unused[1],
296-
&f.Independent.State,
297-
&f.Original.Layer3.SrcIP,
298-
&f.Original.Layer3.DstIP,
299-
&f.Original.Layer4.SrcPort,
300-
&f.Original.Layer4.DstPort,
301-
&f.Reply.Layer3.SrcIP,
302-
&f.Reply.Layer3.DstIP,
303-
&f.Reply.Layer4.SrcPort,
304-
&f.Reply.Layer4.DstPort,
305-
&f.Independent.ID,
306-
)
307-
}
310+
regExpBegin = `(\[[[:alpha:]]*\])\s+([[:alpha:]]*)\s+\d+\s+\d+\s+([[:alpha:]_]*)\s+`
311+
var lineMatch = regexp.MustCompile(regExpBegin + regExpEnd)
312+
matches := lineMatch.FindSubmatch(line)
313+
if len(matches) != 13 {
314+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: matches count: %v ", line, len(matches))
315+
}
308316

309-
if err != nil {
310-
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
317+
f.Type = string(matches[1])
318+
f.Original.Layer4.Proto = string(matches[2])
319+
f.Independent.State = string(matches[3])
320+
f.Original.Layer3.SrcIP = string(matches[4])
321+
f.Original.Layer3.DstIP = string(matches[5])
322+
f.Original.Layer4.SrcPort, err = strconv.Atoi(string(matches[6]))
323+
if err != nil {
324+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
325+
}
326+
f.Original.Layer4.DstPort, err = strconv.Atoi(string(matches[7]))
327+
if err != nil {
328+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
329+
}
330+
f.Reply.Layer3.SrcIP = string(matches[8])
331+
f.Reply.Layer3.DstIP = string(matches[9])
332+
f.Reply.Layer4.SrcPort, err = strconv.Atoi(string(matches[10]))
333+
if err != nil {
334+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
335+
}
336+
f.Reply.Layer4.DstPort, err = strconv.Atoi(string(matches[11]))
337+
if err != nil {
338+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
339+
}
340+
f.Independent.ID, err = strconv.ParseInt(string(matches[12]), 10, 64)
341+
if err != nil {
342+
return flow{}, fmt.Errorf("Error parsing streamed flow %q: %v ", line, err)
343+
}
311344
}
345+
312346
f.Reply.Layer4.Proto = f.Original.Layer4.Proto
313347
return f, nil
314348
}
@@ -346,12 +380,7 @@ func (c *conntrackWalker) existingConnections() ([]flow, error) {
346380
}
347381

348382
func decodeDumpedFlow(scanner *bufio.Scanner) (flow, error) {
349-
var (
350-
// Use ints for parsing unused fields since allocations
351-
// are almost for free
352-
unused [4]int
353-
f flow
354-
)
383+
var f flow
355384

356385
// Example:
357386
// " tcp 6 431997 ESTABLISHED src=10.32.0.1 dst=10.32.0.1 sport=50274 dport=4040 src=10.32.0.1 dst=10.32.0.1 sport=4040 dport=50274 [ASSURED] mark=0 use=1 id=407401088c"
@@ -361,24 +390,37 @@ func decodeDumpedFlow(scanner *bufio.Scanner) (flow, error) {
361390
return flow{}, err
362391
}
363392

364-
_, err = fmt.Sscanf(string(line), "%s %d %d %s src=%s dst=%s sport=%d dport=%d src=%s dst=%s sport=%d dport=%d mark=%d use=%d id=%d",
365-
&f.Original.Layer4.Proto,
366-
&unused[0],
367-
&unused[1],
368-
&f.Independent.State,
369-
&f.Original.Layer3.SrcIP,
370-
&f.Original.Layer3.DstIP,
371-
&f.Original.Layer4.SrcPort,
372-
&f.Original.Layer4.DstPort,
373-
&f.Reply.Layer3.SrcIP,
374-
&f.Reply.Layer3.DstIP,
375-
&f.Reply.Layer4.SrcPort,
376-
&f.Reply.Layer4.DstPort,
377-
&unused[2],
378-
&unused[3],
379-
&f.Independent.ID,
380-
)
393+
var regExpEnd string = `src=([^\s]+).*\sdst=([^\s]+).*\ssport=([^\s]+).*\sdport=([^\s]+).*\ssrc=([^\s]+).*\sdst=([^\s]+).*\ssport=([^\s]+).*\sdport=([^\s]+).*\sid=([^\s]+)`
394+
var regExpBegin string = `([[:alpha:]]*)\s+\d+\s+\d+\s+([[:alpha:]_]*)\s+`
395+
var lineMatch = regexp.MustCompile(regExpBegin + regExpEnd)
396+
matches := lineMatch.FindSubmatch(line)
397+
if len(matches) != 12 {
398+
return flow{}, fmt.Errorf("Error parsing dumped flow %q: matches count: %v ", line, len(matches))
399+
}
381400

401+
f.Original.Layer4.Proto = string(matches[1])
402+
f.Independent.State = string(matches[2])
403+
f.Original.Layer3.SrcIP = string(matches[3])
404+
f.Original.Layer3.DstIP = string(matches[4])
405+
f.Original.Layer4.SrcPort, err = strconv.Atoi(string(matches[5]))
406+
if err != nil {
407+
return flow{}, fmt.Errorf("Error parsing dumped flow %q: %v ", line, err)
408+
}
409+
f.Original.Layer4.DstPort, err = strconv.Atoi(string(matches[6]))
410+
if err != nil {
411+
return flow{}, fmt.Errorf("Error parsing dumped flow %q: %v ", line, err)
412+
}
413+
f.Reply.Layer3.SrcIP = string(matches[7])
414+
f.Reply.Layer3.DstIP = string(matches[8])
415+
f.Reply.Layer4.SrcPort, err = strconv.Atoi(string(matches[9]))
416+
if err != nil {
417+
return flow{}, fmt.Errorf("Error parsing dumped flow %q: %v ", line, err)
418+
}
419+
f.Reply.Layer4.DstPort, err = strconv.Atoi(string(matches[10]))
420+
if err != nil {
421+
return flow{}, fmt.Errorf("Error parsing dumped flow %q: %v ", line, err)
422+
}
423+
f.Independent.ID, err = strconv.ParseInt(string(matches[11]), 10, 64)
382424
if err != nil {
383425
return flow{}, fmt.Errorf("Error parsing dumped flow %q: %v ", line, err)
384426
}

0 commit comments

Comments
 (0)