1# Copyright 2016, Tresys Technology, LLC
2#
3# This file is part of SETools.
4#
5# SETools is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License as
7# published by the Free Software Foundation, either version 2.1 of
8# the License, or (at your option) any later version.
9#
10# SETools is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU Lesser General Public License for more details.
14#
15# You should have received a copy of the GNU Lesser General Public
16# License along with SETools.  If not, see
17# <http://www.gnu.org/licenses/>.
18#
19from collections import namedtuple
20
21from .context import ContextWrapper
22from .descriptors import DiffResultDescriptor
23from .difference import Difference, Wrapper
24
25
26modified_portcon_record = namedtuple("modified_portcon", ["rule",
27                                                          "added_context",
28                                                          "removed_context"])
29
30
31class PortconsDifference(Difference):
32
33    """Determine the difference in portcons between two policies."""
34
35    added_portcons = DiffResultDescriptor("diff_portcons")
36    removed_portcons = DiffResultDescriptor("diff_portcons")
37    modified_portcons = DiffResultDescriptor("diff_portcons")
38
39    def diff_portcons(self):
40        """Generate the difference in portcons between the policies."""
41
42        self.log.info("Generating portcon differences from {0.left_policy} to {0.right_policy}".
43                      format(self))
44
45        self.added_portcons, self.removed_portcons, matched_portcons = self._set_diff(
46            (PortconWrapper(n) for n in self.left_policy.portcons()),
47            (PortconWrapper(n) for n in self.right_policy.portcons()))
48
49        self.modified_portcons = []
50
51        for left_portcon, right_portcon in matched_portcons:
52            # Criteria for modified portcons
53            # 1. change to context
54            if ContextWrapper(left_portcon.context) != ContextWrapper(right_portcon.context):
55                self.modified_portcons.append(modified_portcon_record(left_portcon,
56                                                                      right_portcon.context,
57                                                                      left_portcon.context))
58
59    #
60    # Internal functions
61    #
62    def _reset_diff(self):
63        """Reset diff results on policy changes."""
64        self.log.debug("Resetting portcon differences")
65        self.added_portcons = None
66        self.removed_portcons = None
67        self.modified_portcons = None
68
69
70class PortconWrapper(Wrapper):
71
72    """Wrap portcon statements for diff purposes."""
73
74    def __init__(self, ocon):
75        self.origin = ocon
76        self.protocol = ocon.protocol
77        self.low, self.high = ocon.ports
78        self.key = hash(ocon)
79
80    def __hash__(self):
81        return self.key
82
83    def __lt__(self, other):
84        return self.origin < other.origin
85
86    def __eq__(self, other):
87        return self.protocol == other.protocol and \
88               self.low == other.low and \
89               self.high == other.high
90