Commit e062f089 authored by Peter V. Saveliev's avatar Peter V. Saveliev Committed by GitHub

Merge pull request #283 from ffourcot/various-ipset

ipset: flush, rename etc.
parents f5218a13 374feb97
......@@ -15,6 +15,7 @@ from pyroute2.netlink import NLM_F_DUMP
from pyroute2.netlink import NLM_F_ACK
from pyroute2.netlink import NLM_F_EXCL
from pyroute2.netlink import NETLINK_NETFILTER
from pyroute2.netlink.exceptions import NetlinkError, IPSetError
from pyroute2.netlink.nlsocket import NetlinkSocket
from pyroute2.netlink.nfnetlink import NFNL_SUBSYS_IPSET
from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_PROTOCOL
......@@ -24,6 +25,8 @@ from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_SWAP
from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_LIST
from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_ADD
from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_DEL
from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_FLUSH
from pyroute2.netlink.nfnetlink.ipset import IPSET_CMD_RENAME
from pyroute2.netlink.nfnetlink.ipset import ipset_msg
from pyroute2.netlink.nfnetlink.ipset import IPSET_FLAG_WITH_COUNTERS
from pyroute2.netlink.nfnetlink.ipset import IPSET_FLAG_WITH_COMMENT
......@@ -58,9 +61,12 @@ class IPSet(NetlinkSocket):
msg_flags=NLM_F_REQUEST | NLM_F_DUMP,
terminate=None):
msg['nfgen_family'] = self._nfgen_family
return self.nlm_request(msg,
msg_type | (NFNL_SUBSYS_IPSET << 8),
msg_flags, terminate=terminate)
try:
return self.nlm_request(msg,
msg_type | (NFNL_SUBSYS_IPSET << 8),
msg_flags, terminate=terminate)
except NetlinkError as err:
raise IPSetError(err.code)
def list(self, name=None):
'''
......@@ -90,7 +96,7 @@ class IPSet(NetlinkSocket):
def create(self, name, stype='hash:ip', family=socket.AF_INET,
exclusive=True, counters=False, comment=False,
maxelem=IPSET_DEFAULT_MAXELEM, forceadd=False,
hashsize=None):
hashsize=None, timeout=None):
'''
Create an ipset `name` of type `stype`, by default
`hash:ip`.
......@@ -112,6 +118,8 @@ class IPSet(NetlinkSocket):
["IPSET_ATTR_MAXELEM", maxelem]]}
if hashsize is not None:
data['attrs'] += [["IPSET_ATTR_HASHSIZE", hashsize]]
if timeout is not None:
data['attrs'] += [["IPSET_ATTR_TIMEOUT", timeout]]
msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version],
['IPSET_ATTR_SETNAME', name],
......@@ -124,7 +132,8 @@ class IPSet(NetlinkSocket):
msg_flags=NLM_F_REQUEST | NLM_F_ACK | excl_flag,
terminate=_nlmsg_error)
def _add_delete(self, name, entry, family, cmd, exclusive, comment=None):
def _add_delete(self, name, entry, family, cmd, exclusive, comment=None,
timeout=None):
if family == socket.AF_INET:
entry_type = 'IPSET_ATTR_IPADDR_IPV4'
elif family == socket.AF_INET6:
......@@ -137,6 +146,8 @@ class IPSet(NetlinkSocket):
if comment is not None:
data_attrs += [["IPSET_ATTR_COMMENT", comment],
["IPSET_ATTR_CADT_LINENO", 0]]
if timeout is not None:
data_attrs += [["IPSET_ATTR_TIMEOUT", timeout]]
msg = ipset_msg()
msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version],
['IPSET_ATTR_SETNAME', name],
......@@ -147,12 +158,12 @@ class IPSet(NetlinkSocket):
terminate=_nlmsg_error)
def add(self, name, entry, family=socket.AF_INET, exclusive=True,
comment=None):
comment=None, timeout=None):
'''
Add a member to the ipset
'''
return self._add_delete(name, entry, family, IPSET_CMD_ADD, exclusive,
comment=comment)
comment=comment, timeout=timeout)
def delete(self, name, entry, family=socket.AF_INET, exclusive=True):
'''
......@@ -171,3 +182,27 @@ class IPSet(NetlinkSocket):
return self.request(msg, IPSET_CMD_SWAP,
msg_flags=NLM_F_REQUEST | NLM_F_ACK,
terminate=_nlmsg_error)
def flush(self, name=None):
'''
Flush all ipsets. When name is set, flush only this ipset.
'''
msg = ipset_msg()
msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version]]
if name is not None:
msg['attrs'].append(['IPSET_ATTR_SETNAME', name])
return self.request(msg, IPSET_CMD_FLUSH,
msg_flags=NLM_F_REQUEST | NLM_F_ACK,
terminate=_nlmsg_error)
def rename(self, name_src, name_dst):
'''
Rename the ipset.
'''
msg = ipset_msg()
msg['attrs'] = [['IPSET_ATTR_PROTOCOL', self._proto_version],
['IPSET_ATTR_SETNAME', name_src],
['IPSET_ATTR_TYPENAME', name_dst]]
return self.request(msg, IPSET_CMD_RENAME,
msg_flags=NLM_F_REQUEST | NLM_F_ACK,
terminate=_nlmsg_error)
......@@ -40,3 +40,43 @@ class NetlinkNLADecodeError(NetlinkDecodeError):
The error occured while decoding NLA chain
'''
pass
class IPSetError(NetlinkError):
'''
Netlink error with IPSet special error codes.
Messages are imported from errcode.c
'''
def __init__(self, code, msg=None):
if code in self.error_map:
msg = self.error_map[code]
super(IPSetError, self).__init__(code, msg)
error_map = {4097: "Kernel error received: ipset protocol error",
4098: "Kernel error received: set type not supported",
4099: "Kernel error received: maximal number of sets reached,"
" cannot create more.",
4100: "Set cannot be destroyed: it is in use by a kernel"
" component",
4101: "Set cannot be renamed: a set with the new name already"
" exists / Sets cannot be swapped: the second set does"
" not exist",
4102: "The sets cannot be swapped: their type does not match",
4103: "Element cannot be added to the set: it's already"
" added",
4104: "The value of the CIDR parameter of the IP address is"
" invalid",
4105: "The value of the netmask parameter is invalid",
4106: "Protocol family not supported by the set type",
4107: "Timeout cannot be used: set was created without"
" timeout support",
4108: "Set cannot be renamed: it is in use by another system",
4109: "An IPv4 address is expected, but not received",
4110: "An IPv6 address is expected, but not received",
4111: "Packet/byte counters cannot be used: set was created"
" without counter support",
4112: "Comment string is too long!",
4113: "The value of the markmask parameter is invalid",
4114: "Skbinfo mapping cannot be used: set was created "
" without skbinfo support"}
......@@ -66,7 +66,7 @@ class ipset_msg(nfgen_msg):
(4, 'IPSET_ATTR_PORT', 'hex'),
(4, 'IPSET_ATTR_PORT_FROM', 'hex'),
(5, 'IPSET_ATTR_PORT_TO', 'hex'),
(6, 'IPSET_ATTR_TIMEOUT', 'hex'),
(6, 'IPSET_ATTR_TIMEOUT', 'be32', NLA_F_NET_BYTEORDER),
(7, 'IPSET_ATTR_PROTO', 'recursive'),
(8, 'IPSET_ATTR_CADT_FLAGS', 'be32', NLA_F_NET_BYTEORDER),
(9, 'IPSET_ATTR_CADT_LINENO', 'be32'),
......@@ -96,7 +96,7 @@ class ipset_msg(nfgen_msg):
(4, 'IPSET_ATTR_PORT', 'hex'),
(4, 'IPSET_ATTR_PORT_FROM', 'hex'),
(5, 'IPSET_ATTR_PORT_TO', 'hex'),
(6, 'IPSET_ATTR_TIMEOUT', 'hex'),
(6, 'IPSET_ATTR_TIMEOUT', 'be32', NLA_F_NET_BYTEORDER),
(7, 'IPSET_ATTR_PROTO', 'recursive'),
(8, 'IPSET_ATTR_CADT_FLAGS', 'be32', NLA_F_NET_BYTEORDER),
(9, 'IPSET_ATTR_CADT_LINENO', 'be32'),
......
import errno
from time import sleep
from pyroute2.ipset import IPSet
from pyroute2.netlink.exceptions import NetlinkError
from utils import require_user
......@@ -25,7 +26,8 @@ class TestIPSet(object):
ip = x.get_attr('IPSET_ATTR_IP_FROM').get_attr(ipaddr)
res[ip] = (x.get_attr("IPSET_ATTR_PACKETS"),
x.get_attr("IPSET_ATTR_BYTES"),
x.get_attr("IPSET_ATTR_COMMENT"))
x.get_attr("IPSET_ATTR_COMMENT"),
x.get_attr("IPSET_ATTR_TIMEOUT"))
return res
except:
return {}
......@@ -220,3 +222,44 @@ class TestIPSet(object):
assert ipaddr in self.list_ipset(name)
self.ip.destroy(name)
def test_flush(self):
require_user('root')
name = str(uuid4())[:16]
self.ip.create(name)
ip_a = "1.1.1.1"
ip_b = "1.2.3.4"
self.ip.add(name, ip_a)
self.ip.add(name, ip_b)
assert ip_a in self.list_ipset(name)
assert ip_b in self.list_ipset(name)
self.ip.flush(name)
assert ip_a not in self.list_ipset(name)
assert ip_b not in self.list_ipset(name)
self.ip.destroy(name)
def test_rename(self):
require_user('root')
name = str(uuid4())[:16]
name_bis = str(uuid4())[:16]
self.ip.create(name)
self.ip.rename(name, name_bis)
assert self.get_ipset(name_bis)
self.ip.destroy(name_bis)
def test_timeout(self):
require_user('root')
name = str(uuid4())[:16]
ip = "1.2.3.4"
self.ip.create(name, timeout=1)
self.ip.add(name, ip)
sleep(2)
assert ip not in self.list_ipset(name)
# check that we can overwrite default timeout value
self.ip.add(name, ip, timeout=5)
sleep(2)
assert ip in self.list_ipset(name)
assert self.list_ipset(name)[ip][3] > 0 # timeout
sleep(3)
assert ip not in self.list_ipset(name)
self.ip.destroy(name)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment