From e08704ce9e0868f4c1f089415b18dd37f4a0208c Mon Sep 17 00:00:00 2001 From: Adphi Date: Wed, 18 May 2022 13:16:39 +0200 Subject: [PATCH] modules: add basic multiport support Signed-off-by: Adphi --- match_modules.go | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ parser_test.go | 31 +++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/match_modules.go b/match_modules.go index 07182c7..4a0394e 100644 --- a/match_modules.go +++ b/match_modules.go @@ -28,6 +28,8 @@ func (p *Parser) parseMatch(ms *[]Match) (state, error) { s, err = p.parseUdp(&m.Flags) case "statistic": s, err = p.parseStatistic(&m.Flags) + case "multiport": + s, err = p.parseMultiport(&m.Flags) default: if _, ok := matchModules[lit]; ok { return sError, fmt.Errorf("match modules %q is not implemented", lit) @@ -385,6 +387,78 @@ func (p *Parser) parseTcp(f *map[string]Flag) (state, error) { return sStart, nil } +func (p *Parser) parseMultiport(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 == "--dports" || lit == "--destination-ports": + (*f)["destination-ports"] = Flag{ + Not: true, + Values: p.parsePorts(), + } + s = sStart + case lit == "--sports" || lit == "--source-ports": + (*f)["source-ports"] = Flag{ + Not: true, + Values: p.parsePorts(), + } + s = sStart + case lit == "--ports": + (*f)["ports"] = Flag{ + Not: true, + Values: p.parsePorts(), + } + s = sStart + default: + p.unscan(1) + return sNot, nil + } + case sIF: + switch { + case lit == "--dports" || lit == "--destination-ports": + (*f)["destination-ports"] = Flag{ + Values: p.parsePorts(), + } + s = sStart + case lit == "--sports" || lit == "--source-ports": + (*f)["source-ports"] = Flag{ + Values: p.parsePorts(), + } + s = sStart + case lit == "--ports": + (*f)["ports"] = Flag{ + Not: true, + Values: p.parsePorts(), + } + 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) parsePort() string { _, l := p.scanIgnoreWhitespace() if t, _ := p.scanIgnoreWhitespace(); t == COLON { @@ -396,6 +470,22 @@ func (p *Parser) parsePort() string { return l } +func (p *Parser) parsePorts() []string { + var ports []string + _, l := p.scanIgnoreWhitespace() + ports = append(ports, l) + for { + if t, _ := p.scanIgnoreWhitespace(); t == COMMA { + _, c := p.scan() + ports = append(ports, c) + } else { + p.unscan(1) + break + } + } + return ports +} + func (p *Parser) parseList() (strs []string) { const ( sC state = iota*2 + 1 diff --git a/parser_test.go b/parser_test.go index 44b54e5..6973c3b 100644 --- a/parser_test.go +++ b/parser_test.go @@ -707,6 +707,37 @@ func TestParser_Parse(t *testing.T) { }, err: nil, }, + { + name: "parse rule with multiport jump target DNAT", + s: "-A foo -p tcp -m multiport --dports=25,143,465,587,993,4190 -4 -j DNAT --to-destination 192.168.1.1", + r: Rule{ + Chain: "foo", + IPv4: true, + Protocol: &StringPair{ + Not: false, + Value: "tcp", + }, + Matches: []Match{ + { + Type: "multiport", + Flags: map[string]Flag{ + "destination-ports": { + Values: []string{"25", "143", "465", "587", "993", "4190"}, + }, + }, + }, + }, + Jump: &Target{ + Name: "DNAT", + Flags: map[string]Flag{ + "to-destination": { + Values: []string{"192.168.1.1"}, + }, + }, + }, + }, + err: nil, + }, { name: "parse rule with jump target SNAT", s: "-A foo -o eth0 -4 -j SNAT --to-source 192.168.1.1",