# Authors: # Jason Gerard DeRose # Rob Crittenden # # Copyright (C) 2008 Red Hat # see file 'COPYING' for use and warranty information # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ RPC client and shared RPC client/server functionality. This module adds some additional functionality on top of the ``xmlrpclib`` module in the Python standard library. For documentation on the ``xmlrpclib`` module, see: http://docs.python.org/library/xmlrpclib.html Also see the `ipaserver.rpcserver` module. """ from types import NoneType import threading import sys import os import errno import locale from xmlrpclib import Binary, Fault, dumps, loads, ServerProxy, Transport, ProtocolError import kerberos from ipalib.backend import Connectible from ipalib.errors import public_errors, PublicError, UnknownError, NetworkError, KerberosError, XMLRPCMarshallError from ipalib import errors from ipalib.request import context from ipapython import ipautil, dnsclient import httplib import socket from ipapython.nsslib import NSSHTTPS, NSSConnection from nss.error import NSPRError from urllib2 import urlparse # Some Kerberos error definitions from krb5.h KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = (-1765328377L) KRB5KRB_AP_ERR_TKT_EXPIRED = (-1765328352L) KRB5_FCC_PERM = (-1765328190L) KRB5_FCC_NOFILE = (-1765328189L) KRB5_CC_FORMAT = (-1765328185L) KRB5_REALM_CANT_RESOLVE = (-1765328164L) def xml_wrap(value): """ Wrap all ``str`` in ``xmlrpclib.Binary``. Because ``xmlrpclib.dumps()`` will itself convert all ``unicode`` instances into UTF-8 encoded ``str`` instances, we don't do it here. So in total, when encoding data for an XML-RPC packet, the following transformations occur: * All ``str`` instances are treated as binary data and are wrapped in an ``xmlrpclib.Binary()`` instance. * Only ``unicode`` instances are treated as character data. They get converted to UTF-8 encoded ``str`` instances (although as mentioned, not by this function). Also see `xml_unwrap()`. :param value: The simple scalar or simple compound value to wrap. """ if type(value) in (list, tuple): return tuple(xml_wrap(v) for v in value) if type(value) is dict: return dict( (k, xml_wrap(v)) for (k, v) in value.iteritems() ) if type(value) is str: return Binary(value) assert type(value) in (unicode, int, float, bool, NoneType) return value def xml_unwrap(value, encoding='UTF-8'): """ Unwrap all ``xmlrpc.Binary``, decode all ``str`` into ``unicode``. When decoding data from an XML-RPC packet, the following transformations occur: * The binary payloads of all ``xmlrpclib.Binary`` instances are returned as ``str`` instances. * All ``str`` instances are treated as UTF-8 encoded Unicode strings. They are decoded and the resulting ``unicode`` instance is returned. Also see `xml_wrap()`. :param value: The value to unwrap. :param encoding: The Unicode encoding to use (defaults to ``'UTF-8'``). """ if type(value) in (list, tuple): return tuple(xml_unwrap(v, encoding) for v in value) if type(value) is dict: return dict( (k, xml_unwrap(v, encoding)) for (k, v) in value.iteritems() ) if type(value) is str: return value.decode(encoding) if isinstance(value, Binary): assert type(value.data) is str return value.data assert type(value) in (unicode, int, float, bool, NoneType) return value def xml_dumps(params, methodname=None, methodresponse=False, encoding='UTF-8'): """ Encode an XML-RPC data packet, transparently wraping ``params``. This function will wrap ``params`` using `xml_wrap()` and will then encode the XML-RPC data packet using ``xmlrpclib.dumps()`` (from the Python standard library). For documentation on the ``xmlrpclib.dumps()`` function, see: http://docs.python.org/library/xmlrpclib.html#convenience-functions Also see `xml_loads()`. :param params: A ``tuple`` or an ``xmlrpclib.Fault`` instance. :param methodname: The name of the method to call if this is a request. :param methodresponse: Set this to ``True`` if this is a response. :param encoding: The Unicode encoding to use (defaults to ``'UTF-8'``). """ if type(params) is tuple: params = xml_wrap(params) else: assert isinstance(params, Fault) return dumps(params, methodname=methodname, methodresponse=methodresponse, encoding=encoding, allow_none=True, ) def decode_fault(e, encoding='UTF-8'): assert isinstance(e, Fault) if type(e.faultString) is str: return Fault(e.faultCode, e.faultString.decode(encoding)) return e def xml_loads(data, encoding='UTF-8'): """ Decode the XML-RPC packet in ``data``, transparently unwrapping its params. This function will decode the XML-RPC packet in ``data`` using ``xmlrpclib.loads()`` (from the Python standard library). If ``data`` contains a fault, ``xmlrpclib.loads()`` will itself raise an ``xmlrpclib.Fault`` exception. Assuming an exception is not raised, this function will then unwrap the params in ``data`` using `xml_unwrap()`. Finally, a ``(params, methodname)`` tuple is returned containing the unwrapped params and the name of the method being called. If the packet contains no method name, ``methodname`` will be ``None``. For documentation on the ``xmlrpclib.loads()`` function, see: http://docs.python.org/library/xmlrpclib.html#convenience-functions Also see `xml_dumps()`. :param data: The XML-RPC packet to decode. """ try: (params, method) = loads(data) return (xml_unwrap(params), method) except Fault, e: raise decode_fault(e) class LanguageAwareTransport(Transport): """Transport sending Accept-Language header""" def get_host_info(self, host): (host, extra_headers, x509) = Transport.get_host_info(self, host) try: lang = locale.setlocale(locale.LC_ALL, '').split('.')[0].lower() except locale.Error: # fallback to default locale lang = 'en_us' if not isinstance(extra_headers, list): extra_headers = [] extra_headers.append( ('Accept-Language', lang.replace('_', '-')) ) return (host, extra_headers, x509) class SSLTransport(LanguageAwareTransport): """Handles an HTTPS transaction to an XML-RPC server.""" def make_connection(self, host): host, self._extra_headers, x509 = self.get_host_info(host) host, self._extra_headers, x509 = self.get_host_info(host) # Python 2.7 changed the internal class used in xmlrpclib from # HTTP to HTTPConnection. We need to use the proper subclass (major, minor, micro, releaselevel, serial) = sys.version_info if major == 2 and minor < 7: conn = NSSHTTPS(host, 443, dbdir="/etc/pki/nssdb") else: conn = NSSConnection(host, 443, dbdir="/etc/pki/nssdb") conn.connect() return conn class KerbTransport(SSLTransport): """ Handles Kerberos Negotiation authentication to an XML-RPC server. """ def _handle_exception(self, e, service=None): (major, minor) = ipautil.get_gsserror(e) if minor[1] == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: raise errors.ServiceError(service=service) elif minor[1] == KRB5_FCC_NOFILE: raise errors.NoCCacheError() elif minor[1] == KRB5KRB_AP_ERR_TKT_EXPIRED: raise errors.TicketExpired() elif minor[1] == KRB5_FCC_PERM: raise errors.BadCCachePerms() elif minor[1] == KRB5_CC_FORMAT: raise errors.BadCCacheFormat() elif minor[1] == KRB5_REALM_CANT_RESOLVE: raise errors.CannotResolveKDC() else: raise errors.KerberosError(major=major, minor=minor) def get_host_info(self, host): (host, extra_headers, x509) = SSLTransport.get_host_info(self, host) # Set the remote host principal service = "HTTP@" + host.split(':')[0] try: (rc, vc) = kerberos.authGSSClientInit(service, kerberos.GSS_C_DELEG_FLAG | kerberos.GSS_C_MUTUAL_FLAG | kerberos.GSS_C_SEQUENCE_FLAG) except kerberos.GSSError, e: self._handle_exception(e) try: kerberos.authGSSClientStep(vc, "") except kerberos.GSSError, e: self._handle_exception(e, service=service) if not isinstance(extra_headers, list): extra_headers = [] for (h, v) in extra_headers: if h == 'Authorization': extra_headers.remove((h, v)) break extra_headers.append( ('Authorization', 'negotiate %s' % kerberos.authGSSClientResponse(vc)) ) return (host, extra_headers, x509) class xmlclient(Connectible): """ Forwarding backend plugin for XML-RPC client. Also see the `ipaserver.rpcserver.xmlserver` plugin. """ def __init__(self): super(xmlclient, self).__init__() self.__errors = dict((e.errno, e) for e in public_errors) def reconstruct_url(self): """ The URL directly isn't stored in the ServerProxy. We can't store it in the connection object itself but we can reconstruct it from the ServerProxy. """ if not hasattr(self.conn, '_ServerProxy__transport'): return None if isinstance(self.conn._ServerProxy__transport, KerbTransport): scheme = "https" else: scheme = "http" server = '%s://%s%s' % (scheme, self.conn._ServerProxy__host, self.conn._ServerProxy__handler) return server def get_url_list(self): """ Create a list of urls consisting of the available IPA servers. """ # the configured URL defines what we use for the discovered servers (scheme, netloc, path, params, query, fragment) = urlparse.urlparse(self.env.xmlrpc_uri) servers = [] name = '_ldap._tcp.%s.' % self.env.domain rs = dnsclient.query(name, dnsclient.DNS_C_IN, dnsclient.DNS_T_SRV) for r in rs: if r.dns_type == dnsclient.DNS_T_SRV: rsrv = r.rdata.server.rstrip('.') servers.append('https://%s%s' % (rsrv, path)) servers = list(set(servers)) # the list/set conversion won't preserve order so stick in the # local config file version here. servers.insert(0, self.env.xmlrpc_uri) return servers def create_connection(self, ccache=None, verbose=False, fallback=True): servers = self.get_url_list() serverproxy = None for server in servers: kw = dict(allow_none=True, encoding='UTF-8') kw['verbose'] = verbose if server.startswith('https://'): kw['transport'] = KerbTransport() else: kw['transport'] = LanguageAwareTransport() self.log.info('trying %s' % server) serverproxy = ServerProxy(server, **kw) if len(servers) == 1 or not fallback: # if we have only 1 server to try then let the main # requester handle any errors return serverproxy try: command = getattr(serverproxy, 'ping') try: response = command() except Fault, e: e = decode_fault(e) if e.faultCode in self.__errors: error = self.__errors[e.faultCode] raise error(message=e.faultString) else: raise UnknownError( code=e.faultCode, error=e.faultString, server=server, ) # We don't care about the response, just that we got one break except KerberosError, krberr: # kerberos error on one server is likely on all raise errors.KerberosError(major=str(krberr), minor='') except Exception, e: if not fallback: raise e serverproxy = None if serverproxy is None: raise NetworkError(uri='any of the configured servers', error=', '.join(servers)) return serverproxy def destroy_connection(self): pass def forward(self, name, *args, **kw): """ Forward call to command named ``name`` over XML-RPC. This method will encode and forward an XML-RPC request, and will then decode and return the corresponding XML-RPC response. :param command: The name of the command being forwarded. :param args: Positional arguments to pass to remote command. :param kw: Keyword arguments to pass to remote command. """ if name not in self.Command: raise ValueError( '%s.forward(): %r not in api.Command' % (self.name, name) ) server = self.reconstruct_url() self.info('Forwarding %r to server %r', name, server) command = getattr(self.conn, name) params = [args, kw] try: response = command(*xml_wrap(params)) return xml_unwrap(response) except Fault, e: e = decode_fault(e) self.debug('Caught fault %d from server %s: %s', e.faultCode, server, e.faultString) if e.faultCode in self.__errors: error = self.__errors[e.faultCode] raise error(message=e.faultString) raise UnknownError( code=e.faultCode, error=e.faultString, server=server, ) except NSPRError, e: raise NetworkError(uri=server, error=str(e)) except ProtocolError, e: raise NetworkError(uri=server, error=e.errmsg) except socket.error, e: raise NetworkError(uri=server, error=str(e)) except (OverflowError, TypeError), e: raise XMLRPCMarshallError(error=str(e)) 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
#!/bin/sh
#
# network-functions-ipv6
#
# Taken from:
# (P) & (C) 1997-2001 by Peter Bieringer <pb@bieringer.de>
#
# Version: 2001-03-03b
#
# Extended address detection is enabled, if 'ipv6calc' is installed
#  Available here: http://www.bieringer.de/linux/IPv6/tools/index.html#ipv6calc
#
# Known bugs:
#  sit0 will not be shutdowned, if an additional IPv6 address was manually added to this device
#

# Filter tags (for stripping, empty lines following here if all is stripped)




# Return values
#  0 = ok
#  1 = error occurs
#  2 = not enabled, i.e. no IPv6 kernel support or switched off by configuration

##### Test for "ipv6calc" (can be used for better duplicate address detection)
EXISTS_ipv6calc=no	
	if which ipv6calc >/dev/null 2>&1; then
		EXISTS_ipv6calc=yes
	else
		true
	fi


##### Test for IPv6 capabilites

function test_ipv6()
{

	# Test for IPv6 enabled kernel

	if ! [ -f /proc/net/if_inet6 ]; then
		modprobe ipv6
	
		if ! [ -f /proc/net/if_inet6 ]; then
				echo $"Kernel is not compiled with IPv6 support"
			return 2
		fi
	fi



	return 0
}

##### Control IPv6 forwarding
# Display usage
function forwarding_ipv6_usage() {
	echo $"Usage: $0 yes|no [device]"
}


# Control IPv6 forwarding
#  $1: control [yes|no|on|off]
#  $2: network device (if not given, global IPv6 forwarding is set)
function forwarding_ipv6() {
	fw_control=$1
	fw_device=$2		# maybe empty

	if [ -z "$fw_control" ]; then
		echo $"Missing parameter 'forwarding control'"
		forwarding_ipv6_usage
		return 1
	fi

	if ! [ "$fw_control" = "yes" -o "$fw_control" = "no" -o "$fw_control" = "on" -o "$fw_control" = "off" ]; then
		echo $"Don't understand forwarding control parameter '$fw_control'"
		forwarding_ipv6_usage
		return 1
	fi
	
	# Device "lo" need no IPv6 configuration
	if [ "$fw_device" = "lo" ]; then
		return 0;
	fi

	# Run IPv6 test
	test_ipv6 || return    


	if [ "$fw_control" = "yes" -o "$fw_control" = "on" ]; then
		status=1
	else
		status=0
	fi

	# Global control? (if no device is given)
	if [ -z "$fw_device" ]; then
			sysctl -w net.ipv6.conf.all.forwarding=$status >/dev/null 2>&1
	fi
	
	# Per device control
	if [ ! -z "$fw_device" ]; then
			sysctl -w net.ipv6.conf.$fw_device.forwarding=$status >/dev/null 2>&1
	fi
	
}


##### Static IPv6 route configuration

# Display usage
function ifupdown_ipv6_route_usage() {
	echo $"Usage: $0 IPv6-network IPv6-gateway [device]"
}

# Set static IPv6 route
#  $1: IPv6 network to route
#  $2: IPv6 gateway over which $1 should be routed
#  $3: Interface (optional)
function ifup_ipv6_route() {
	networkipv6=$1
	gatewayipv6=$2
	device=$3		# maybe empty

	if [ -z "$networkipv6" ]; then
		echo $"Missing parameter 'IPv6-network'"
		ifupdown_ipv6_route_usage
		return 1
	fi

	if [ -z "$gatewayipv6" ]; then
		echo $"Missing parameter 'IPv6-gateway'"
		ifupdown_ipv6_route_usage
		return 1
	fi

	# Device "lo" need no IPv6 configuration
	if [ "$device" = "lo" ]; then
		return 0;
	fi
	
	# Run IPv6 test
	test_ipv6 || return    

	
	if [ -z "$device" ]; then
			output="`LC_ALL=C route -A inet6 add $networkipv6 gw $gatewayipv6 2>&1`"
			if [ $? -ne 0 ]; then
				if echo $output | grep -i -q 'SIOCADDRT: File exists';  then
					true
				else
					echo $output
				fi
			fi
	else
			output="`LC_ALL=C route -A inet6 add $networkipv6 gw $gatewayipv6 dev $device 2>&1`"
			if [ $? -ne 0 ]; then
				if echo $output | grep -i -q 'SIOCADDRT: File exists';  then
					true
				else
					echo $output
				fi
			fi
	fi

}

# Delete static IPv6 route
#  $1: IPv6 network to route
#  $2: IPv6 gateway over which $1 should be routed
#  $3: Interface (optional)
function ifdown_ipv6_route() {
	networkipv6=$1
	gatewayipv6=$2
	device=$3		# maybe empty

	if [ -z "$networkipv6" ]; then
		echo $"Missing parameter 'IPv6-network'"
		ifup_ipv6_route_usage
		return 1
	fi

	if [ -z "$gatewayipv6" ]; then
		echo $"Missing parameter 'IPv6-gateway'"
		ifup_ipv6_route_usage
		return 1
	fi

	# Device "lo" need no IPv6 configuration
	if [ "$device" = "lo" ]; then
		return 0;
	fi

	# Run IPv6 test
	test_ipv6 || return    

	
	if [ -z "$device" ]; then
			output="`LC_ALL=C route -A inet6 del $networkipv6 gw $gatewayipv6 2>&1`"
			if [ $? -ne 0 ]; then
				if echo $output | grep -i -q 'SIOCDELRT: No such process'; then
					true
				else
					echo $output
				fi
			fi
	else
			output="`LC_ALL=C route -A inet6 del $networkipv6 gw $gatewayipv6 dev $device 2>&1`"
			if [ $? -ne 0 ]; then
				if echo $output | grep -i -q 'SIOCDELRT: No such process'; then
					true
				else
					echo $output
				fi
			fi
	fi

}


##### automatic tunneling configuration

## Configure automatic tunneling up
function ifup_ipv6_autotunnel() {
	
	# Run IPv6 test
	test_ipv6 || return    


	# enable IPv6-over-IPv4 tunnels
	if LC_ALL=C ifconfig sit0 | grep -q "UP "; then
		# already up, do nothing
		true
	else
		# basic tunnel device to up
			ifconfig sit0 up

			# Switch on forwarding
			forwarding_ipv6 on sit0
	fi
	
}


## Configure automatic tunneling down
function ifdown_ipv6_autotunnel() {

	# Run IPv6 test
	test_ipv6 || return    


	if LC_ALL=C ifconfig sit0 | grep -q "UP "; then
		# still up?

		# disable IPv6-over-IPv4 tunnels (if a tunnel is no longer up)
		if LC_ALL=C route -n -A inet6 -n | grep sit0 | awk '{ print $2 }' | grep -v -q "^::$"; then
			# still existing routes, skip shutdown of sit0
			true
		elif LC_ALL=C ifconfig sit0 | grep 'inet6 addr:' | awk '{ print $3 }' | grep -v -q '^::'; then
			# still existing IPv6 addresses, skip shutdown of sit0
			true
		else
			# basic tunnel device to down
				# Switch off forwarding 
				forwarding_ipv6 off sit0
			
				ifconfig sit0 down
		fi
	fi

}	


##### static tunneling configuration

function ifupdown_ipv6_tunnel_usage() {
	echo $"Usage: $0 interfacename IPv4-tunneladdress IPv6-route"
}


## Configure static tunnels up
#  $1: Interface (not needed - dummy)
#  $2: IPv4 address of foreign tunnel
#  $3: IPv6 route through this tunnel
function ifup_ipv6_tunnel() {
	device=$1
	addressipv4tunnel=$2
	routeipv6=$3

	if [ -z "$device" ]; then
		echo $"Missing parameter 'device'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	if [ -z "$addressipv4tunnel" ]; then
		echo $"Missing parameter 'IPv4-tunneladdress'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	if [ -z "$routeipv6" ]; then
		echo $"Missing parameter 'IPv6-route'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi
	
	# Run IPv6 test
	test_ipv6 || return    


	# enable general IPv6-over-IPv4 tunneling
	ifup_ipv6_autotunnel
	
    	# Set up a tunnel
		output="`LC_ALL=C route -A inet6 add $routeipv6 gw ::$addressipv4tunnel dev sit0 2>&1`"
		if [ $? -ne 0 ]; then
			if echo $output | grep -i -q 'SIOCADDRT: File exists';  then
				true
			else
				echo $output
			fi
		fi

}	


## Configure static tunnels down
#  $1: Interface (not used - dummy)
#  $2: IPv4 address of foreign tunnel
#  $3: IPv6 route through this tunnel
function ifdown_ipv6_tunnel() {
	device=$1
	addressipv4tunnel=$2
	routeipv6=$3

	if [ -z "$device" ]; then
		echo $"Missing parameter 'device'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	if [ -z "$addressipv4tunnel" ]; then
		echo $"Missing parameter 'IPv4-tunneladdress'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	if [ -z "$routeipv6" ]; then
		echo $"Missing parameter 'IPv6-route'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	# Run IPv6 test
	test_ipv6 || return    


		# Set up a tunnel
		output="`LC_ALL=C route -A inet6 del $routeipv6 gw ::$addressipv4tunnel dev sit0 2>&1`"
		if [ $? -ne 0 ]; then
			if echo $output | grep -i -q 'SIOCDELRT: No such process'; then
				true
			else
				echo $output
			fi
		fi

	# disable IPv6-over-IPv4 tunneling (if no longer a tunnel is up)
	ifdown_ipv6_autotunnel

}	


## Remove all IPv6 tunnels for a given tunnel endpoint
#  $1: Interface (not used - dummy)
#  $2: IPv4-tunneladdress 
function ifdown_ipv6_tunnel_all() {
	idtuall_device=$1
	idtuall_tunnel=$2

	if [ -z "$idtuall_device" ]; then
		echo $"Missing parameter 'device'"
		echo $"Usage: ifdown_ipv6_tunnel_all interfacename IPv4-tunneladdress"
		return 1
	fi

	if [ -z "$idtuall_tunnel" ]; then
		echo $"Missing parameter 'IPv4-tunneladdress'"
		echo $"Usage: ifdown_ipv6_tunnel_all interfacename IPv4-tunneladdress"
		return 1
	fi
	# Get all IPv6 routes through given interface and remove them
	LC_ALL=C route -n -A inet6 | grep "::$idtuall_tunnel" | while read ipv6net nexthop flags metric ref use iface args; do
		if [ "::$idtuall_tunnel" = "$nexthop" ]; then
			if echo $flags | grep -v -q "A"; then
				# Only non addrconf (automatic installed) routes should be removed
				ifdown_ipv6_tunnel $idtuall_device $idtuall_tunnel $ipv6net
			fi
		fi
	done
}


##### Test, whether an IPv6 address exists on an interface
# $1: Device for testing
# $2: Address to test (without prefix)
# $3: Prefix of address $1
# return values: 1:problem, 10:not exists, 11:exits
function test_ipv6_addrs_exists () {
	testdevice=$1
	testaddr=$2
	testprefix=$3

	if [ -z "$testaddr" ]; then
		echo $"Missing parameter 'IPv6AddrToTest'"
		return 1
	fi


	if [ "$EXISTS_ipv6calc" = "yes" ]; then
		# Using ipv6calc and compare against /proc/net/if_inet6

		convertresult="`LC_ALL=C ipv6calc --addr2if_inet6 $testaddr/$testprefix`"
		# Split in address, scope and prefix length
		test_addr="`echo $convertresult | awk '{ print $1 }'`"
		test_scope="`echo $convertresult | awk '{ print $2 }'`"
		test_prefixlength="`echo $convertresult | awk '{ print $3 }'`"

		if [ -z "$test_prefixlength" ]; then
			testresult="`grep "$test_addr .. .. $test_scope .." /proc/net/if_inet6 | grep $testdevice$`"
		else
			testresult="`grep "$test_addr .. $test_prefixlength $test_scope .." /proc/net/if_inet6 | grep $testdevice$`"
		fi
		if [ ! -z "$testresult" ]; then
			return 11
		else
			return 10
		fi
	else
		# low budget version, only works if given address is in equal form like ifconfig displays
		testresult="`LC_ALL=C ifconfig $testdevice | grep "inet6 addr:" | grep -i ": $testaddr/$testprefix" | awk '{ print $3 }'`"
		if [ ! -z "$testresult" ]; then
			return 11
		else
			return 10
		fi
	fi
}

##### Interface configuration
function ifupdown_ipv6_usage() {
	echo $"Usage: $0 interfacename IPv6-address/IPv6-prefixlength"
}

## Add an IPv6 address for given interface
#  $1: Interface 
#  $2: IPv6 address
function ifup_ipv6_real() {
	device=$1
	address=$2

	if [ -z "$device" ]; then
		echo $"Missing parameter 'device'"
		ifupdown_ipv6_usage
		return 1
	fi

   	# Device "lo" need no IPv6 configuration
   	if [ "$device" = "lo" ]; then
		return 0;
	fi

	if [ -z "$address" ]; then
		echo $"Missing parameter 'IPv6-address'"
		ifupdown_ipv6_usage
		return 1
	fi

	# Test status of interface
	if LC_ALL=C ifconfig $device | grep -q "UP "; then
		# Interface is up
			true
	else
		# no IPv4 for this interface, interface is still down, do up ...
		ifconfig $device up
	fi

	# Extract address parts
	prefixlength_implicit="`echo $address | awk -F/ '{ print $2 }'`"
	address_implicit="`echo $address | awk -F/ '{ print $1 }'`"

	# Test for prefix length
	if [ -z "$prefixlength_implicit" ]; then
		echo $"Missing 'prefix length' for given address"
		ifupdown_ipv6_usage
		return 1
	elif [ $prefixlength_implicit -lt 0 -o $prefixlength_implicit -gt 128 ]; then
		echo $"'prefix length' on given address is out of range (0-128)"
		ifupdown_ipv6_usage
		return 1
	fi

	# Run IPv6 test
	test_ipv6 || return    


	# Only add, if address do not already exist
	test_ipv6_addrs_exists $device $address_implicit $prefixlength_implicit
	retval=$?
	if [ $retval -lt 10 ]; then
		return 2
	fi

	if [ $retval -eq 11 ]; then
		true
	else
			ifconfig $device add $address || return 2
	fi

}


## Remove all IPv6 routes and addresses for given interface
#   cleanup to prevent kernel crashes
#  $1: Interface 
function ifdown_ipv6_real_all() {
	idall_device=$1

	if [ -z "$idall_device" ]; then
		echo $"Missing parameter 'device'"
		echo $"Usage: ifdown_ipv6_real_all interfacename"
		return 1
	fi

	# Get all IPv6 routes through given interface and remove them
	LC_ALL=C route -n -A inet6 | grep $idall_device | while read ipv6net nexthop flags metric ref use iface args; do
		if [ "$idall_device" = "$iface" ]; then
			if echo $flags | grep -v -q "A"; then
				# Only non addrconf (automatic installed) routes should be removed
				ifdown_ipv6_route $ipv6net $nexthop $iface
			fi
		fi
	done
	
	# Get all IPv6 addresses assigned to given interface and remove them
	if [ "$EXISTS_ipv6calc" = "yes" ]; then
		grep $idall_device$ /proc/net/if_inet6 | while read hexaddr dummy1 hexprefixlenth hexscope device args; do
			if [ "$hexscope" != "20" ]; then
				ipv6addr="`ipv6calc --if_inet62addr $hexaddr $hexprefixlenth`"
				ifdown_ipv6_real $idall_device $ipv6addr
			fi
		done
	else
		LC_ALL=C ifconfig $idall_device | grep "inet6 addr:" | while read dummy1 dummy2 ipv6addr scope args; do
			if [ "$scope" != "Scope:Link" ]; then
				ifdown_ipv6_real $idall_device $ipv6addr
			fi
		done
	fi
}

## Remove an IPv6 address on given interface
#  $1: Interface 
#  $2: IPv6 address
function ifdown_ipv6_real() {
	device=$1
	address=$2

	if [ -z "$device" ]; then
		echo $"Missing parameter 'device'"
		ifupdown_ipv6_usage
		return 1
	fi

	# Device "lo" need no IPv6 configuration
	if [ "$device" = "lo" ]; then
		return 0;
	fi

	if [ -z "$address" ]; then
		echo $"Missing parameter 'IPv6-address'"
		ifupdown_ipv6_usage
		return 1
	fi

	# Extract address parts
	prefixlength_implicit="`echo $address | awk -F/ '{ print $2 }'`"
	address_implicit="`echo $address | awk -F/ '{ print $1 }'`"

	# Test for prefix length
	if [ -z "$prefixlength_implicit" ]; then
		echo $"Missing 'prefix length' for given address"
		ifupdown_ipv6_usage
		return 1
	elif [ $prefixlength_implicit -lt 0 -o $prefixlength_implicit -gt 128 ]; then
		echo $"'prefix length' on given address is out of range (0-128)"
		ifupdown_ipv6_usage
		return 1
	fi

	# Run IPv6 test
	test_ipv6 || return    


	# Only remove, if address exists and is not link-local (prevents from kernel crashing)
	test_ipv6_addrs_exists $device $address_implicit $prefixlength_implicit
	retval=$?
	if [ $retval -lt 10 ]; then
		return 2
	fi

	if [ $retval -eq 11 ]; then
			ifconfig $device del $address || return 2
	else
		true
	fi
    
}