Skip to content

Commit f0f502e

Browse files
authored
Merge branch 'main' into fix-ipv6-ident
2 parents 3bb8ea9 + 7eaddee commit f0f502e

File tree

6 files changed

+190
-10
lines changed

6 files changed

+190
-10
lines changed

match_modules.go

+106
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ func (p *Parser) parseMatch(ms *[]Match) (state, error) {
3030
s, err = p.parseStatistic(&m.Flags)
3131
case "multiport":
3232
s, err = p.parseMultiport(&m.Flags)
33+
case "icmp":
34+
s, err = p.parseIcmp(&m.Flags)
35+
case "icmp6":
36+
s, err = p.parseIcmp6(&m.Flags)
3337
default:
3438
if _, ok := matchModules[lit]; ok {
3539
return sError, fmt.Errorf("match modules %q is not implemented", lit)
@@ -529,3 +533,105 @@ func (p *Parser) parseComment(f *map[string]Flag) (state, error) {
529533
}
530534
return sStart, nil
531535
}
536+
537+
func (p *Parser) parseIcmp(f *map[string]Flag) (state, error) {
538+
s := sStart
539+
for tok, lit := p.scanIgnoreWhitespace(); tok != EOF; tok, lit = p.scanIgnoreWhitespace() {
540+
for nextValue := false; !nextValue; {
541+
nextValue = true
542+
switch s {
543+
case sStart:
544+
switch tok {
545+
case NOT:
546+
s = sINotF
547+
case FLAG:
548+
s = sIF
549+
nextValue = false
550+
default:
551+
return sError, fmt.Errorf("unexpected token %q, expected flag, or \"!\"", lit)
552+
}
553+
case sINotF:
554+
switch {
555+
case lit == "--icmp-type":
556+
_, lit := p.scanIgnoreWhitespace()
557+
(*f)["icmp-type"] = Flag{
558+
Not: true,
559+
Values: []string{lit},
560+
}
561+
s = sStart
562+
default:
563+
p.unscan(1)
564+
return sNot, nil
565+
}
566+
case sIF:
567+
switch {
568+
case lit == "--icmp-type":
569+
_, lit := p.scanIgnoreWhitespace()
570+
(*f)["icmp-type"] = Flag{
571+
Values: []string{lit},
572+
}
573+
s = sStart
574+
default:
575+
// The end of the match statement is reached.
576+
p.unscan(1)
577+
return sStart, nil
578+
}
579+
580+
default:
581+
return sStart, errors.New("unexpected error parsing match extension")
582+
}
583+
}
584+
}
585+
return sStart, nil
586+
}
587+
588+
func (p *Parser) parseIcmp6(f *map[string]Flag) (state, error) {
589+
s := sStart
590+
for tok, lit := p.scanIgnoreWhitespace(); tok != EOF; tok, lit = p.scanIgnoreWhitespace() {
591+
for nextValue := false; !nextValue; {
592+
nextValue = true
593+
switch s {
594+
case sStart:
595+
switch tok {
596+
case NOT:
597+
s = sINotF
598+
case FLAG:
599+
s = sIF
600+
nextValue = false
601+
default:
602+
return sError, fmt.Errorf("unexpected token %q, expected flag, or \"!\"", lit)
603+
}
604+
case sINotF:
605+
switch {
606+
case lit == "--icmpv6-type":
607+
_, lit := p.scanIgnoreWhitespace()
608+
(*f)["icmpv6-type"] = Flag{
609+
Not: true,
610+
Values: []string{lit},
611+
}
612+
s = sStart
613+
default:
614+
p.unscan(1)
615+
return sNot, nil
616+
}
617+
case sIF:
618+
switch {
619+
case lit == "--icmpv6-type":
620+
_, lit := p.scanIgnoreWhitespace()
621+
(*f)["icmpv6-type"] = Flag{
622+
Values: []string{lit},
623+
}
624+
s = sStart
625+
default:
626+
// The end of the match statement is reached.
627+
p.unscan(1)
628+
return sStart, nil
629+
}
630+
631+
default:
632+
return sStart, errors.New("unexpected error parsing match extension")
633+
}
634+
}
635+
}
636+
return sStart, nil
637+
}

parser.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ func (d Policy) String() string {
6464
return fmt.Sprintf("%s%s %s", prefix, d.Chain, d.Action)
6565
}
6666

67+
type Commit struct{}
68+
69+
func (c Commit) String() string {
70+
return "COMMIT"
71+
}
72+
6773
// Rule represents a rule in an iptables dump. Normally the start with -A.
6874
// The parser treats the -A flag like any other flag, thus does not require
6975
// the -A flag as the leading flag.
@@ -373,6 +379,8 @@ func (p *Parser) Parse() (l Line, err error) {
373379
return p.parseRule()
374380
case COLON:
375381
return p.parseDefault(p.s.scanLine())
382+
case COMMIT:
383+
return Commit{}, nil
376384
case EOF:
377385
return nil, io.EOF // ErrEOF
378386
case NEWLINE:
@@ -412,7 +420,7 @@ func init() {
412420
}
413421

414422
var (
415-
regDefault *regexp.Regexp = regexp.MustCompile(`^\s*(\S+)\s+(\S+)\s+(\[\d*\:\d*\])\s*$`)
423+
regDefault *regexp.Regexp = regexp.MustCompile(`^\s*(\S+)\s+(\S+)(?:\s+(\[\d*\:\d*\]))?\s*$`)
416424
regCounter *regexp.Regexp = regexp.MustCompile(`^\[(\d*)\:(\d*)\]$`)
417425
)
418426

@@ -422,12 +430,17 @@ func (p *Parser) parseDefault(lit string) (Line, error) {
422430
a := regDefault.ReplaceAll([]byte(lit), []byte("$2"))
423431
r.Action = string(a)
424432
cs := regDefault.ReplaceAll([]byte(lit), []byte("$3"))
425-
c, err := parseCounter(cs)
426-
if err != nil {
427-
return nil, err
433+
if string(cs) == "" {
434+
// nothing has changed
435+
// iptables-restore allows the counter to not exist
436+
r.Counter = &Counter{}
437+
} else {
438+
c, err := parseCounter(cs)
439+
if err != nil {
440+
return nil, err
441+
}
442+
r.Counter = &c
428443
}
429-
430-
r.Counter = &c
431444
return r, nil
432445
}
433446

parser_test.go

+57-2
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,19 @@ func TestParser_Parse(t *testing.T) {
347347
},
348348
err: nil,
349349
},
350+
{
351+
name: "parse default rule without counter",
352+
s: ":hello-chain DROP",
353+
r: Policy{
354+
Chain: "hello-chain",
355+
Action: "DROP",
356+
Counter: &Counter{
357+
packets: 0,
358+
bytes: 0,
359+
},
360+
},
361+
err: nil,
362+
},
350363
{
351364
name: "parse policy",
352365
s: "-P hello-chain DROP",
@@ -592,6 +605,46 @@ func TestParser_Parse(t *testing.T) {
592605
},
593606
err: nil,
594607
},
608+
{
609+
name: "parse rule with icmp type",
610+
s: "-A foo -p icmp -m icmp --icmp-type 11",
611+
r: Rule{
612+
Chain: "foo",
613+
Protocol: &StringPair{
614+
Not: false,
615+
Value: "icmp",
616+
},
617+
Matches: []Match{
618+
{
619+
Type: "icmp",
620+
Flags: map[string]Flag{
621+
"icmp-type": {Values: []string{"11"}},
622+
},
623+
},
624+
},
625+
},
626+
err: nil,
627+
},
628+
{
629+
name: "parse rule with icmp type",
630+
s: "-A foo -p ipv6-icmp -m icmp6 --icmpv6-type 11",
631+
r: Rule{
632+
Chain: "foo",
633+
Protocol: &StringPair{
634+
Not: false,
635+
Value: "ipv6-icmp",
636+
},
637+
Matches: []Match{
638+
{
639+
Type: "icmp6",
640+
Flags: map[string]Flag{
641+
"icmpv6-type": {Values: []string{"11"}},
642+
},
643+
},
644+
},
645+
},
646+
err: nil,
647+
},
595648
{
596649
name: "parse rule with match expression tcp and a lot of flags and overwriting",
597650
s: "-A foo -m tcp --tcp-flags SYN,FIN ACK --sport 1010 ! --dport=1000:1010 --syn! --syn ! --tcp-option 1 ! -f ",
@@ -1211,7 +1264,7 @@ func TestParser_ParseMore(t *testing.T) {
12111264
},
12121265
},
12131266
{
1214-
name: "Parse some rules from iptables -S",
1267+
name: "Parse some rules from iptables -S as well as iptables-save",
12151268
s: `-P INPUT ACCEPT
12161269
-P FORWARD DROP
12171270
-P OUTPUT ACCEPT
@@ -1220,7 +1273,8 @@ func TestParser_ParseMore(t *testing.T) {
12201273
-N DOCKER-ISOLATION-STAGE-2
12211274
-N DOCKER-USER
12221275
-A FORWARD -j DOCKER-USER
1223-
-A FORWARD -j DOCKER-ISOLATION-STAGE-1`,
1276+
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
1277+
COMMIT`,
12241278
r: []interface{}{
12251279
Policy{
12261280
UserDefined: &_false,
@@ -1265,6 +1319,7 @@ func TestParser_ParseMore(t *testing.T) {
12651319
Name: "DOCKER-ISOLATION-STAGE-1",
12661320
},
12671321
},
1322+
Commit{},
12681323
},
12691324
},
12701325
} {

scanner.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ func (s *scanner) scan() (tok Token, lit string) {
2727
return s.scanWhitespace()
2828
case isLetter(ch) || isDigit(ch):
2929
s.unread()
30-
return s.scanIdent()
30+
tok, lit := s.scanIdent()
31+
if lit == "COMMIT" {
32+
return COMMIT, "COMMIT"
33+
}
34+
return tok, lit
3135
}
3236

3337
// Otherwise read the individual character.
@@ -192,4 +196,4 @@ func isLetter(ch rune) bool { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && c
192196
// isDigit returns true if the rune is a digit.
193197
func isDigit(ch rune) bool { return (ch >= '0' && ch <= '9') }
194198

195-
func isMisc(ch rune) bool { return (ch == '.' || ch == '/' || ch == '-' || ch == ':') }
199+
func isMisc(ch rune) bool { return (ch == '.' || ch == '/' || ch == '-' || ch == '_' || ch == ':') }

scanner_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestScanner_Scan(t *testing.T) {
2929
{s: "192.168.178.2/24", tok: IDENT, lit: "192.168.178.2/24"},
3030
{s: "2001:db8::/64", tok: IDENT, lit: "2001:db8::/64"},
3131
{s: "# 192.168.178.2/24", tok: COMMENTLINE, lit: " 192.168.178.2/24"},
32+
{s: "I_test_rule-something", tok: IDENT, lit: "I_test_rule-something"},
3233
} {
3334
s := newScanner(strings.NewReader(tc.s))
3435
tok, lit := s.scan()

token.go

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const (
1010

1111
// Literals
1212
IDENT // main
13+
COMMIT
1314

1415
// Misc characters
1516
COLON // :

0 commit comments

Comments
 (0)