############################################################################
##
## Copyright (c) 2000-2001 BalaBit IT Ltd, Budapest, Hungary
## All rights reserved.
##
## $Id: IPChains.py,v 1.1.2.2.2.1 2001/05/11 14:19:10 bazsi Exp $
##
############################################################################

"""Module implementing interface to the kernel packet filter named ipchains.

IPChains is used by Linux kernels version 2.2.x, and this code can
be used to append, delete or change packet filtering rules from within
Zorp. 

Note

  Note that this code doesn't call the user space program 'ipchains', it
  calls system calls directly.
"""

import Zorp
from Domain import InetDomain

CORE_IPCHAINS = 'core.ipchains'

# Values for "fw_flg" field
IP_FW_F_PRN     = 0x0001  # Print packet if it matches 
IP_FW_F_TCPSYN  = 0x0002  # For tcp packets-check SYN only 
IP_FW_F_FRAG    = 0x0004  # Set if rule is a fragment rule 
IP_FW_F_MARKABS = 0x0008  # Set the mark to fw_mark, not add. 
IP_FW_F_WILDIF  = 0x0010  # Need only match start of interface name. 
IP_FW_F_NETLINK = 0x0020  # Redirect to netlink: 2.1.x only 
IP_FW_F_MASK    = 0x003F  # All possible flag bits mask   

# Values for "fw_invflg" field
IP_FW_INV_SRCIP = 0x0001  # Invert the sense of fw_src. 
IP_FW_INV_DSTIP = 0x0002  # Invert the sense of fw_dst. 
IP_FW_INV_PROTO = 0x0004  # Invert the sense of fw_proto. 
IP_FW_INV_SRCPT = 0x0008  # Invert the sense of source ports. 
IP_FW_INV_DSTPT = 0x0010  # Invert the sense of destination ports. 
IP_FW_INV_VIA   = 0x0020  # Invert the sense of fw_vianame. 
IP_FW_INV_SYN   = 0x0040  # Invert the sense of IP_FW_F_TCPSYN. 
IP_FW_INV_FRAG  = 0x0080  # Invert the sense of IP_FW_F_FRAG. 


def calcIpMask(dom):
	"""Function calculating the IP, netmask values of dom.
	
	This function returns a tuple containing an IP and a netmask value
	in network byte order extracted from the parameter 'dom'. It's an
	internal function used by the IPChains manipulating code.
	
	Arguments
	
	  dom -- an InetDomain or SockAddrInet instance whose address/netmask
	         is to be calculated, the exact type is determined at runtime.
	
	Returns
	
	  a tuple of (ip, netmask)
	"""
	if isinstance(dom, InetDomain):
		if dom.family != AF_INET:
			raise ValueError, 'IPChains supports only AF_INET address domains'
		return dom.ip, dom.mask
	else:
		return dom.ip, 0xffffffff


class IPChains:
	"""Class encapsulating the kernel packet filter
	
	This class is an interface to the kernel packet filter named
	'ipchains'.
	
	Attributes
	
	  rules -- an array of rules added by this object, these rules
	           are implicitly cleared when this object is deleted
	  
	  ipchains -- an instance of the Zorp.IPChains class implemented
	              by the Zorp core.
	
	"""
	
	
	def __init__(self):
		"""Constructor to initialize an IPChains instance
		
		This constructor initializes an IPChains instance by
		setting default values for attributes, and creating
		a Zorp.IPChains instance.
		
		Arguments
		
		  self -- this instance
		"""
		# rule: (target, (src, smask), (dst, dmask), mark, proto, flags, inv_flags, (sportmin, sportmax), (dportmin, dportmax), redirport, iface, tosand, tosxor)
		self.rules = []
		self.ipchains = Zorp.IPChains()
	
	def __del__(self):
		"""Destructor to delete rules added by this object.
		
		This destructor is called when the IPChains instance is freed.
		It automatically removes all rules we didn't explicitly remove.
		
		Arguments
		
		  self -- this instance
		"""
		if self.ipchains:
			for rule in self.rules[:]:
				self.delRule(rule)

	def addRule(self, rule):
		"""Method to actually add a rule to a chain.
		
		This is a general function called by more specific methods.
		
		Arguments
		
		  self -- this instance
		  
		  rule -- a tuple describing the rule to add in the syntax
                          (target, (src, smask), (dst, dmask), mark, proto, flags, inv_flags, (sportmin, sportmax), (dportmin, dportmax), redirport, iface, tosand, tosxor)
		"""
		apply(self.ipchains.addRule, rule)
		Zorp.log(CORE_IPCHAINS, 7, 'adding ipchains rule; rule='%s'' % (rule,))
		self.rules.append(rule)
	
	def delRule(self, rule):
		"""Method to delete a rule.
		
		This method deletes a rule specified in its rule argument.
		
		Arguments
		
		  self -- this instance
		  
		  rule -- rule to delete
		"""
		Zorp.log(CORE_IPCHAINS, 7, 'deleting ipchains rule; rule='%s'' % (rule,))
		apply(self.ipchains.delRule, rule)
		self.rules.remove(rule)

	def addREDIRECT(self, chain, sdom, sports, ddom, dports, redirport, iface="", mark=0, proto=0, flags=0, inv_flags=0, tosand=0xff, tosxor=0x00):
		"""Method specialized to add a 'REDIRECT' rule
		
		This is a wrapper around the addRule() function
		making it easier to add 'REDIRECT' rules. It returns the
		full rule added, so that later invocations of delRule()
		can delete it. For more information on different arguments
		check out the ipchains(8) manual page.
		
		Arguments
		
		  self -- this instance
		  
		  chain -- chain to append this rule to
		  
		  sdom -- source address range (specified as an InetDomain or SockAddrInet instance)
		  
		  sports -- a tuple of two ports specifying matching port range
		  
		  ddom -- destination address range
		  
		  dports -- destination port range
		  
		  redirport -- redirect connections to this local port
		  
		  iface -- interface specified as a string (default none)
		  
		  mark -- mark packets with this fwmark, (default 0)
		  
		  proto -- match packets with this protocoll (default 0)

		  flags -- a combination of IP_FW_F_* flags above (default 0)
		  
		  inv_flags -- a combination of IP_FW_INV_F_* flags above (default 0)
		  
		  tosand    -- mask to type of service (default 0xff)
		  
		  tosxor    -- xor to type of service (default 0x00)
		"""
		rule = (chain, ('REDIRECT', calcIpMask(sdom), calcIpMask(ddom), 0, proto, flags, inv_flags, sports, dports, redirport, iface, tosand, tosxor))
		self.addRule(rule)
		return rule
	
	def addACCEPT(self, chain, sdom, sports, ddom, dports, iface="", mark=0, proto=0, flags=0, inv_flags=0, redirport=0, tosand=0xff, tosxor=0x00):
		"""Method specialized to add an 'ACCEPT' rule.
		
		This is a wrapper around addRule() to add an 'ACCEPT' 
		rule to one of the chains.
		
		Arguments
		
		  self -- this instance

		  chain -- chain to append this rule to
		  
		  sdom -- source address range (specified as an InetDomain or SockAddrInet instance)
		  
		  sports -- a tuple of two ports specifying matching port range
		  
		  ddom -- destination address range
		  
		  dports -- destination port range
		  
		  iface -- interface specified as a string (default none)
		  
		  mark -- mark packets with this fwmark, (default 0)
		  
		  proto -- match packets with this protocoll (default 0)

		  flags -- a combination of IP_FW_F_* flags above (default 0)
		  
		  inv_flags -- a combination of IP_FW_INV_F_* flags above (default 0)
		  
		  tosand    -- mask to type of service (default 0xff)
		  
		  tosxor    -- xor to type of service (default 0x00)
		"""
		rule = (chain, ('ACCEPT', calcIpMask(sdom), calcIpMask(ddom), 0, proto, flags, inv_flags, sports, dports, redirport, iface, tosand, tosxor))
		self.addRule(rule)
		return rule

	def addDENY(self, chain, sdom, sports, ddom, dports, iface="", mark=0, proto=0, flags=0, inv_flags=0, redirport=0, tosand=0xff, tosxor=0x00):
		"""Method specialized to add a 'DENY' rule.
		
		This is a wrapper around addRule() to add an 'DENY' 
		rule to one of the chains.
		
		Arguments
		
		  self -- this instance
		  
		  chain -- chain to append this rule to
		  
		  sdom -- source address range (specified as an InetDomain or SockAddrInet instance)
		  
		  sports -- a tuple of two ports specifying matching port range
		  
		  ddom -- destination address range
		  
		  dports -- destination port range
		  
		  iface -- interface specified as a string (default none)
		  
		  mark -- mark packets with this fwmark, (default 0)
		  
		  proto -- match packets with this protocoll (default 0)

		  flags -- a combination of IP_FW_F_* flags above (default 0)
		  
		  inv_flags -- a combination of IP_FW_INV_F_* flags above (default 0)
		  
		  tosand    -- mask to type of service (default 0xff)
		  
		  tosxor    -- xor to type of service (default 0x00)
		"""
		rule = (chain, ('DENY', calcIpMask(sdom), calcIpMask(ddom), 0, proto, flags, inv_flags, sports, dports, redirport, iface, tosand, tosxor))
		self.addRule(rule)
		return rule

	def addREJECT(self, chain, sdom, sports, ddom, dports, iface="", mark=0, proto=0, flags=0, inv_flags=0, redirport=0, tosand=0xff, tosxor=0x00):
		"""Method specialized to add a 'REJECT' rule.
		
		This is a wrapper around addRule() to add an 'REJECT' 
		rule to one of the chains.
		
		Arguments
		
		  self -- this instance
		  
		  chain -- chain to append this rule to
		  
		  sdom -- source address range (specified as an InetDomain or SockAddrInet instance)
		  
		  sports -- a tuple of two ports specifying matching port range
		  
		  ddom -- destination address range
		  
		  dports -- destination port range
		  
		  iface -- interface specified as a string (default none)
		  
		  mark -- mark packets with this fwmark, (default 0)
		  
		  proto -- match packets with this protocoll (default 0)

		  flags -- a combination of IP_FW_F_* flags above (default 0)
		  
		  inv_flags -- a combination of IP_FW_INV_F_* flags above (default 0)
		  
		  tosand    -- mask to type of service (default 0xff)
		  
		  tosxor    -- xor to type of service (default 0x00)
		"""
		rule = (chain, ('REJECT', calcIpMask(sdom), calcIpMask(ddom), 0, proto, flags, inv_flags, sports, dports, redirport, iface, tosand, tosxor))
		self.addRule(rule)
		return rule

	def addRETURN(self, chain, sdom, sports, ddom, dports, iface="", mark=0, proto=0, flags=0, inv_flags=0, redirport=0, tosand=0xff, tosxor=0x00):
		"""Method specialized to add a 'RETURN' rule.
		
		This is a wrapper around addRule() to add an 'RETURN' 
		rule to one of the chains.
		
		Arguments
		
		  self -- this instance
		  
		  chain -- chain to append this rule to
		  
		  sdom -- source address range (specified as an InetDomain or SockAddrInet instance)
		  
		  sports -- a tuple of two ports specifying matching port range
		  
		  ddom -- destination address range
		  
		  dports -- destination port range
		  
		  iface -- interface specified as a string (default none)
		  
		  mark -- mark packets with this fwmark, (default 0)
		  
		  proto -- match packets with this protocoll (default 0)

		  flags -- a combination of IP_FW_F_* flags above (default 0)
		  
		  inv_flags -- a combination of IP_FW_INV_F_* flags above (default 0)
		  
		  tosand    -- mask to type of service (default 0xff)
		  
		  tosxor    -- xor to type of service (default 0x00)
		"""
		rule = (chain, ('RETURN', calcIpMask(sdom), calcIpMask(ddom), 0, proto, flags, inv_flags, sports, dports, redirport, iface, tosand, tosxor))
		self.addRule(rule)
		return rule
	
