diff --git a/qubesadmin/firewall.py b/qubesadmin/firewall.py index ad6014b..991c603 100644 --- a/qubesadmin/firewall.py +++ b/qubesadmin/firewall.py @@ -286,8 +286,10 @@ class Rule(object): :param kwargs: rule elements ''' self._action = None + self._forwardtype = None self._proto = None self._dsthost = None + self._srcports = None self._dstports = None self._icmptype = None self._specialtarget = None @@ -304,8 +306,9 @@ class Rule(object): rule_dict['comment'] = comment rule_dict.update(kwargs) - rule_elements = ('action', 'proto', 'dsthost', 'dst4', 'dst6', - 'specialtarget', 'dstports', 'icmptype', 'expire', 'comment') + rule_elements = ('action', 'forwardtype', 'proto', 'dsthost', 'dst4', + 'dst6', 'specialtarget', 'srcports', 'dstports', 'icmptype', + 'expire', 'comment') for rule_opt in rule_elements: value = rule_dict.pop(rule_opt, None) if value is None: @@ -332,6 +335,17 @@ class Rule(object): value = Action(value) self._action = value + @property + def forwardtype(self): + '''type of forwarding (internal or external)''' + return self._forwardtype + + @forwardtype.setter + def forwardtype(self, value): + if not isinstance(value, ForwardType): + value = ForwardType(value) + self._forwardtype = value + @property def proto(self): '''protocol to match''' @@ -358,6 +372,21 @@ class Rule(object): value = DstHost(value) self._dsthost = value + @property + def srcports(self): + ''''Source port(s) (for forwarding only)''' + return self._srcports + + @srcports.setter + def srcports(self, value): + if value is not None: + if self.proto not in ('tcp', 'udp'): + raise ValueError( + 'srcports valid only for \'tcp\' and \'udp\' protocols') + if not isinstance(value, DstPorts): + value = SrcPorts(value) + self._srcports = value + @property def dstports(self): ''''Destination port(s) (for \'tcp\' and \'udp\' protocol only)''' @@ -425,8 +454,8 @@ class Rule(object): '''API representation of this rule''' values = [] # comment must be the last one - for prop in ('action', 'proto', 'dsthost', 'dstports', 'icmptype', - 'specialtarget', 'expire', 'comment'): + for prop in ('action', 'forwardtype', 'proto', 'dsthost', 'srcports', + 'dstports', 'icmptype', 'specialtarget', 'expire', 'comment'): value = getattr(self, prop) if value is None: continue diff --git a/qubesadmin/tools/qvm_firewall.py b/qubesadmin/tools/qvm_firewall.py index c6b59d9..d138a6c 100644 --- a/qubesadmin/tools/qvm_firewall.py +++ b/qubesadmin/tools/qvm_firewall.py @@ -44,7 +44,7 @@ class RuleAction(argparse.Action): if not values: setattr(namespace, self.dest, None) return - assumed_order = ['action', 'dsthost', 'proto', 'dstports', 'icmptype'] + assumed_order = ['action', 'forwardtype', 'dsthost', 'proto', 'srcports', 'dstports', 'icmptype'] allowed_opts = assumed_order + ['specialtarget', 'comment', 'expire'] kwargs = {} for opt in values: @@ -148,8 +148,8 @@ def rules_list_table(vm): :param vm: VM object :return: None ''' - header = ['NO', 'ACTION', 'FORWARD TYPE', 'HOST', 'PROTOCOL', 'SRCPORT(s)', - 'PORT(S)', 'SPECIAL TARGET', 'ICMP TYPE', 'EXPIRE', 'COMMENT'] + header = ['NO', 'ACTION', 'FORWARD TYPE', 'HOST', 'PROTOCOL', 'SRCPORT(S)', + 'DSTPORT(S)', 'SPECIAL TARGET', 'ICMP TYPE', 'EXPIRE', 'COMMENT'] rows = [] for (rule, rule_no) in zip(vm.firewall.rules, itertools.count()): row = [x.pretty_value if x is not None else '-' for x in [ @@ -162,7 +162,7 @@ def rules_list_table(vm): rule.specialtarget, rule.icmptype, rule.expire, - rule.comment + rule.comment, ]] rows.append([str(rule_no)] + row) qubesadmin.tools.print_table([header] + rows)