Skip to content

Commit

Permalink
Merge branch 'main' into fix-ipv6-ident
Browse files Browse the repository at this point in the history
  • Loading branch information
leonnicolas committed Mar 31, 2024
2 parents 3bb8ea9 + 7eaddee commit f0f502e
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 10 deletions.
106 changes: 106 additions & 0 deletions match_modules.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func (p *Parser) parseMatch(ms *[]Match) (state, error) {
s, err = p.parseStatistic(&m.Flags)
case "multiport":
s, err = p.parseMultiport(&m.Flags)
case "icmp":
s, err = p.parseIcmp(&m.Flags)
case "icmp6":
s, err = p.parseIcmp6(&m.Flags)
default:
if _, ok := matchModules[lit]; ok {
return sError, fmt.Errorf("match modules %q is not implemented", lit)
Expand Down Expand Up @@ -529,3 +533,105 @@ func (p *Parser) parseComment(f *map[string]Flag) (state, error) {
}
return sStart, nil
}

func (p *Parser) parseIcmp(f *map[string]Flag) (state, error) {
s := sStart
for tok, lit := p.scanIgnoreWhitespace(); tok != EOF; tok, lit = p.scanIgnoreWhitespace() {
for nextValue := false; !nextValue; {
nextValue = true
switch s {
case sStart:
switch tok {
case NOT:
s = sINotF
case FLAG:
s = sIF
nextValue = false
default:
return sError, fmt.Errorf("unexpected token %q, expected flag, or \"!\"", lit)
}
case sINotF:
switch {
case lit == "--icmp-type":
_, lit := p.scanIgnoreWhitespace()
(*f)["icmp-type"] = Flag{
Not: true,
Values: []string{lit},
}
s = sStart
default:
p.unscan(1)
return sNot, nil
}
case sIF:
switch {
case lit == "--icmp-type":
_, lit := p.scanIgnoreWhitespace()
(*f)["icmp-type"] = Flag{
Values: []string{lit},
}
s = sStart
default:
// The end of the match statement is reached.
p.unscan(1)
return sStart, nil
}

default:
return sStart, errors.New("unexpected error parsing match extension")
}
}
}
return sStart, nil
}

func (p *Parser) parseIcmp6(f *map[string]Flag) (state, error) {
s := sStart
for tok, lit := p.scanIgnoreWhitespace(); tok != EOF; tok, lit = p.scanIgnoreWhitespace() {
for nextValue := false; !nextValue; {
nextValue = true
switch s {
case sStart:
switch tok {
case NOT:
s = sINotF
case FLAG:
s = sIF
nextValue = false
default:
return sError, fmt.Errorf("unexpected token %q, expected flag, or \"!\"", lit)
}
case sINotF:
switch {
case lit == "--icmpv6-type":
_, lit := p.scanIgnoreWhitespace()
(*f)["icmpv6-type"] = Flag{
Not: true,
Values: []string{lit},
}
s = sStart
default:
p.unscan(1)
return sNot, nil
}
case sIF:
switch {
case lit == "--icmpv6-type":
_, lit := p.scanIgnoreWhitespace()
(*f)["icmpv6-type"] = Flag{
Values: []string{lit},
}
s = sStart
default:
// The end of the match statement is reached.
p.unscan(1)
return sStart, nil
}

default:
return sStart, errors.New("unexpected error parsing match extension")
}
}
}
return sStart, nil
}
25 changes: 19 additions & 6 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ func (d Policy) String() string {
return fmt.Sprintf("%s%s %s", prefix, d.Chain, d.Action)
}

type Commit struct{}

func (c Commit) String() string {
return "COMMIT"
}

// Rule represents a rule in an iptables dump. Normally the start with -A.
// The parser treats the -A flag like any other flag, thus does not require
// the -A flag as the leading flag.
Expand Down Expand Up @@ -373,6 +379,8 @@ func (p *Parser) Parse() (l Line, err error) {
return p.parseRule()
case COLON:
return p.parseDefault(p.s.scanLine())
case COMMIT:
return Commit{}, nil
case EOF:
return nil, io.EOF // ErrEOF
case NEWLINE:
Expand Down Expand Up @@ -412,7 +420,7 @@ func init() {
}

var (
regDefault *regexp.Regexp = regexp.MustCompile(`^\s*(\S+)\s+(\S+)\s+(\[\d*\:\d*\])\s*$`)
regDefault *regexp.Regexp = regexp.MustCompile(`^\s*(\S+)\s+(\S+)(?:\s+(\[\d*\:\d*\]))?\s*$`)
regCounter *regexp.Regexp = regexp.MustCompile(`^\[(\d*)\:(\d*)\]$`)
)

Expand All @@ -422,12 +430,17 @@ func (p *Parser) parseDefault(lit string) (Line, error) {
a := regDefault.ReplaceAll([]byte(lit), []byte("$2"))
r.Action = string(a)
cs := regDefault.ReplaceAll([]byte(lit), []byte("$3"))
c, err := parseCounter(cs)
if err != nil {
return nil, err
if string(cs) == "" {
// nothing has changed
// iptables-restore allows the counter to not exist
r.Counter = &Counter{}
} else {
c, err := parseCounter(cs)
if err != nil {
return nil, err
}
r.Counter = &c
}

r.Counter = &c
return r, nil
}

Expand Down
59 changes: 57 additions & 2 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,19 @@ func TestParser_Parse(t *testing.T) {
},
err: nil,
},
{
name: "parse default rule without counter",
s: ":hello-chain DROP",
r: Policy{
Chain: "hello-chain",
Action: "DROP",
Counter: &Counter{
packets: 0,
bytes: 0,
},
},
err: nil,
},
{
name: "parse policy",
s: "-P hello-chain DROP",
Expand Down Expand Up @@ -592,6 +605,46 @@ func TestParser_Parse(t *testing.T) {
},
err: nil,
},
{
name: "parse rule with icmp type",
s: "-A foo -p icmp -m icmp --icmp-type 11",
r: Rule{
Chain: "foo",
Protocol: &StringPair{
Not: false,
Value: "icmp",
},
Matches: []Match{
{
Type: "icmp",
Flags: map[string]Flag{
"icmp-type": {Values: []string{"11"}},
},
},
},
},
err: nil,
},
{
name: "parse rule with icmp type",
s: "-A foo -p ipv6-icmp -m icmp6 --icmpv6-type 11",
r: Rule{
Chain: "foo",
Protocol: &StringPair{
Not: false,
Value: "ipv6-icmp",
},
Matches: []Match{
{
Type: "icmp6",
Flags: map[string]Flag{
"icmpv6-type": {Values: []string{"11"}},
},
},
},
},
err: nil,
},
{
name: "parse rule with match expression tcp and a lot of flags and overwriting",
s: "-A foo -m tcp --tcp-flags SYN,FIN ACK --sport 1010 ! --dport=1000:1010 --syn! --syn ! --tcp-option 1 ! -f ",
Expand Down Expand Up @@ -1211,7 +1264,7 @@ func TestParser_ParseMore(t *testing.T) {
},
},
{
name: "Parse some rules from iptables -S",
name: "Parse some rules from iptables -S as well as iptables-save",
s: `-P INPUT ACCEPT
-P FORWARD DROP
-P OUTPUT ACCEPT
Expand All @@ -1220,7 +1273,8 @@ func TestParser_ParseMore(t *testing.T) {
-N DOCKER-ISOLATION-STAGE-2
-N DOCKER-USER
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1`,
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
COMMIT`,
r: []interface{}{
Policy{
UserDefined: &_false,
Expand Down Expand Up @@ -1265,6 +1319,7 @@ func TestParser_ParseMore(t *testing.T) {
Name: "DOCKER-ISOLATION-STAGE-1",
},
},
Commit{},
},
},
} {
Expand Down
8 changes: 6 additions & 2 deletions scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ func (s *scanner) scan() (tok Token, lit string) {
return s.scanWhitespace()
case isLetter(ch) || isDigit(ch):
s.unread()
return s.scanIdent()
tok, lit := s.scanIdent()
if lit == "COMMIT" {
return COMMIT, "COMMIT"
}
return tok, lit
}

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

func isMisc(ch rune) bool { return (ch == '.' || ch == '/' || ch == '-' || ch == ':') }
func isMisc(ch rune) bool { return (ch == '.' || ch == '/' || ch == '-' || ch == '_' || ch == ':') }
1 change: 1 addition & 0 deletions scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func TestScanner_Scan(t *testing.T) {
{s: "192.168.178.2/24", tok: IDENT, lit: "192.168.178.2/24"},
{s: "2001:db8::/64", tok: IDENT, lit: "2001:db8::/64"},
{s: "# 192.168.178.2/24", tok: COMMENTLINE, lit: " 192.168.178.2/24"},
{s: "I_test_rule-something", tok: IDENT, lit: "I_test_rule-something"},
} {
s := newScanner(strings.NewReader(tc.s))
tok, lit := s.scan()
Expand Down
1 change: 1 addition & 0 deletions token.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const (

// Literals
IDENT // main
COMMIT

// Misc characters
COLON // :
Expand Down

0 comments on commit f0f502e

Please sign in to comment.