From bfb3df62a1d994692b5ae663721c57882dc73a9d Mon Sep 17 00:00:00 2001 From: "A. Wilcox" Date: Wed, 16 Dec 2020 12:08:12 -0600 Subject: ietf-ip: Add ability to manipulate IP addresses --- doc/roadmap.rst | 10 ++++---- ncserver/module/ip.py | 49 +++++++++++++++++++++++++++++++++++---- ncserver/module/nms_ifupdownng.py | 17 +++++++------- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/doc/roadmap.rst b/doc/roadmap.rst index c75584f..8eead9e 100644 --- a/doc/roadmap.rst +++ b/doc/roadmap.rst @@ -296,9 +296,9 @@ Resolved TBDs * [x] Set IPv4 MTU - * [ ] Retrieve/set IPv4 address(es) + * [x] Retrieve/set IPv4 address(es) - * [ ] Handle neighbour / static ARP cache (set) + * [-] Handle neighbour / static ARP cache (set) * [x] Enable/disable IPv6 on an interface @@ -306,7 +306,7 @@ Resolved TBDs * [x] Set IPv6 MTU - * [ ] Retrieve/set IPv6 address(es) + * [x] Retrieve/set IPv6 address(es) * [x] IPv6 DAD: Neighbour-Solicitation messages sent @@ -314,9 +314,9 @@ Resolved TBDs * [ ] State nodes - * [ ] Origin of IPv4 address(es) + * [/] Origin of IPv4 address(es) - * [ ] Origin of IPv6 address(es) + * [/] Origin of IPv6 address(es) * [ ] IPv6 address status diff --git a/ncserver/module/ip.py b/ncserver/module/ip.py index 84eab13..024ce4f 100644 --- a/ncserver/module/ip.py +++ b/ncserver/module/ip.py @@ -10,6 +10,8 @@ with this source distribution for more information. SPDX-License-Identifier: NCSA """ +from socket import AF_INET, AF_INET6 + import ipaddress import logging @@ -267,6 +269,47 @@ def _clear_ipv4(iface: str): nmsa.unset_param(iface, 'ipv4_mtu') +def _edit_address(session, rpc, node, operation, iface: str, _type): + """Edit an IP address.""" + addr_node = node.find('{'+M_NS+'}ip') + if addr_node is None: + raise error.MissingElementAppError(rpc, node) + address = addr_node.text + + nmsa = get_nmsa() + + if operation in ('delete', 'remove'): + try: + nmsa.remove_address(iface, address) + except KeyError as key_e: + if operation == 'delete': + raise error.DataMissingAppError(rpc) from key_e + log_config_change(session, "[ietf-ip %s]" % iface, + "%s IP %s" % (operation, address)) + return + + pref_node = node.find('{'+M_NS+'}prefix-length') + if pref_node is None: + raise error.MissingElementAppError(rpc, node) + prefix = pref_node.text + + if operation not in ('create', 'merge', 'replace'): + raise error.OperationNotSupportedAppError(rpc) + + try: + nmsa.add_address(iface, _type, address, prefix) + except ValueError as val_e: + raise error.InvalidValueAppError(rpc, info="invalid IP") from val_e + except RuntimeError as run_e: + if operation == 'create': + raise error.DataExistsAppError(rpc) from run_e + else: + nmsa.modify_prefix(iface, address, prefix) + else: + log_config_change(session, "[ietf-ip %s]" % iface, + "%s IP %s/%s" % (operation, address, prefix)) + + def _edit_ipv4(session, rpc, node, def_op, iface: str): """Edit IPv4 configuration for a given interface.""" _params = {'enabled': 'ipv4_enabled', 'forwarding': 'ipv4_forwarding', @@ -292,8 +335,7 @@ def _edit_ipv4(session, rpc, node, def_op, iface: str): "IPv4 %s: -> %s" % (param, xparam.text)) _edit_param(iface, param, operation, rpc, xparam) elif qparam.localname == 'address': - # Oh no. - raise NotImplementedError + _edit_address(session, rpc, xparam, operation, iface, AF_INET) elif qparam.localname == 'neighbor': # Oh *no*! raise NotImplementedError @@ -372,8 +414,7 @@ def _edit_ipv6(session, rpc, node, def_op, iface: str): "IPv6 %s: -> %s" % (param, xparam.text)) _edit_param(iface, param, p_op, rpc, xparam) elif qparam.localname == 'address': - # Oh no. - raise NotImplementedError + _edit_address(session, rpc, xparam, operation, iface, AF_INET6) elif qparam.localname == 'neighbor': # Oh *no*! raise NotImplementedError diff --git a/ncserver/module/nms_ifupdownng.py b/ncserver/module/nms_ifupdownng.py index a607215..76f7c32 100644 --- a/ncserver/module/nms_ifupdownng.py +++ b/ncserver/module/nms_ifupdownng.py @@ -756,21 +756,22 @@ def add_address(iface: str, _type, addr: str, prefix): LOGGER.error(_("unknown address type %r"), _type) return - addr = None - iface = ipaddress.IPv6Interface + ip_addr = None + ctor = ipaddress.IPv6Interface if _type == socket.AF_INET: - iface = ipaddress.IPv4Interface + ctor = ipaddress.IPv4Interface try: - addr = iface("{a}/{p}".format(a=addr, p=prefix)) + ip_addr = ctor("{a}/{p}".format(a=addr, p=prefix)) except Exception as err: raise ValueError("IP address is not valid") from err - s_addr = str(addr) - - if s_addr in list_addresses(iface): - raise RuntimeError("Duplicate address attempt") + for candidate in list_addresses(iface): + cand_addr = candidate.split('/')[0] + if addr == cand_addr: + raise RuntimeError("Duplicate address attempt") + s_addr = str(ip_addr) _add_one_to_list(iface, 'address', s_addr) -- cgit v1.2.3-60-g2f50