Resolve common IANA protocol names and reject ports on portless protocols
This commit is contained in:
@@ -972,7 +972,19 @@ func l4Matches(proto string, dports, sports config.PortSpec) ([]l4Match, error)
|
||||
if proto != "" && p == "" {
|
||||
return nil, fmt.Errorf("empty element in proto list %q", proto)
|
||||
}
|
||||
isICMP := strings.EqualFold(p, "icmp") || strings.EqualFold(p, "icmpv6") || strings.EqualFold(p, "ipv6-icmp")
|
||||
var pm []expr.Any
|
||||
isICMP := false
|
||||
if p != "" {
|
||||
n, err := protoNumber(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
isICMP = n == unix.IPPROTO_ICMP || n == unix.IPPROTO_ICMPV6
|
||||
if (len(dports) > 0 && !isICMP || len(sports) > 0) && !hasPorts(n) {
|
||||
return nil, fmt.Errorf("protocol %q does not support ports", p)
|
||||
}
|
||||
pm = matchProtoNum(n)
|
||||
}
|
||||
parseD := parsePortOrRange
|
||||
if isICMP {
|
||||
if len(protos) > 1 && len(dports) > 0 {
|
||||
@@ -980,14 +992,6 @@ func l4Matches(proto string, dports, sports config.PortSpec) ([]l4Match, error)
|
||||
}
|
||||
parseD = matchICMPType
|
||||
}
|
||||
var pm []expr.Any
|
||||
if p != "" {
|
||||
m, err := matchProto(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pm = m
|
||||
}
|
||||
dalts, err := portAlternatives(dports, parseD)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1051,36 +1055,27 @@ func matchIfaceName(input bool, name string) []expr.Any {
|
||||
}
|
||||
}
|
||||
|
||||
func matchProto(proto string) ([]expr.Any, error) {
|
||||
var protoNum byte
|
||||
switch strings.ToLower(proto) {
|
||||
case "tcp":
|
||||
protoNum = unix.IPPROTO_TCP
|
||||
case "udp":
|
||||
protoNum = unix.IPPROTO_UDP
|
||||
case "icmp":
|
||||
protoNum = unix.IPPROTO_ICMP
|
||||
case "icmpv6", "ipv6-icmp":
|
||||
protoNum = unix.IPPROTO_ICMPV6
|
||||
case "gre":
|
||||
protoNum = 47
|
||||
case "esp":
|
||||
protoNum = 50
|
||||
case "ah":
|
||||
protoNum = 51
|
||||
case "sctp":
|
||||
protoNum = unix.IPPROTO_SCTP
|
||||
default:
|
||||
n, err := strconv.ParseUint(proto, 10, 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unknown protocol %q", proto)
|
||||
}
|
||||
protoNum = byte(n)
|
||||
var protoNumbers = map[string]byte{
|
||||
"icmp": 1, "igmp": 2, "ipip": 4, "ipencap": 4, "tcp": 6, "udp": 17,
|
||||
"gre": 47, "esp": 50, "ah": 51, "icmpv6": 58, "ipv6-icmp": 58,
|
||||
"ospf": 89, "ospfigp": 89, "pim": 103, "vrrp": 112, "l2tp": 115,
|
||||
"sctp": 132, "udplite": 136,
|
||||
}
|
||||
|
||||
// protoNumber resolves a common IANA protocol name or a 0-255 number.
|
||||
func protoNumber(proto string) (byte, error) {
|
||||
if n, ok := protoNumbers[strings.ToLower(proto)]; ok {
|
||||
return n, nil
|
||||
}
|
||||
return []expr.Any{
|
||||
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
|
||||
&expr.Cmp{Op: expr.CmpOpEq, Register: 1, Data: []byte{protoNum}},
|
||||
}, nil
|
||||
n, err := strconv.ParseUint(proto, 10, 8)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unknown protocol %q", proto)
|
||||
}
|
||||
return byte(n), nil
|
||||
}
|
||||
|
||||
func hasPorts(proto byte) bool {
|
||||
return proto == unix.IPPROTO_TCP || proto == unix.IPPROTO_UDP || proto == unix.IPPROTO_SCTP || proto == unix.IPPROTO_UDPLITE
|
||||
}
|
||||
|
||||
func matchDPort(port uint16) []expr.Any {
|
||||
|
||||
@@ -1581,6 +1581,24 @@ func TestCompile_PortAndProtoLists(t *testing.T) {
|
||||
chain: "prerouting",
|
||||
want: []want{{6, "53"}, {17, "53"}},
|
||||
},
|
||||
{
|
||||
name: "protocol names",
|
||||
rule: config.Rule{Action: config.RuleAccept, Source: "net", Dest: "fw", Proto: "ospf,OSPFIGP,igmp,gre,esp,ah,vrrp,pim,ipencap,ipv6-icmp"},
|
||||
chain: "input",
|
||||
want: []want{{89, ""}, {89, ""}, {2, ""}, {47, ""}, {50, ""}, {51, ""}, {112, ""}, {103, ""}, {4, ""}, {58, ""}},
|
||||
},
|
||||
{
|
||||
name: "protocol numbers",
|
||||
rule: config.Rule{Action: config.RuleAccept, Source: "net", Dest: "fw", Proto: "0,89,255"},
|
||||
chain: "input",
|
||||
want: []want{{0, ""}, {89, ""}, {255, ""}},
|
||||
},
|
||||
{
|
||||
name: "numeric tcp with port",
|
||||
rule: config.Rule{Action: config.RuleAccept, Source: "net", Dest: "fw", Proto: "6,udplite", DPort: config.PortSpec{"22"}},
|
||||
chain: "input",
|
||||
want: []want{{6, "22"}, {136, "22"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -1724,6 +1742,9 @@ func TestCompile_ListErrors(t *testing.T) {
|
||||
{"icmp code out of range", config.Rule{Proto: "icmp", DPort: config.PortSpec{"3/256"}}},
|
||||
{"unknown proto in list", config.Rule{Proto: "tcp,udpp", DPort: config.PortSpec{"53"}}},
|
||||
{"proto number out of range", config.Rule{Proto: "256"}},
|
||||
{"unknown proto name", config.Rule{Proto: "bogus"}},
|
||||
{"dport with ospf", config.Rule{Proto: "ospf", DPort: config.PortSpec{"80"}}},
|
||||
{"sport with gre", config.Rule{Proto: "gre", SPort: config.PortSpec{"80"}}},
|
||||
{"dport with icmp in proto list", config.Rule{Proto: "icmp,tcp", DPort: config.PortSpec{"80"}}},
|
||||
{"ratelimit with port list", config.Rule{Proto: "tcp", DPort: config.PortSpec{"80,443"}, RateLimit: "10/sec"}},
|
||||
{"connlimit with proto list", config.Rule{Proto: "tcp,udp", DPort: config.PortSpec{"53"}, ConnLimit: "10"}},
|
||||
|
||||
Reference in New Issue
Block a user