1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 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 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
|
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# author: Andrea Mayer <andrea.mayer@uniroma2.it>
#
# This script is designed for testing the support of NEXT-C-SID flavor for SRv6
# End behavior.
# A basic knowledge of SRv6 architecture [1] and of the compressed SID approach
# [2] is assumed for the reader.
#
# The network topology used in the selftest is depicted hereafter, composed by
# two hosts and four routers. Hosts hs-1 and hs-2 are connected through an
# IPv4/IPv6 L3 VPN service, offered by routers rt-1, rt-2, rt-3 and rt-4 using
# the NEXT-C-SID flavor. The key components for such VPNs are:
#
# i) The SRv6 H.Encaps/H.Encaps.Red behaviors [1] apply SRv6 Policies on
# traffic received by connected hosts, initiating the VPN tunnel;
#
# ii) The SRv6 End behavior [1] advances the active SID in the SID List
# carried by the SRH;
#
# iii) The NEXT-C-SID mechanism [2] offers the possibility of encoding several
# SRv6 segments within a single 128-bit SID address, referred to as a
# Compressed SID (C-SID) container. In this way, the length of the SID
# List can be drastically reduced.
# The NEXT-C-SID is provided as a "flavor" of the SRv6 End behavior
# which advances the current C-SID (i.e. the Locator-Node Function defined
# in [2]) with the next one carried in the Argument, if available.
# When no more C-SIDs are available in the Argument, the SRv6 End behavior
# will apply the End function selecting the next SID in the SID List.
#
# iv) The SRv6 End.DT46 behavior [1] is used for removing the SRv6 Policy and,
# thus, it terminates the VPN tunnel. Such a behavior is capable of
# handling, at the same time, both tunneled IPv4 and IPv6 traffic.
#
# [1] https://datatracker.ietf.org/doc/html/rfc8986
# [2] https://datatracker.ietf.org/doc/html/draft-ietf-spring-srv6-srh-compression
#
#
# cafe::1 cafe::2
# 10.0.0.1 10.0.0.2
# +--------+ +--------+
# | | | |
# | hs-1 | | hs-2 |
# | | | |
# +---+----+ +----+---+
# cafe::/64 | | cafe::/64
# 10.0.0.0/24 | | 10.0.0.0/24
# +---+----+ +----+---+
# | | fcf0:0:1:2::/64 | |
# | rt-1 +-------------------+ rt-2 |
# | | | |
# +---+----+ +----+---+
# | . . |
# | fcf0:0:1:3::/64 . |
# | . . |
# | . . |
# fcf0:0:1:4::/64 | . | fcf0:0:2:3::/64
# | . . |
# | . . |
# | fcf0:0:2:4::/64 . |
# | . . |
# +---+----+ +----+---+
# | | | |
# | rt-4 +-------------------+ rt-3 |
# | | fcf0:0:3:4::/64 | |
# +---+----+ +----+---+
#
# Every fcf0:0:x:y::/64 network interconnects the SRv6 routers rt-x with rt-y in
# the selftest network.
#
# Local SID/C-SID table
# =====================
#
# Each SRv6 router is configured with a Local SID/C-SID table in which
# SIDs/C-SIDs are stored. Considering an SRv6 router rt-x, SIDs/C-SIDs are
# configured in the Local SID/C-SIDs table as follows:
#
# Local SID/C-SID table for SRv6 router rt-x
# +-----------------------------------------------------------+
# |fcff:x::d46 is associated with the non-compressed SRv6 |
# | End.DT46 behavior |
# +-----------------------------------------------------------+
# |fcbb:0:0x00::/48 is associated with the NEXT-C-SID flavor |
# | of SRv6 End behavior |
# +-----------------------------------------------------------+
# |fcbb:0:0x00:d46::/64 is associated with the SRv6 End.DT46 |
# | behavior when NEXT-C-SID compression is turned on |
# +-----------------------------------------------------------+
#
# The fcff::/16 prefix is reserved for implementing SRv6 services with regular
# (non compressed) SIDs. Reachability of SIDs is ensured by proper configuration
# of the IPv6 routing tables in the routers.
# Similarly, the fcbb:0::/32 prefix is reserved for implementing SRv6 VPN
# services leveraging the NEXT-C-SID compression mechanism. Indeed, the
# fcbb:0::/32 is used for encoding the Locator-Block while the Locator-Node
# Function is encoded with 16 bits.
#
# Incoming traffic classification and application of SRv6 Policies
# ================================================================
#
# An SRv6 ingress router applies different SRv6 Policies to the traffic received
# from a connected host, considering the IPv4 or IPv6 destination address.
# SRv6 policy enforcement consists of encapsulating the received traffic into a
# new IPv6 packet with a given SID List contained in the SRH.
# When the SID List contains only one SID, the SRH could be omitted completely
# and that SID is stored directly in the IPv6 Destination Address (DA) (this is
# called "reduced" encapsulation).
#
# Test cases for NEXT-C-SID
# =========================
#
# We consider two test cases for NEXT-C-SID: i) single SID and ii) double SID.
#
# In the single SID test case we have a number of segments that are all
# contained in a single Compressed SID (C-SID) container. Therefore the
# resulting SID List has only one SID. Using the reduced encapsulation format
# this will result in a packet with no SRH.
#
# In the double SID test case we have one segment carried in a Compressed SID
# (C-SID) container, followed by a regular (non compressed) SID. The resulting
# SID List has two segments and it is possible to test the advance to the next
# SID when all the C-SIDs in a C-SID container have been processed. Using the
# reduced encapsulation format this will result in a packet with an SRH
# containing 1 segment.
#
# For the single SID test case, we use the IPv4 addresses of hs-1 and hs-2, for
# the double SID test case, we use their IPv6 addresses. This is only done to
# simplify the test setup and avoid adding other hosts or multiple addresses on
# the same interface of a host.
#
# Traffic from hs-1 to hs-2
# -------------------------
#
# Packets generated from hs-1 and directed towards hs-2 are handled by rt-1
# which applies the SRv6 Policies as follows:
#
# i) IPv6 DA=cafe::2, H.Encaps.Red with SID List=fcbb:0:0400:0300:0200:d46::
# ii) IPv4 DA=10.0.0.2, H.Encaps.Red with SID List=fcbb:0:0300::,fcff:2::d46
#
# ### i) single SID
#
# The router rt-1 is configured to enforce the given Policy through the SRv6
# H.Encaps.Red behavior which avoids the presence of the SRH at all, since it
# pushes the single SID directly in the IPv6 DA. Such a SID encodes a whole
# C-SID container carrying several C-SIDs (e.g. 0400, 0300, etc).
#
# As the packet reaches the router rt-4, the enabled NEXT-C-SID SRv6 End
# behavior (associated with fcbb:0:0400::/48) is triggered. This behavior
# analyzes the IPv6 DA and checks whether the Argument of the C-SID container
# is zero or not. In this case, the Argument is *NOT* zero and the IPv6 DA is
# updated as follows:
#
# +---------------------------------------------------------------+
# | Before applying the rt-4 enabled NEXT-C-SID SRv6 End behavior |
# +---------------------------------------------------------------+
# | +---------- Argument |
# | vvvvvvvvvvvvvvvv |
# | IPv6 DA fcbb:0:0400:0300:0200:d46:: |
# | ^^^^ <-- shifting |
# | | |
# | Locator-Node Function |
# +---------------------------------------------------------------+
# | After applying the rt-4 enabled NEXT-C-SID SRv6 End behavior |
# +---------------------------------------------------------------+
# | +---------- Argument |
# | vvvvvvvvvvvv |
# | IPv6 DA fcbb:0:0300:0200:d46:: |
# | ^^^^ |
# | | |
# | Locator-Node Function |
# +---------------------------------------------------------------+
#
# After having applied the enabled NEXT-C-SID SRv6 End behavior, the packet is
# sent to the next node, i.e. rt-3.
#
# The enabled NEXT-C-SID SRv6 End behavior on rt-3 is executed as the packet is
# received. This behavior processes the packet and updates the IPv6 DA with
# fcbb:0:0200:d46::, since the Argument is *NOT* zero. Then, the packet is sent
# to the router rt-2.
#
# The router rt-2 is configured for decapsulating the inner IPv6 packet and,
# for this reason, it applies the SRv6 End.DT46 behavior on the received
# packet. It is worth noting that the SRv6 End.DT46 behavior does not require
# the presence of the SRH: it is fully capable to operate properly on
# IPv4/IPv6-in-IPv6 encapsulations.
# At the end of the decap operation, the packet is sent to the
# host hs-2.
#
# ### ii) double SID
#
# The router rt-1 is configured to enforce the given Policy through the SRv6
# H.Encaps.Red. As a result, the first SID fcbb:0:0300:: is stored into the
# IPv6 DA, while the SRH pushed into the packet is made of only one SID, i.e.
# fcff:2::d46. Hence, the packet sent by hs-1 to hs-2 is encapsulated in an
# outer IPv6 header plus the SRH.
#
# As the packet reaches the node rt-3, the router applies the enabled NEXT-C-SID
# SRv6 End behavior.
#
# +---------------------------------------------------------------+
# | Before applying the rt-3 enabled NEXT-C-SID SRv6 End behavior |
# +---------------------------------------------------------------+
# | +---------- Argument |
# | vvvv (Argument is all filled with zeros) |
# | IPv6 DA fcbb:0:0300:: |
# | ^^^^ |
# | | |
# | Locator-Node Function |
# +---------------------------------------------------------------+
# | After applying the rt-3 enabled NEXT-C-SID SRv6 End behavior |
# +---------------------------------------------------------------+
# | |
# | IPv6 DA fcff:2::d46 |
# | ^^^^^^^^^^^ |
# | | |
# | SID copied from the SID List contained in the SRH |
# +---------------------------------------------------------------+
#
# Since the Argument of the C-SID container is zero, the behavior can not
# update the Locator-Node function with the next C-SID carried in the Argument
# itself. Thus, the enabled NEXT-C-SID SRv6 End behavior operates as the
# traditional End behavior: it updates the IPv6 DA by copying the next
# available SID in the SID List carried by the SRH. After that, the packet is
# sent to the node rt-2.
#
# Once the packet is received by rt-2, the router decapsulates the inner IPv6
# packet using the SRv6 End.DT46 behavior (associated with the SID fcff:2::d46)
# and sends it to the host hs-2.
#
# Traffic from hs-2 to hs-1
# -------------------------
#
# Packets generated from hs-2 and directed towards hs-1 are handled by rt-2
# which applies the SRv6 Policies as follows:
#
# i) IPv6 DA=cafe::1, SID List=fcbb:0:0300:0400:0100:d46::
# ii) IPv4 DA=10.0.0.1, SID List=fcbb:0:0300::,fcff:1::d46
#
# For simplicity, such SRv6 Policies were chosen so that, in both use cases (i)
# and (ii), the network paths crossed by traffic from hs-2 to hs-1 are the same
# as those taken by traffic from hs-1 to hs-2.
# In this way, traffic from hs-2 to hs-1 is processed similarly to traffic from
# hs-1 to hs-2. So, the traffic processing scheme turns out to be the same as
# that adopted in the use cases already examined (of course, it is necessary to
# consider the different SIDs/C-SIDs).
# Kselftest framework requirement - SKIP code is 4.
readonly ksft_skip=4
readonly RDMSUFF="$(mktemp -u XXXXXXXX)"
readonly DUMMY_DEVNAME="dum0"
readonly VRF_TID=100
readonly VRF_DEVNAME="vrf-${VRF_TID}"
readonly RT2HS_DEVNAME="veth-t${VRF_TID}"
readonly LOCALSID_TABLE_ID=90
readonly IPv6_RT_NETWORK=fcf0:0
readonly IPv6_HS_NETWORK=cafe
readonly IPv4_HS_NETWORK=10.0.0
readonly VPN_LOCATOR_SERVICE=fcff
readonly DT46_FUNC=0d46
readonly HEADEND_ENCAP="encap.red"
# do not add ':' as separator
readonly LCBLOCK_ADDR=fcbb0000
readonly LCBLOCK_BLEN=32
# do not add ':' as separator
readonly LCNODEFUNC_FMT="0%d00"
readonly LCNODEFUNC_BLEN=16
readonly LCBLOCK_NODEFUNC_BLEN=$((LCBLOCK_BLEN + LCNODEFUNC_BLEN))
readonly CSID_CNTR_PREFIX="dead:beaf::/32"
# ID of the router used for testing the C-SID container cfgs
readonly CSID_CNTR_RT_ID_TEST=1
# Routing table used for testing the C-SID container cfgs
readonly CSID_CNTR_RT_TABLE=91
# C-SID container configurations to be tested
#
# An entry of the array is defined as "a,b,c" where:
# - 'a' and 'b' elements represent respectively the Locator-Block length
# (lblen) in bits and the Locator-Node Function length (nflen) in bits.
# 'a' and 'b' can be set to default values using the placeholder "d" which
# indicates the default kernel values (32 for lblen and 16 for nflen);
# otherwise, any numeric value is accepted;
# - 'c' indicates whether the C-SID configuration provided by the values 'a'
# and 'b' should be considered valid ("y") or invalid ("n").
declare -ra CSID_CONTAINER_CFGS=(
"d,d,y"
"d,16,y"
"16,d,y"
"16,32,y"
"32,16,y"
"48,8,y"
"8,48,y"
"d,0,n"
"0,d,n"
"32,0,n"
"0,32,n"
"17,d,n"
"d,17,n"
"120,16,n"
"16,120,n"
"0,128,n"
"128,0,n"
"130,0,n"
"0,130,n"
"0,0,n"
)
PING_TIMEOUT_SEC=4
PAUSE_ON_FAIL=${PAUSE_ON_FAIL:=no}
# IDs of routers and hosts are initialized during the setup of the testing
# network
ROUTERS=''
HOSTS=''
SETUP_ERR=1
ret=${ksft_skip}
nsuccess=0
nfail=0
log_test()
{
local rc="$1"
local expected="$2"
local msg="$3"
if [ "${rc}" -eq "${expected}" ]; then
nsuccess=$((nsuccess+1))
printf "\n TEST: %-60s [ OK ]\n" "${msg}"
else
ret=1
nfail=$((nfail+1))
printf "\n TEST: %-60s [FAIL]\n" "${msg}"
if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
echo
echo "hit enter to continue, 'q' to quit"
read a
[ "$a" = "q" ] && exit 1
fi
fi
}
print_log_test_results()
{
printf "\nTests passed: %3d\n" "${nsuccess}"
printf "Tests failed: %3d\n" "${nfail}"
# when a test fails, the value of 'ret' is set to 1 (error code).
# Conversely, when all tests are passed successfully, the 'ret' value
# is set to 0 (success code).
if [ "${ret}" -ne 1 ]; then
ret=0
fi
}
log_section()
{
echo
echo "################################################################################"
echo "TEST SECTION: $*"
echo "################################################################################"
}
test_command_or_ksft_skip()
{
local cmd="$1"
if [ ! -x "$(command -v "${cmd}")" ]; then
echo "SKIP: Could not run test without \"${cmd}\" tool";
exit "${ksft_skip}"
fi
}
get_nodename()
{
local name="$1"
echo "${name}-${RDMSUFF}"
}
get_rtname()
{
local rtid="$1"
get_nodename "rt-${rtid}"
}
get_hsname()
{
local hsid="$1"
get_nodename "hs-${hsid}"
}
__create_namespace()
{
local name="$1"
ip netns add "${name}"
}
create_router()
{
local rtid="$1"
local nsname
nsname="$(get_rtname "${rtid}")"
__create_namespace "${nsname}"
}
create_host()
{
local hsid="$1"
local nsname
nsname="$(get_hsname "${hsid}")"
__create_namespace "${nsname}"
}
cleanup()
{
local nsname
local i
# destroy routers
for i in ${ROUTERS}; do
nsname="$(get_rtname "${i}")"
ip netns del "${nsname}" &>/dev/null || true
done
# destroy hosts
for i in ${HOSTS}; do
nsname="$(get_hsname "${i}")"
ip netns del "${nsname}" &>/dev/null || true
done
# check whether the setup phase was completed successfully or not. In
# case of an error during the setup phase of the testing environment,
# the selftest is considered as "skipped".
if [ "${SETUP_ERR}" -ne 0 ]; then
echo "SKIP: Setting up the testing environment failed"
exit "${ksft_skip}"
fi
exit "${ret}"
}
add_link_rt_pairs()
{
local rt="$1"
local rt_neighs="$2"
local neigh
local nsname
local neigh_nsname
nsname="$(get_rtname "${rt}")"
for neigh in ${rt_neighs}; do
neigh_nsname="$(get_rtname "${neigh}")"
ip link add "veth-rt-${rt}-${neigh}" netns "${nsname}" \
type veth peer name "veth-rt-${neigh}-${rt}" \
netns "${neigh_nsname}"
done
}
get_network_prefix()
{
local rt="$1"
local neigh="$2"
local p="${rt}"
local q="${neigh}"
if [ "${p}" -gt "${q}" ]; then
p="${q}"; q="${rt}"
fi
echo "${IPv6_RT_NETWORK}:${p}:${q}"
}
# Setup the basic networking for the routers
setup_rt_networking()
{
local rt="$1"
local rt_neighs="$2"
local nsname
local net_prefix
local devname
local neigh
nsname="$(get_rtname "${rt}")"
for neigh in ${rt_neighs}; do
devname="veth-rt-${rt}-${neigh}"
net_prefix="$(get_network_prefix "${rt}" "${neigh}")"
ip -netns "${nsname}" addr \
add "${net_prefix}::${rt}/64" dev "${devname}" nodad
ip -netns "${nsname}" link set "${devname}" up
done
ip -netns "${nsname}" link add "${DUMMY_DEVNAME}" type dummy
ip -netns "${nsname}" link set "${DUMMY_DEVNAME}" up
ip -netns "${nsname}" link set lo up
ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
ip netns exec "${nsname}" sysctl -wq net.ipv6.conf.all.forwarding=1
ip netns exec "${nsname}" sysctl -wq net.ipv4.conf.all.rp_filter=0
ip netns exec "${nsname}" sysctl -wq net.ipv4.conf.default.rp_filter=0
ip netns exec "${nsname}" sysctl -wq net.ipv4.ip_forward=1
}
# build an ipv6 prefix/address based on the input string
# Note that the input string does not contain ':' and '::' which are considered
# to be implicit.
# e.g.:
# - input: fbcc00000400300
# - output: fbcc:0000:0400:0300:0000:0000:0000:0000
# ^^^^^^^^^^^^^^^^^^^
# fill the address with 0s
build_ipv6_addr()
{
local addr="$1"
local out=""
local strlen="${#addr}"
local padn
local i
# add ":" every 4 digits (16 bits)
for (( i = 0; i < strlen; i++ )); do
if (( i > 0 && i < 32 && (i % 4) == 0 )); then
out="${out}:"
fi
out="${out}${addr:$i:1}"
done
# fill the remaining bits of the address with 0s
padn=$((32 - strlen))
for (( i = padn; i > 0; i-- )); do
if (( i > 0 && i < 32 && (i % 4) == 0 )); then
out="${out}:"
fi
out="${out}0"
done
printf "${out}"
}
build_csid()
{
local nodeid="$1"
printf "${LCNODEFUNC_FMT}" "${nodeid}"
}
build_lcnode_func_prefix()
{
local nodeid="$1"
local lcnodefunc
local prefix
local out
lcnodefunc="$(build_csid "${nodeid}")"
prefix="$(build_ipv6_addr "${LCBLOCK_ADDR}${lcnodefunc}")"
out="${prefix}/${LCBLOCK_NODEFUNC_BLEN}"
echo "${out}"
}
# Setup local SIDs for an SRv6 router
setup_rt_local_sids()
{
local rt="$1"
local rt_neighs="$2"
local net_prefix
local devname
local nsname
local neigh
local lcnode_func_prefix
local lcblock_prefix
nsname="$(get_rtname "${rt}")"
for neigh in ${rt_neighs}; do
devname="veth-rt-${rt}-${neigh}"
net_prefix="$(get_network_prefix "${rt}" "${neigh}")"
# set underlay network routes for SIDs reachability
ip -netns "${nsname}" -6 route \
add "${VPN_LOCATOR_SERVICE}:${neigh}::/32" \
table "${LOCALSID_TABLE_ID}" \
via "${net_prefix}::${neigh}" dev "${devname}"
# set the underlay network for C-SIDs reachability
lcnode_func_prefix="$(build_lcnode_func_prefix "${neigh}")"
ip -netns "${nsname}" -6 route \
add "${lcnode_func_prefix}" \
table "${LOCALSID_TABLE_ID}" \
via "${net_prefix}::${neigh}" dev "${devname}"
done
lcnode_func_prefix="$(build_lcnode_func_prefix "${rt}")"
# enabled NEXT-C-SID SRv6 End behavior (note that "dev" is the dummy
# dum0 device chosen for the sake of simplicity).
ip -netns "${nsname}" -6 route \
add "${lcnode_func_prefix}" \
table "${LOCALSID_TABLE_ID}" \
encap seg6local action End flavors next-csid \
lblen "${LCBLOCK_BLEN}" nflen "${LCNODEFUNC_BLEN}" \
dev "${DUMMY_DEVNAME}"
# all SIDs for VPNs start with a common locator. Routes and SRv6
# Endpoint behavior instaces are grouped together in the 'localsid'
# table.
ip -netns "${nsname}" -6 rule \
add to "${VPN_LOCATOR_SERVICE}::/16" \
lookup "${LOCALSID_TABLE_ID}" prio 999
# common locator block for NEXT-C-SIDS compression mechanism.
lcblock_prefix="$(build_ipv6_addr "${LCBLOCK_ADDR}")"
ip -netns "${nsname}" -6 rule \
add to "${lcblock_prefix}/${LCBLOCK_BLEN}" \
lookup "${LOCALSID_TABLE_ID}" prio 999
}
# build and install the SRv6 policy into the ingress SRv6 router as well as the
# decap SID in the egress one.
# args:
# $1 - src host (evaluate automatically the ingress router)
# $2 - dst host (evaluate automatically the egress router)
# $3 - SRv6 routers configured for steering traffic (End behaviors)
# $4 - single SID or double SID
# $5 - traffic type (IPv6 or IPv4)
__setup_l3vpn()
{
local src="$1"
local dst="$2"
local end_rts="$3"
local mode="$4"
local traffic="$5"
local nsname
local policy
local container
local decapsid
local lcnfunc
local dt
local n
local rtsrc_nsname
local rtdst_nsname
rtsrc_nsname="$(get_rtname "${src}")"
rtdst_nsname="$(get_rtname "${dst}")"
container="${LCBLOCK_ADDR}"
# build first SID (C-SID container)
for n in ${end_rts}; do
lcnfunc="$(build_csid "${n}")"
container="${container}${lcnfunc}"
done
if [ "${mode}" -eq 1 ]; then
# single SID policy
dt="$(build_csid "${dst}")${DT46_FUNC}"
container="${container}${dt}"
# build the full ipv6 address for the container
policy="$(build_ipv6_addr "${container}")"
# build the decap SID used in the decap node
container="${LCBLOCK_ADDR}${dt}"
decapsid="$(build_ipv6_addr "${container}")"
else
# double SID policy
decapsid="${VPN_LOCATOR_SERVICE}:${dst}::${DT46_FUNC}"
policy="$(build_ipv6_addr "${container}"),${decapsid}"
fi
# apply encap policy
if [ "${traffic}" -eq 6 ]; then
ip -netns "${rtsrc_nsname}" -6 route \
add "${IPv6_HS_NETWORK}::${dst}" vrf "${VRF_DEVNAME}" \
encap seg6 mode "${HEADEND_ENCAP}" segs "${policy}" \
dev "${VRF_DEVNAME}"
ip -netns "${rtsrc_nsname}" -6 neigh \
add proxy "${IPv6_HS_NETWORK}::${dst}" \
dev "${RT2HS_DEVNAME}"
else
# "dev" must be different from the one where the packet is
# received, otherwise the proxy arp does not work.
ip -netns "${rtsrc_nsname}" -4 route \
add "${IPv4_HS_NETWORK}.${dst}" vrf "${VRF_DEVNAME}" \
encap seg6 mode "${HEADEND_ENCAP}" segs "${policy}" \
dev "${VRF_DEVNAME}"
fi
# apply decap
# Local End.DT46 behavior (decap)
ip -netns "${rtdst_nsname}" -6 route \
add "${decapsid}" \
table "${LOCALSID_TABLE_ID}" \
encap seg6local action End.DT46 vrftable "${VRF_TID}" \
dev "${VRF_DEVNAME}"
}
# see __setup_l3vpn()
setup_ipv4_vpn_2sids()
{
__setup_l3vpn "$1" "$2" "$3" 2 4
}
# see __setup_l3vpn()
setup_ipv6_vpn_1sid()
{
__setup_l3vpn "$1" "$2" "$3" 1 6
}
setup_hs()
{
local hs="$1"
local rt="$2"
local hsname
local rtname
hsname="$(get_hsname "${hs}")"
rtname="$(get_rtname "${rt}")"
ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.all.accept_dad=0
ip netns exec "${hsname}" sysctl -wq net.ipv6.conf.default.accept_dad=0
ip -netns "${hsname}" link add veth0 type veth \
peer name "${RT2HS_DEVNAME}" netns "${rtname}"
ip -netns "${hsname}" addr \
add "${IPv6_HS_NETWORK}::${hs}/64" dev veth0 nodad
ip -netns "${hsname}" addr add "${IPv4_HS_NETWORK}.${hs}/24" dev veth0
ip -netns "${hsname}" link set veth0 up
ip -netns "${hsname}" link set lo up
# configure the VRF on the router which is directly connected to the
# source host.
ip -netns "${rtname}" link \
add "${VRF_DEVNAME}" type vrf table "${VRF_TID}"
ip -netns "${rtname}" link set "${VRF_DEVNAME}" up
# enslave the veth interface connecting the router with the host to the
# VRF in the access router
ip -netns "${rtname}" link \
set "${RT2HS_DEVNAME}" master "${VRF_DEVNAME}"
# set default routes to unreachable for both ipv6 and ipv4
ip -netns "${rtname}" -6 route \
add unreachable default metric 4278198272 \
vrf "${VRF_DEVNAME}"
ip -netns "${rtname}" -4 route \
add unreachable default metric 4278198272 \
vrf "${VRF_DEVNAME}"
ip -netns "${rtname}" addr \
add "${IPv6_HS_NETWORK}::254/64" dev "${RT2HS_DEVNAME}" nodad
ip -netns "${rtname}" addr \
add "${IPv4_HS_NETWORK}.254/24" dev "${RT2HS_DEVNAME}"
ip -netns "${rtname}" link set "${RT2HS_DEVNAME}" up
ip netns exec "${rtname}" \
sysctl -wq net.ipv6.conf."${RT2HS_DEVNAME}".proxy_ndp=1
ip netns exec "${rtname}" \
sysctl -wq net.ipv4.conf."${RT2HS_DEVNAME}".proxy_arp=1
# disable the rp_filter otherwise the kernel gets confused about how
# to route decap ipv4 packets.
ip netns exec "${rtname}" \
sysctl -wq net.ipv4.conf."${RT2HS_DEVNAME}".rp_filter=0
ip netns exec "${rtname}" sh -c "echo 1 > /proc/sys/net/vrf/strict_mode"
}
setup()
{
local i
# create routers
ROUTERS="1 2 3 4"; readonly ROUTERS
for i in ${ROUTERS}; do
create_router "${i}"
done
# create hosts
HOSTS="1 2"; readonly HOSTS
for i in ${HOSTS}; do
create_host "${i}"
done
# set up the links for connecting routers
add_link_rt_pairs 1 "2 3 4"
add_link_rt_pairs 2 "3 4"
add_link_rt_pairs 3 "4"
# set up the basic connectivity of routers and routes required for
# reachability of SIDs.
setup_rt_networking 1 "2 3 4"
setup_rt_networking 2 "1 3 4"
setup_rt_networking 3 "1 2 4"
setup_rt_networking 4 "1 2 3"
# set up the hosts connected to routers
setup_hs 1 1
setup_hs 2 2
# set up default SRv6 Endpoints (i.e. SRv6 End and SRv6 End.DT46)
setup_rt_local_sids 1 "2 3 4"
setup_rt_local_sids 2 "1 3 4"
setup_rt_local_sids 3 "1 2 4"
setup_rt_local_sids 4 "1 2 3"
# set up SRv6 Policies
# create an IPv6 VPN between hosts hs-1 and hs-2.
#
# Direction hs-1 -> hs-2
# - rt-1 encap (H.Encaps.Red)
# - rt-4 SRv6 End behavior (NEXT-C-SID flavor)
# - rt-3 SRv6 End behavior (NEXT-C-SID flavor)
# - rt-2 SRv6 End.DT46 behavior
setup_ipv6_vpn_1sid 1 2 "4 3"
# Direction hs2 -> hs-1
# - rt-2 encap (H.Encaps.Red)
# - rt-3 SRv6 End behavior (NEXT-C-SID flavor)
# - rt-4 SRv6 End behavior (NEXT-C-SID flavor)
# - rt-1 SRv6 End.DT46 behavior
setup_ipv6_vpn_1sid 2 1 "3 4"
# create an IPv4 VPN between hosts hs-1 and hs-2
#
# Direction hs-1 -> hs-2
# - rt-1 encap (H.Encaps.Red)
# - rt-3 SRv6 End behavior (NEXT-C-SID flavor)
# - rt-2 SRv6 End.DT46 behavior
setup_ipv4_vpn_2sids 1 2 "3"
# Direction hs-2 -> hs-1
# - rt-2 encap (H.Encaps.Red)
# - rt-3 SRv6 End behavior (NEXT-C-SID flavor)
# - rt-1 SRv6 End.DT46 behavior
setup_ipv4_vpn_2sids 2 1 "3"
# testing environment was set up successfully
SETUP_ERR=0
}
check_rt_connectivity()
{
local rtsrc="$1"
local rtdst="$2"
local prefix
local rtsrc_nsname
rtsrc_nsname="$(get_rtname "${rtsrc}")"
prefix="$(get_network_prefix "${rtsrc}" "${rtdst}")"
ip netns exec "${rtsrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
"${prefix}::${rtdst}" >/dev/null 2>&1
}
check_and_log_rt_connectivity()
{
local rtsrc="$1"
local rtdst="$2"
check_rt_connectivity "${rtsrc}" "${rtdst}"
log_test $? 0 "Routers connectivity: rt-${rtsrc} -> rt-${rtdst}"
}
check_hs_ipv6_connectivity()
{
local hssrc="$1"
local hsdst="$2"
local hssrc_nsname
hssrc_nsname="$(get_hsname "${hssrc}")"
ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
"${IPv6_HS_NETWORK}::${hsdst}" >/dev/null 2>&1
}
check_hs_ipv4_connectivity()
{
local hssrc="$1"
local hsdst="$2"
local hssrc_nsname
hssrc_nsname="$(get_hsname "${hssrc}")"
ip netns exec "${hssrc_nsname}" ping -c 1 -W "${PING_TIMEOUT_SEC}" \
"${IPv4_HS_NETWORK}.${hsdst}" >/dev/null 2>&1
}
check_and_log_hs2gw_connectivity()
{
local hssrc="$1"
check_hs_ipv6_connectivity "${hssrc}" 254
log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> gw"
check_hs_ipv4_connectivity "${hssrc}" 254
log_test $? 0 "IPv4 Hosts connectivity: hs-${hssrc} -> gw"
}
check_and_log_hs_ipv6_connectivity()
{
local hssrc="$1"
local hsdst="$2"
check_hs_ipv6_connectivity "${hssrc}" "${hsdst}"
log_test $? 0 "IPv6 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
}
check_and_log_hs_ipv4_connectivity()
{
local hssrc="$1"
local hsdst="$2"
check_hs_ipv4_connectivity "${hssrc}" "${hsdst}"
log_test $? 0 "IPv4 Hosts connectivity: hs-${hssrc} -> hs-${hsdst}"
}
router_tests()
{
local i
local j
log_section "IPv6 routers connectivity test"
for i in ${ROUTERS}; do
for j in ${ROUTERS}; do
if [ "${i}" -eq "${j}" ]; then
continue
fi
check_and_log_rt_connectivity "${i}" "${j}"
done
done
}
host2gateway_tests()
{
local hs
log_section "IPv4/IPv6 connectivity test among hosts and gateways"
for hs in ${HOSTS}; do
check_and_log_hs2gw_connectivity "${hs}"
done
}
host_vpn_tests()
{
log_section "SRv6 VPN connectivity test hosts (h1 <-> h2, IPv6)"
check_and_log_hs_ipv6_connectivity 1 2
check_and_log_hs_ipv6_connectivity 2 1
log_section "SRv6 VPN connectivity test hosts (h1 <-> h2, IPv4)"
check_and_log_hs_ipv4_connectivity 1 2
check_and_log_hs_ipv4_connectivity 2 1
}
__nextcsid_end_behavior_test()
{
local nsname="$1"
local cmd="$2"
local blen="$3"
local flen="$4"
local layout=""
if [ "${blen}" != "d" ]; then
layout="${layout} lblen ${blen}"
fi
if [ "${flen}" != "d" ]; then
layout="${layout} nflen ${flen}"
fi
ip -netns "${nsname}" -6 route \
"${cmd}" "${CSID_CNTR_PREFIX}" \
table "${CSID_CNTR_RT_TABLE}" \
encap seg6local action End flavors next-csid ${layout} \
dev "${DUMMY_DEVNAME}" &>/dev/null
return "$?"
}
rt_x_nextcsid_end_behavior_test()
{
local rt="$1"
local blen="$2"
local flen="$3"
local nsname
local ret
nsname="$(get_rtname "${rt}")"
__nextcsid_end_behavior_test "${nsname}" "add" "${blen}" "${flen}"
ret="$?"
__nextcsid_end_behavior_test "${nsname}" "del" "${blen}" "${flen}"
return "${ret}"
}
__parse_csid_container_cfg()
{
local cfg="$1"
local index="$2"
local out
echo "${cfg}" | cut -d',' -f"${index}"
}
csid_container_cfg_tests()
{
local valid
local blen
local flen
local cfg
local ret
log_section "C-SID Container config tests (legend: d='kernel default')"
for cfg in "${CSID_CONTAINER_CFGS[@]}"; do
blen="$(__parse_csid_container_cfg "${cfg}" 1)"
flen="$(__parse_csid_container_cfg "${cfg}" 2)"
valid="$(__parse_csid_container_cfg "${cfg}" 3)"
rt_x_nextcsid_end_behavior_test \
"${CSID_CNTR_RT_ID_TEST}" \
"${blen}" \
"${flen}"
ret="$?"
if [ "${valid}" == "y" ]; then
log_test "${ret}" 0 \
"Accept valid C-SID container cfg (lblen=${blen}, nflen=${flen})"
else
log_test "${ret}" 2 \
"Reject invalid C-SID container cfg (lblen=${blen}, nflen=${flen})"
fi
done
}
test_iproute2_supp_or_ksft_skip()
{
if ! ip route help 2>&1 | grep -qo "next-csid"; then
echo "SKIP: Missing SRv6 NEXT-C-SID flavor support in iproute2"
exit "${ksft_skip}"
fi
}
test_dummy_dev_or_ksft_skip()
{
local test_netns
test_netns="dummy-$(mktemp -u XXXXXXXX)"
if ! ip netns add "${test_netns}"; then
echo "SKIP: Cannot set up netns for testing dummy dev support"
exit "${ksft_skip}"
fi
modprobe dummy &>/dev/null || true
if ! ip -netns "${test_netns}" link \
add "${DUMMY_DEVNAME}" type dummy; then
echo "SKIP: dummy dev not supported"
ip netns del "${test_netns}"
exit "${ksft_skip}"
fi
ip netns del "${test_netns}"
}
test_vrf_or_ksft_skip()
{
modprobe vrf &>/dev/null || true
if [ ! -e /proc/sys/net/vrf/strict_mode ]; then
echo "SKIP: vrf sysctl does not exist"
exit "${ksft_skip}"
fi
}
if [ "$(id -u)" -ne 0 ]; then
echo "SKIP: Need root privileges"
exit "${ksft_skip}"
fi
# required programs to carry out this selftest
test_command_or_ksft_skip ip
test_command_or_ksft_skip ping
test_command_or_ksft_skip sysctl
test_command_or_ksft_skip grep
test_command_or_ksft_skip cut
test_iproute2_supp_or_ksft_skip
test_dummy_dev_or_ksft_skip
test_vrf_or_ksft_skip
set -e
trap cleanup EXIT
setup
set +e
csid_container_cfg_tests
router_tests
host2gateway_tests
host_vpn_tests
print_log_test_results
|