Commit c5cf13fb authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ipv4-address-protocol'



Petr Machata says:

====================
net: Allow changing IPv4 address protocol

IPv4 and IPv6 addresses can be assigned a protocol value that indicates the
provenance of the IP address. The attribute is modeled after ip route
protocols, and essentially allows the administrator or userspace stack to
tag addresses in some way that makes sense to the actor in question.

When IP address protocol field was added in commit 47f0bd50 ("net: Add
new protocol attribute to IP addresses"), the semantics included the
ability to change the protocol for IPv6 addresses, but not for IPv4
addresses. It seems this was not deliberate, but rather by accident.

One particular use case is tagging the addresses differently depending on
whether the routing stack should advertise them or not. Without support for
protocol replacement, this can not be done.

In this patchset, extend IPv4 to allow changing the protocol defined at an
address (in patch #1). Then in patches #2 and #3 add selftest coverage for
ip address protocols.

Currently the kernel simply ignores the new value. Thus allowing the
replacement changes the observable behavior. However, since IPv6 already
behaves like this anyway, and since the feature as such is relatively new,
it seems like the change is safe to make.

An example session with the feature in action:

	bash-5.2# ip address add dev d 192.0.2.1/28 proto 0xab
	bash-5.2# ip address show dev d
	4: d: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
	    link/ether 06:29:74:fd:1f:eb brd ff:ff:ff:ff:ff:ff
	    inet 192.0.2.1/28 scope global proto 0xab d
	       valid_lft forever preferred_lft forever

	bash-5.2# ip address replace dev d 192.0.2.1/28 proto 0x11
	bash-5.2# ip address show dev d
	4: d: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
	    link/ether 06:29:74:fd:1f:eb brd ff:ff:ff:ff:ff:ff
	    inet 192.0.2.1/28 scope global proto 0x11 d
	       valid_lft forever preferred_lft forever
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 90bf6610 6a414fd7
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -962,6 +962,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
					 extack);
	} else {
		u32 new_metric = ifa->ifa_rt_priority;
		u8 new_proto = ifa->ifa_proto;

		inet_free_ifa(ifa);

@@ -975,6 +976,8 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
			ifa->ifa_rt_priority = new_metric;
		}

		ifa->ifa_proto = new_proto;

		set_ifa_lifetime(ifa, valid_lft, prefered_lft);
		cancel_delayed_work(&check_lifetime_work);
		queue_delayed_work(system_power_efficient_wq,
+131 −34
Original line number Diff line number Diff line
@@ -4,6 +4,31 @@
#
# set -e

ALL_TESTS="
	kci_test_polrouting
	kci_test_route_get
	kci_test_addrlft
	kci_test_promote_secondaries
	kci_test_tc
	kci_test_gre
	kci_test_gretap
	kci_test_ip6gretap
	kci_test_erspan
	kci_test_ip6erspan
	kci_test_bridge
	kci_test_addrlabel
	kci_test_ifalias
	kci_test_vrf
	kci_test_encap
	kci_test_macsec
	kci_test_ipsec
	kci_test_ipsec_offload
	kci_test_fdb_get
	kci_test_neigh_get
	kci_test_bridge_parent_id
	kci_test_address_proto
"

devdummy="test-dummy0"

# Kselftest framework requirement - SKIP code is 4.
@@ -1225,62 +1250,126 @@ kci_test_bridge_parent_id()
	echo "PASS: bridge_parent_id"
}

kci_test_rtnl()
address_get_proto()
{
	local addr=$1; shift

	ip -N -j address show dev "$devdummy" |
	    jq -e -r --arg addr "${addr%/*}" \
	       '.[].addr_info[] | select(.local == $addr) | .protocol'
}

address_count()
{
	ip -N -j address show dev "$devdummy" "$@" |
	    jq -e -r '[.[].addr_info[] | .local | select(. != null)] | length'
}

do_test_address_proto()
{
	local what=$1; shift
	local addr=$1; shift
	local addr2=${addr%/*}2/${addr#*/}
	local addr3=${addr%/*}3/${addr#*/}
	local proto
	local count
	local ret=0
	kci_add_dummy
	if [ $ret -ne 0 ];then
		echo "FAIL: cannot add dummy interface"
		return 1
	fi
	local err

	kci_test_polrouting
	ip address add dev "$devdummy" "$addr3"
	check_err $?
	kci_test_route_get
	check_err $?
	kci_test_addrlft
	proto=$(address_get_proto "$addr3")
	[[ "$proto" == null ]]
	check_err $?
	kci_test_promote_secondaries
	check_err $?
	kci_test_tc
	check_err $?
	kci_test_gre
	check_err $?
	kci_test_gretap
	check_err $?
	kci_test_ip6gretap
	check_err $?
	kci_test_erspan

	ip address add dev "$devdummy" "$addr2" proto 0x99
	check_err $?
	kci_test_ip6erspan
	proto=$(address_get_proto "$addr2")
	[[ "$proto" == 0x99 ]]
	check_err $?
	kci_test_bridge

	ip address add dev "$devdummy" "$addr" proto 0xab
	check_err $?
	kci_test_addrlabel
	proto=$(address_get_proto "$addr")
	[[ "$proto" == 0xab ]]
	check_err $?
	kci_test_ifalias

	ip address replace dev "$devdummy" "$addr" proto 0x11
	proto=$(address_get_proto "$addr")
	check_err $?
	kci_test_vrf
	[[ "$proto" == 0x11 ]]
	check_err $?
	kci_test_encap

	count=$(address_count)
	check_err $?
	kci_test_macsec
	(( count == 3 )) # $addr, $addr2 and $addr3

	count=$(address_count proto 0)
	check_err $?
	kci_test_ipsec
	(( count == 1 )) # just $addr2

	count=$(address_count proto 0x11)
	check_err $?
	kci_test_ipsec_offload
	(( count == 2 )) # $addr and $addr2

	count=$(address_count proto 0xab)
	check_err $?
	kci_test_fdb_get
	(( count == 1 )) # just $addr2

	ip address del dev "$devdummy" "$addr"
	ip address del dev "$devdummy" "$addr2"
	ip address del dev "$devdummy" "$addr3"

	if [ $ret -ne 0 ]; then
		echo "FAIL: address proto $what"
		return 1
	fi
	echo "PASS: address proto $what"
}

kci_test_address_proto()
{
	local ret=0

	do_test_address_proto IPv4 192.0.2.1/28
	check_err $?
	kci_test_neigh_get

	do_test_address_proto IPv6 2001:db8:1::1/64
	check_err $?
	kci_test_bridge_parent_id

	return $ret
}

kci_test_rtnl()
{
	local current_test
	local ret=0

	kci_add_dummy
	if [ $ret -ne 0 ];then
		echo "FAIL: cannot add dummy interface"
		return 1
	fi

	for current_test in ${TESTS:-$ALL_TESTS}; do
		$current_test
		check_err $?
	done

	kci_del_dummy
	return $ret
}

usage()
{
	cat <<EOF
usage: ${0##*/} OPTS

        -t <test>   Test(s) to run (default: all)
                    (options: $(echo $ALL_TESTS))
EOF
}

#check for needed privileges
if [ "$(id -u)" -ne 0 ];then
	echo "SKIP: Need root privileges"
@@ -1295,6 +1384,14 @@ for x in ip tc;do
	fi
done

while getopts t:h o; do
	case $o in
		t) TESTS=$OPTARG;;
		h) usage; exit 0;;
		*) usage; exit 1;;
	esac
done

kci_test_rtnl

exit $?