Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

291 atomic ip block #306

Merged
merged 52 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
91b1c1a
code for implementing atomic for ipBlockTerm
ShiriMoran Mar 3, 2025
0e93761
Merge branch 'main' into 291_atomic_ip_block
ShiriMoran Mar 3, 2025
e8ad291
revert committed by mistake
ShiriMoran Mar 3, 2025
fd5f72d
update Conjunction functions to handle IPBlocks (not checked yet(
ShiriMoran Mar 3, 2025
660deb7
IsTautology no longer false for all impl over atomicTerm structs
ShiriMoran Mar 4, 2025
dfc7f69
update func (c *Conjunction) isTautology() bool
ShiriMoran Mar 4, 2025
54b17e4
minor code improvements
ShiriMoran Mar 4, 2025
16c2646
improvred comment
ShiriMoran Mar 4, 2025
d293e67
merge with main leftovers
ShiriMoran Mar 4, 2025
8743041
separate unit tests to two files
ShiriMoran Mar 4, 2025
d4d52cb
tests IpBlockTerm String()
ShiriMoran Mar 4, 2025
41ec110
check negation (1)
ShiriMoran Mar 4, 2025
e655f71
added a test and fixed bugs it revealed
ShiriMoran Mar 4, 2025
6d73254
simplified code
ShiriMoran Mar 4, 2025
26de2a3
isNegateOf check
ShiriMoran Mar 4, 2025
c303e31
isNegateOf negative check
ShiriMoran Mar 4, 2025
eed6546
fix bug
ShiriMoran Mar 4, 2025
a137743
IsTautology() tests added
ShiriMoran Mar 4, 2025
41078d8
disjoint tests added
ShiriMoran Mar 4, 2025
976eedb
supersetOf tests added
ShiriMoran Mar 4, 2025
28ac23a
added tests for func (c *Conjunction) add(atom atomic) *Conjunction;
ShiriMoran Mar 4, 2025
df97678
test added
ShiriMoran Mar 4, 2025
f14a67a
test added
ShiriMoran Mar 4, 2025
838b301
test added
ShiriMoran Mar 4, 2025
3e25ebe
test added
ShiriMoran Mar 4, 2025
6f90fce
extended isEmptySet
ShiriMoran Mar 4, 2025
ffcc75a
extended disjoint; made isEmptySet more efficient
ShiriMoran Mar 4, 2025
5f5f2d3
added isEmptySet tests
ShiriMoran Mar 4, 2025
3c3901f
minor syntactic refactor
ShiriMoran Mar 5, 2025
2f86483
tests isTautology()
ShiriMoran Mar 5, 2025
13a76e9
Conjunction isEmptySet is actually isFalse; does not hold for an empt…
ShiriMoran Mar 5, 2025
f5eeb40
tests contains
ShiriMoran Mar 5, 2025
66d9c8f
added explanation to tests
ShiriMoran Mar 5, 2025
f3e3191
added explanation to tests
ShiriMoran Mar 5, 2025
9f5d181
1. 0.0.0.0/0 is implied also by Conj with no ipblockTerm. Fixed code …
ShiriMoran Mar 5, 2025
cd274a8
added IsContradiction() to atomic interface
ShiriMoran Mar 5, 2025
4dddb38
simplified "(c *Conjunction) contains(atom atomic) bool" so that it d…
ShiriMoran Mar 5, 2025
a0c1fc1
simplified "func (ipBlockTerm *ipBlockAtomicTerm) supersetOf(otherAto…
ShiriMoran Mar 5, 2025
0718d22
clarified comment
ShiriMoran Mar 5, 2025
4aa2067
Merge branch 'main' into 291_atomic_ip_block
ShiriMoran Mar 5, 2025
ea6b40b
merge with main
ShiriMoran Mar 5, 2025
d06a12d
lint
ShiriMoran Mar 5, 2025
d269354
lint
ShiriMoran Mar 5, 2025
b6f19a9
CR
ShiriMoran Mar 5, 2025
d6f6a2d
CR
ShiriMoran Mar 5, 2025
e124609
CR
ShiriMoran Mar 5, 2025
6dad6ba
CR: add getBlock() to atomic interface
ShiriMoran Mar 5, 2025
bbf17d2
lint
ShiriMoran Mar 5, 2025
3b281a8
my fix to my PR comment
haim-kermany Mar 5, 2025
7666748
Merge branch '291_atomic_ip_block' of https://github.com/np-guard/vmw…
haim-kermany Mar 5, 2025
5ec6d7b
lint
ShiriMoran Mar 5, 2025
5d4338c
Merge remote-tracking branch 'origin/291_atomic_ip_block' into 291_at…
ShiriMoran Mar 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions pkg/configuration/topology/external_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import (
)

type ExternalIP struct {
ipBlock
IPBlock
}

func NewExternalIP(block *netset.IPBlock) *ExternalIP {
e := &ExternalIP{ipBlock: ipBlock{Block: block, originalIP: block.String()}}
e := &ExternalIP{IPBlock: IPBlock{Block: block, OriginalIP: block.String()}}
return e
}

func (ip *ExternalIP) Name() string { return ip.originalIP }
func (ip *ExternalIP) String() string { return ip.originalIP }
func (ip *ExternalIP) Name() string { return ip.OriginalIP }
func (ip *ExternalIP) String() string { return ip.OriginalIP }
func (ip *ExternalIP) Kind() string { return "external IP" }
func (ip *ExternalIP) ID() string { return ip.originalIP }
func (ip *ExternalIP) ID() string { return ip.OriginalIP }
func (ip *ExternalIP) InfoStr() []string {
return []string{ip.Name(), ip.ID(), ip.Name()}
}
func (ip *ExternalIP) Tags() []string { return nil }
func (ip *ExternalIP) IPAddressesStr() string { return ip.originalIP }
func (ip *ExternalIP) IPAddressesStr() string { return ip.OriginalIP }
12 changes: 6 additions & 6 deletions pkg/configuration/topology/ip_blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@ import (
)

// a base struct to represent external endpoints, segments and rule block
type ipBlock struct {
type IPBlock struct {
Block *netset.IPBlock
originalIP string
OriginalIP string
}
type RuleIPBlock struct {
ipBlock
IPBlock
VMs []Endpoint
ExternalIPs []Endpoint
}

func NewRuleIPBlock(ip string, block *netset.IPBlock) *RuleIPBlock {
return &RuleIPBlock{ipBlock: ipBlock{Block: block, originalIP: ip}}
return &RuleIPBlock{IPBlock: IPBlock{Block: block, OriginalIP: ip}}
}

type Segment struct {
ipBlock
IPBlock
name string
VMs []Endpoint
}

func NewSegment(name string, block *netset.IPBlock, subnetsNetworks []string) *Segment {
return &Segment{name: name, ipBlock: ipBlock{Block: block, originalIP: strings.Join(subnetsNetworks, common.CommaSeparator)}}
return &Segment{name: name, IPBlock: IPBlock{Block: block, OriginalIP: strings.Join(subnetsNetworks, common.CommaSeparator)}}
}
69 changes: 21 additions & 48 deletions pkg/synthesis/symbolicexpr/atomicGeneral.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,6 @@ import (
"slices"
)

// tautology implementation

func (tautology) String() string {
return "*"
}

func (tautology) name() string {
return ""
}

func (tautology) negate() atomic {
return tautology{}
}

func (tautology) isNegation() bool {
return false
}

func (tautology) IsTautology() bool {
return true
}

// returns true iff otherAt is negation of
// once we cache the atomic terms, we can just compare pointers
func (tautology) isNegateOf(atomic) bool {
return false
}
func (tautology) AsSelector() (string, bool) {
return "", false
}

// tautology is not disjoint to any atomic term
func (tautology) disjoint(atomic, *Hints) bool {
return false
}

func (tautology) supersetOf(atom atomic, hints *Hints) bool {
return atom.IsTautology()
}

// general atomic functionality

// are two given by name atomicTerms in disjoint list
Expand All @@ -61,11 +21,6 @@ func (hints *Hints) disjoint(name1, name2 string) bool {

// functions of Atomic with identical impl of all implementing structs

// IsTautology an atomicTerm is a non empty cond on a group, a tag etc and is thus not a tautology
func (atomicTerm) IsTautology() bool {
return false
}

func (atomicTerm atomicTerm) isNegation() bool {
return atomicTerm.neg
}
Expand All @@ -82,11 +37,20 @@ func isNegateOf(atom, otherAtom atomic) bool {
// returns true iff otherAtom is disjoint to atom as given by hints
// todo: could e.g. groups and tags have the same name????
func disjoint(atom, otherAtom atomic, hints *Hints) bool {
// in hints list of disjoint groups/tags/.. is given. Actual atomicTerms are disjoint only if both not negated
if atom.isNegation() || otherAtom.isNegation() {
atomBlock := atom.getBlock()
otherAtomBlock := otherAtom.getBlock()
switch {
case atomBlock != nil && otherAtomBlock != nil:
return atomBlock.Intersect(otherAtomBlock).IsEmpty()
case atomBlock != nil || otherAtomBlock != nil:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why false for this case?
can use config details about internal addresses ranges?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If atom is an ipBlockTerm and otherAtom is a tagTerm or a groupTerm then there is no way we can compare them ( groupTerm is used only when we failed to analyze the group's expression).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if an expression is defined over VM properties, it may only capture internal VM addresses.
and in such case if IPBlock is known to be external, can infer they are disjoint?

Copy link
Contributor Author

@ShiriMoran ShiriMoran Mar 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can infer they are disjoint at the moment of synthesis. We can not infer they are meant to be disjoint.
And since using IPs to refer to internal is defined a bad practice, I don't think we should develop methodology for this case (as "guessing" disjointness)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, please open a separate issue for discussing/considering this later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return false
default:
// in hints list of disjoint groups/tags/.. is given. Actual atomicTerms are disjoint only if both not negated
if atom.isNegation() || otherAtom.isNegation() {
return false
}
return hints.disjoint(atom.name(), otherAtom.name())
}
return hints.disjoint(atom.name(), otherAtom.name())
}

// returns true iff atom is supersetOf of otherAtom other as given by hints
Expand All @@ -97,3 +61,12 @@ func disjoint(atom, otherAtom atomic, hints *Hints) bool {
func supersetOf(atom, otherAtom atomic, hints *Hints) bool {
return hints.disjoint(atom.name(), otherAtom.name()) && atom.isNegation() && !otherAtom.isNegation()
}

// return equalSignConst or nonEqualSignConst for atom
func eqSign(atom atomic) string {
equalSign := equalSignConst
if atom.isNegation() {
equalSign = nonEqualSignConst
}
return equalSign
}
73 changes: 64 additions & 9 deletions pkg/synthesis/symbolicexpr/conjunction.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package symbolicexpr

import (
"github.com/np-guard/vmware-analyzer/internal/common"
"github.com/np-guard/vmware-analyzer/pkg/configuration/topology"
)

const emptySet = "empty set "
Expand All @@ -17,7 +18,33 @@ func (c *Conjunction) add(atom atomic) *Conjunction {
if c.contains(atom) {
return c
}
res := append(*c, atom)
var ipBlockAddedToExisting bool
// if c is an IPBlock, adds it to other IPBlock in the Conjunction, if any. Otherwise, just appends it
// in the former case we lose the OriginalIP
block := atom.getBlock()
var res Conjunction
if block != nil { // atom is an IPBlockTerm
// looks for an IPBlock in c
for _, itemAtom := range *c {
itemBlock := itemAtom.getBlock()
if itemBlock == nil { // itemAtom not an IPBlock
res = append(res, itemAtom)
} else {
// note that there could be at most one IPBlock in a conjunction, by design
// since the Conjunction's items are added, we should intersect the IPBlocks
newIPBlockAtomicTerm := &ipBlockAtomicTerm{atomicTerm: atomicTerm{},
IPBlock: &topology.IPBlock{Block: block.Intersect(itemBlock)}}
res = append(res, newIPBlockAtomicTerm)
ipBlockAddedToExisting = true
}
}
}
if ipBlockAddedToExisting {
return &res
}
// atom was not an ipBlockTerm or c did not yet have an ipBlockTerm
res = append(*c, atom)

return &res
}

Expand All @@ -27,8 +54,12 @@ func (c *Conjunction) copy() *Conjunction {
return &newC
}

// tautology: ipBlock 0.0.0.0/0 or tautology struct; at most 2 items
func (c *Conjunction) isTautology() bool {
if len(*c) == 1 && (*c)[0].IsTautology() {
if len(*c) > 2 || len(*c) == 0 || !(*c)[0].IsTautology() {
return false
}
if len(*c) == 1 || (len(*c) == 2 && (*c)[1].IsTautology()) {
return true
}
return false
Expand All @@ -55,7 +86,8 @@ func (c *Conjunction) removeRedundant(hints *Hints) Conjunction {
return newC
}

// atomic atom is a redundant in Conjunction c, if it is a superset of one of c's terms
// atomic atom is a redundant in Conjunction c, if it is a superset of one of c's terms; this applies to tagTerm and
// groupTerm; as to ipBlockTerm - there is at most one such term which is not redundant by design
func atomRedundantInConj(atom atomic, c *Conjunction, hints *Hints) bool {
if len(*c) == 0 { // nil Conjunction is equiv to tautology
return false
Expand All @@ -68,13 +100,21 @@ func atomRedundantInConj(atom atomic, c *Conjunction, hints *Hints) bool {
return false
}

// checks whether the conjunction is empty: either syntactically, or contains an groupAtomicTerm and its negation
// isEmpty: checks whether the conjunction is false:
// either contains an empty ipBlockTerm
// or contains groupAtomicTerm and its negation
// or contains two atoms that are disjoint to each other by hints
func (c *Conjunction) isEmptySet(hints *Hints) bool {
func (c *Conjunction) isEmpty(hints *Hints) bool {
if len(*c) == 0 {
return true
return false
}
for i, outAtomicTerm := range *c {
if outAtomicTerm.IsContradiction() {
return true
}
if outAtomicTerm.getBlock() != nil {
continue
}
reminder := *c
reminder = reminder[i+1:]
for _, inAtomicTerm := range reminder {
Expand Down Expand Up @@ -104,12 +144,26 @@ func (c *Conjunction) disjoint(other *Conjunction, hints *Hints) bool {
return false
}

// a Conjunction c contains an atom atomic if:
// semantically: the condition in atom is already implied by c
// syntactically:
// if atom is a tagTerm or a groupTerm, then if the Conjunction c contains the atom literally
// if atom is an IPBlock, if there is already an IPBlock in c that atom is a superset of it.
func (c *Conjunction) contains(atom atomic) bool {
for _, atomicTerm := range *c {
if atomicTerm.String() == (atom).String() {
if atom.IsTautology() {
return true
}
for _, atomicItem := range *c {
// the following is relevant only when both atom and atomicItem are ipBlockTerms;
// in the other cases supersetOf is based on hints
if atom.supersetOf(atomicItem, &Hints{}) {
return true
}
if atomicItem.String() == (atom).String() {
return true
}
}

return false
}

Expand Down Expand Up @@ -143,7 +197,8 @@ func (c *Conjunction) isSuperset(other *Conjunction, hints *Hints) bool {

// Conjunction c is a superset of atomic atom if any resource satisfying atom also satisfies c
// this is the case if each of c's term is a superset of atom
// e.g., given that Slytherin and Hufflepuff are disjoint, group != Hufflepuff is a superset of group = Slytherin
// e.g., 1.2.1.0/8 is a superset of 1.2.1.0/16;
// given that Slytherin and Hufflepuff are disjoint, group != Hufflepuff is a superset of group = Slytherin
func conjSupersetOfAtom(c *Conjunction, atom atomic, hints *Hints) bool {
if len(*c) == 0 { // nil Conjunction is equiv to tautology
return false
Expand Down
51 changes: 51 additions & 0 deletions pkg/synthesis/symbolicexpr/contradiction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package symbolicexpr

import "github.com/np-guard/models/pkg/netset"

// contradiction implementation; contradiction is the negation of tautology

func (contradiction) String() string {
return "empty set"
}

func (contradiction) name() string {
return ""
}

func (contradiction) negate() atomic {
return tautology{}
}

func (contradiction) isNegation() bool {
return false
}

func (contradiction) IsTautology() bool {
return false
}

func (contradiction) IsContradiction() bool {
return true
}

// returns true iff otherAt is negation of
// once we cache the atomic terms, we can just compare pointers
func (contradiction) isNegateOf(atom atomic) bool {
return atom.IsTautology()
}
func (contradiction) AsSelector() (string, bool) {
return "", false
}

// contradiction is disjoint to any atomic term
func (contradiction) disjoint(atomic, *Hints) bool {
return true
}

func (contradiction) supersetOf(atom atomic, hints *Hints) bool {
return false
}

func (contradiction) getBlock() *netset.IPBlock {
return nil
}
21 changes: 16 additions & 5 deletions pkg/synthesis/symbolicexpr/groupTerm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package symbolicexpr
import (
"fmt"

"github.com/np-guard/models/pkg/netset"
"github.com/np-guard/vmware-analyzer/pkg/collector"
"github.com/np-guard/vmware-analyzer/pkg/logging"
)
Expand All @@ -12,11 +13,17 @@ const equalSignConst = " = "
const nonEqualSignConst = " != "

func (groupTerm groupAtomicTerm) String() string {
equalSign := equalSignConst
if groupTerm.neg {
equalSign = nonEqualSignConst
}
return grp + equalSign + groupTerm.name()
return grp + eqSign(groupTerm) + groupTerm.name()
}

// IsTautology false since an groupAtomicTerm is a non-empty cond on a group
func (groupAtomicTerm) IsTautology() bool {
return false
}

// IsContradiction false since an groupAtomicTerm is a non-empty cond on a group
func (groupAtomicTerm) IsContradiction() bool {
return false
}

func (groupTerm groupAtomicTerm) AsSelector() (string, bool) {
Expand Down Expand Up @@ -82,3 +89,7 @@ func (groupTerm groupAtomicTerm) disjoint(otherAtom atomic, hints *Hints) bool {
func (groupTerm groupAtomicTerm) supersetOf(otherAtom atomic, hints *Hints) bool {
return supersetOf(groupTerm, otherAtom, hints)
}

func (groupAtomicTerm) getBlock() *netset.IPBlock {
return nil
}
Loading