-
Notifications
You must be signed in to change notification settings - Fork 104
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Network: firewall controller nat rules
- Loading branch information
Showing
18 changed files
with
910 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright 2019-2023 The Liqo Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package firewall | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
) | ||
|
||
// IPValueType is the type of the match value. | ||
type IPValueType string | ||
|
||
const ( | ||
// IPValueTypeIP is a string representing an ip. | ||
IPValueTypeIP IPValueType = "ip" | ||
// IPValueTypeSubnet is a string representing a subnet (eg. 10.0.0.0/24). | ||
IPValueTypeSubnet IPValueType = "subnet" | ||
// IPValueTypeInvalid is an invalid match value. | ||
IPValueTypeInvalid IPValueType = "invalid" | ||
) | ||
|
||
// GetIPValueType parses the match value and returns the type of the value. | ||
func GetIPValueType(value string) (IPValueType, error) { | ||
if value == "" { | ||
return IPValueTypeInvalid, fmt.Errorf("match value cannot be empty") | ||
} | ||
|
||
// Check if the value is a pool subnet. | ||
if _, _, err := net.ParseCIDR(value); err == nil { | ||
return IPValueTypeSubnet, nil | ||
} | ||
|
||
// Check if the value is an IP. | ||
if net.ParseIP(value) != nil { | ||
return IPValueTypeIP, nil | ||
} | ||
|
||
return IPValueTypeInvalid, fmt.Errorf("invalid match value %s", value) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright 2019-2023 The Liqo Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package firewall | ||
|
||
var _ Rule = &FilterRule{} | ||
|
||
// FilterRule is a rule to be applied to a filter chain. | ||
// +kubebuilder:object:generate=true | ||
type FilterRule struct { | ||
// Name is the name of the rule. | ||
Name *string `json:"name,omitempty"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright 2019-2023 The Liqo Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package firewall | ||
|
||
import "github.com/google/nftables" | ||
|
||
// GetName returns the name of the rule. | ||
func (fr *FilterRule) GetName() *string { | ||
return fr.Name | ||
} | ||
|
||
// SetName sets the name of the rule. | ||
func (fr *FilterRule) SetName(name string) { | ||
fr.Name = &name | ||
} | ||
|
||
// Add adds the rule to the chain. | ||
func (fr *FilterRule) Add(_ *nftables.Conn, _ *nftables.Chain) error { | ||
// TODO: implement | ||
return nil | ||
} | ||
|
||
// Equal checks if the rule is equal to the given one. | ||
func (fr *FilterRule) Equal(_ *nftables.Rule) bool { | ||
// TODO: implement | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// Copyright 2019-2023 The Liqo Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package firewall | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
|
||
"github.com/google/nftables" | ||
"github.com/google/nftables/expr" | ||
) | ||
|
||
func applyMatch(m *Match, rule *nftables.Rule) error { | ||
op, err := getMatchCmpOp(m) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch { | ||
case m.IP != nil: | ||
return applyMatchIP(m, rule, op) | ||
case m.Dev != nil: | ||
return applyMatchDev(m, rule, op) | ||
} | ||
return nil | ||
} | ||
|
||
func applyMatchIP(m *Match, rule *nftables.Rule, op expr.CmpOp) error { | ||
matchIPValueType, err := GetIPValueType(m.IP.Value) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch matchIPValueType { | ||
case IPValueTypeIP: | ||
return applyMatchIPSingleIP(m, rule, op) | ||
case IPValueTypeSubnet: | ||
return applyMatchIPPoolSubnet(m, rule, op) | ||
default: | ||
return fmt.Errorf("invalid match value type %s", matchIPValueType) | ||
} | ||
} | ||
|
||
func applyMatchIPSingleIP(m *Match, rule *nftables.Rule, op expr.CmpOp) error { | ||
posOffset, err := getMatchIPPositionOffset(m) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
rule.Exprs = append(rule.Exprs, | ||
&expr.Payload{ | ||
DestRegister: 1, | ||
Base: expr.PayloadBaseNetworkHeader, | ||
Offset: posOffset, | ||
Len: 4, | ||
}, | ||
&expr.Cmp{ | ||
Op: op, | ||
Register: 1, | ||
Data: net.ParseIP(m.IP.Value).To4(), | ||
}, | ||
) | ||
return nil | ||
} | ||
|
||
func applyMatchIPPoolSubnet(m *Match, rule *nftables.Rule, op expr.CmpOp) error { | ||
posOffset, err := getMatchIPPositionOffset(m) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ip, subnet, err := net.ParseCIDR(m.IP.Value) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
rule.Exprs = append(rule.Exprs, | ||
&expr.Payload{ | ||
DestRegister: 1, | ||
Base: expr.PayloadBaseNetworkHeader, | ||
Offset: posOffset, | ||
Len: 4, | ||
}, | ||
&expr.Bitwise{ | ||
SourceRegister: 1, | ||
DestRegister: 1, | ||
Len: 4, | ||
Xor: []byte{0x0, 0x0, 0x0, 0x0}, | ||
Mask: subnet.Mask, | ||
}, | ||
&expr.Cmp{ | ||
Op: op, | ||
Register: 1, | ||
Data: ip.To4(), | ||
}, | ||
) | ||
return nil | ||
} | ||
|
||
func applyMatchDev(m *Match, rule *nftables.Rule, op expr.CmpOp) error { | ||
metakey, err := getMatchDevMetaKey(m) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
rule.Exprs = append(rule.Exprs, | ||
&expr.Meta{ | ||
Register: 1, | ||
Key: metakey, | ||
}, | ||
&expr.Cmp{ | ||
Op: op, | ||
Register: 1, | ||
Data: ifname(m.Dev.Value), | ||
}, | ||
) | ||
|
||
return nil | ||
} | ||
|
||
func getMatchCmpOp(m *Match) (expr.CmpOp, error) { | ||
switch m.Op { | ||
case MatchOperationEq: | ||
return expr.CmpOpEq, nil | ||
case MatchOperationNeq: | ||
return expr.CmpOpNeq, nil | ||
} | ||
return expr.CmpOp(0), fmt.Errorf("invalid match operation %s", m.Op) | ||
} | ||
|
||
func getMatchIPPositionOffset(m *Match) (uint32, error) { | ||
switch m.IP.Position { | ||
case MatchIPPositionSrc: | ||
return 12, nil | ||
case MatchIPPositionDst: | ||
return 16, nil | ||
} | ||
return 0, fmt.Errorf("invalid match IP position %s", m.Dev.Position) | ||
} | ||
|
||
func getMatchDevMetaKey(m *Match) (expr.MetaKey, error) { | ||
switch m.Dev.Position { | ||
case MatchDevPositionIn: | ||
return expr.MetaKeyIIFNAME, nil | ||
case MatchDevPositionOut: | ||
return expr.MetaKeyOIFNAME, nil | ||
} | ||
return 0, fmt.Errorf("invalid match IP position %s", m.Dev.Position) | ||
} | ||
|
||
func ifname(n string) []byte { | ||
b := make([]byte, 16) | ||
copy(b, n+"\x00") | ||
return b | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright 2019-2023 The Liqo Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package firewall | ||
|
||
// MatchOperation is the operation of the match. | ||
type MatchOperation string | ||
|
||
const ( | ||
// MatchOperationEq is the operation of the match. | ||
MatchOperationEq MatchOperation = "eq" | ||
// MatchOperationNeq is the operation of the match. | ||
MatchOperationNeq MatchOperation = "neq" | ||
) | ||
|
||
// MatchIPPosition is the position of the IP in the packet. | ||
type MatchIPPosition string | ||
|
||
const ( | ||
// MatchIPPositionSrc is the position of the IP in the packet. | ||
MatchIPPositionSrc MatchIPPosition = "src" | ||
// MatchIPPositionDst is the position of the IP in the packet. | ||
MatchIPPositionDst MatchIPPosition = "dst" | ||
) | ||
|
||
// MatchDevPosition is the position of the device in the packet. | ||
type MatchDevPosition string | ||
|
||
const ( | ||
// MatchDevPositionIn is the position of the device in the packet. | ||
MatchDevPositionIn MatchDevPosition = "in" | ||
// MatchDevPositionOut is the position of the device in the packet. | ||
MatchDevPositionOut MatchDevPosition = "out" | ||
) | ||
|
||
// MatchIP is an IP to be matched. | ||
// +kubebuilder:object:generate=true | ||
type MatchIP struct { | ||
// Value is the IP or a SUbnet to be matched. | ||
Value string `json:"value"` | ||
// Position is the position of the IP in the packet. | ||
// +kubebuilder:validation:Enum=src;dst | ||
Position MatchIPPosition `json:"position"` | ||
} | ||
|
||
// MatchDev is a device to be matched. | ||
// +kubebuilder:object:generate=true | ||
type MatchDev struct { | ||
// Value is the name of the device to be matched. | ||
Value string `json:"value"` | ||
// Position is the source device of the packet. | ||
// +kubebuilder:validation:Enum=in;out | ||
Position MatchDevPosition `json:"position"` | ||
} | ||
|
||
// Match is a match to be applied to a rule. | ||
// +kubebuilder:object:generate=true | ||
// +kubebuilder:validation:MaxProperties=2 | ||
// +kubebuilder:validation:MinProperties=2 | ||
type Match struct { | ||
// Op is the operation of the match. | ||
// +kubebuilder:validation:Enum=eq;neq | ||
Op MatchOperation `json:"op"` | ||
// IP contains the options to match an IP or a Subnet. | ||
IP *MatchIP `json:"ip,omitempty"` | ||
// Dev contains the options to match a device. | ||
Dev *MatchDev `json:"dev,omitempty"` | ||
} |
Oops, something went wrong.