Source code for linuxnet.iptables.matches.ttlmatch

# Copyright (c) 2021, 2022, 2023, Panagiotis Tsirigotis

# This file is part of linuxnet-iptables.
#
# linuxnet-iptables is free software: you can redistribute it and/or
# modify it under the terms of version 3 of the GNU Affero General Public
# License as published by the Free Software Foundation.
#
# linuxnet-iptables is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General
# Public License along with linuxnet-iptables. If not, see
# <https://www.gnu.org/licenses/>.

"""
This module provides matching against the packet TTL
"""

from typing import Iterable, List, Tuple

from ..exceptions import IptablesParsingError
from ..deps import get_logger

from .match import Match, Criterion, MatchParser, CriteriaExhaustedError

_logger = get_logger('linuxnet.iptables.matches.ttlmatch')


[docs]class TtlCriterion(Criterion): """A criterion for a TTL value comparison used by :class:`TtlMatch`. """ _EQ_COMP = '==' _LT_COMP = '<' _GT_COMP = '>' def __init__(self, match: Match): super().__init__(match) self.__value = None self.__comp = None
[docs] def get_value(self) -> Tuple[int, str]: """Returns the value that the criterion is comparing against and the comparison operation (as a string) :rtype: tuple of (int, str) """ return (self.__value, self.__comp)
[docs] def equals(self, value: int) -> Match: # pylint: disable=arguments-differ """Check if the packet TTL is equal to ``value`` :param value: the TTL value """ self.__value = value self.__comp = self._EQ_COMP return self._set_polarity(True)
[docs] def less_than(self, value: int) -> Match: """Check if the packet TTL is less than ``value`` :param value: the TTL value """ self.__value = value self.__comp = self._LT_COMP return self._set_polarity(True)
[docs] def greater_than(self, value: int) -> Match: """Check if the packet TTL is greater than ``value`` :param value: the TTL value """ self.__value = value self.__comp = self._GT_COMP return self._set_polarity(True)
def _crit_iptables_args(self) -> List[str]: """Returns **iptables(8)** arguments for the specified TTL comparison """ if self.__comp == self._EQ_COMP: return ['--ttl-eq', str(self.__value)] if self.__comp == self._LT_COMP: return ['--ttl-lt', str(self.__value)] return ['--ttl-gt', str(self.__value)]
[docs]class TtlMatch(Match): """Match against the packet TTL value """ def __init__(self): self.__ttl_crit = None
[docs] @staticmethod def get_match_name(): """Returns the **iptables(8)** match extension name """ return 'ttl'
[docs] def get_criteria(self) -> Iterable[Criterion]: """Returns the TTL match criteria (only one). """ return (self.__ttl_crit,)
[docs] def ttl(self) -> TtlCriterion: """Returns the TTL criterion """ if self.__ttl_crit is None: self.__ttl_crit = TtlCriterion(self) return self.__ttl_crit
@classmethod def parse(cls, parser: MatchParser) -> Match: """Parse the TTL criterion:: TTL match TTL > 5 :meta private: """ criteria_iter = parser.get_iter() val = next(criteria_iter) if val != 'match': # It must be a TTL target criteria_iter.put_back(val) raise CriteriaExhaustedError() parser.skip_field('TTL') comp = next(criteria_iter) value = int(next(criteria_iter)) if comp == '==': return TtlMatch().ttl().equals(value) if comp == '!=': return TtlMatch().ttl().not_equals(value) if comp == '>': return TtlMatch().ttl().greater_than(value) if comp == '<': return TtlMatch().ttl().less_than(value) raise IptablesParsingError(f"bad TTL comparison: '{comp}' ")
MatchParser.register_match('TTL', TtlMatch)