#!/bin/bash # SPDX-License-Identifier: GPL-2.0 # # Testing For SCTP COLLISION SCENARIO as Below: # # 14:35:47.655279 IP 198.51.200.1.1234 > 198.51.100.1.1234: sctp (1) [INIT] [init tag: 2017837359] # 14:35:48.353250 IP 198.51.100.1.1234 > 198.51.200.1.1234: sctp (1) [INIT] [init tag: 1187206187] # 14:35:48.353275 IP 198.51.200.1.1234 > 198.51.100.1.1234: sctp (1) [INIT ACK] [init tag: 2017837359] # 14:35:48.353283 IP 198.51.100.1.1234 > 198.51.200.1.1234: sctp (1) [COOKIE ECHO] # 14:35:48.353977 IP 198.51.200.1.1234 > 198.51.100.1.1234: sctp (1) [COOKIE ACK] # 14:35:48.855335 IP 198.51.100.1.1234 > 198.51.200.1.1234: sctp (1) [INIT ACK] [init tag: 164579970] # # TOPO: oamcm (link0)<--->(link1) HOST/ROUTER (link2)<--->(link3) oambb # include the c test file in this script cat > ./sctp_test.c << EOF #include #include #include #include #include int main(int argc, char *argv[]) { struct sockaddr_in saddr = {}, daddr = {}; int sd, ret, len = sizeof(daddr); struct timeval tv = {25, 0}; char buf[] = "hello"; if (argc != 6 || (strcmp(argv[1], "server") && strcmp(argv[1], "client"))) { printf("%s \n", argv[0]); return -1; } sd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); if (sd < 0) { printf("Failed to create sd\n"); return -1; } saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = inet_addr(argv[2]); saddr.sin_port = htons(atoi(argv[3])); ret = bind(sd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { printf("Failed to bind to address\n"); return -1; } ret = listen(sd, 5); if (ret < 0) { printf("Failed to listen on port\n"); return -1; } daddr.sin_family = AF_INET; daddr.sin_addr.s_addr = inet_addr(argv[4]); daddr.sin_port = htons(atoi(argv[5])); /* make test shorter than 25s */ ret = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); if (ret < 0) { printf("Failed to setsockopt SO_RCVTIMEO\n"); return -1; } if (!strcmp(argv[1], "server")) { sleep(1); /* wait a bit for client's INIT */ ret = connect(sd, (struct sockaddr *)&daddr, len); if (ret < 0) { printf("Failed to connect to peer\n"); return -1; } ret = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&daddr, &len); if (ret < 0) { printf("Failed to recv msg %d\n", ret); return -1; } ret = sendto(sd, buf, strlen(buf) + 1, 0, (struct sockaddr *)&daddr, len); if (ret < 0) { printf("Failed to send msg %d\n", ret); return -1; } printf("Server: sent! %d\n", ret); } if (!strcmp(argv[1], "client")) { usleep(300000); /* wait a bit for server's listening */ ret = connect(sd, (struct sockaddr *)&daddr, len); if (ret < 0) { printf("Failed to connect to peer\n"); return -1; } sleep(1); /* wait a bit for server's delayed INIT_ACK to reproduce the issue */ ret = sendto(sd, buf, strlen(buf) + 1, 0, (struct sockaddr *)&daddr, len); if (ret < 0) { printf("Failed to send msg %d\n", ret); return -1; } ret = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&daddr, &len); if (ret < 0) { printf("Failed to recv msg %d\n", ret); return -1; } printf("Client: rcvd! %d\n", ret); } close(sd); return 0; } EOF if ! conntrack -V; then echo "SKIP: Could not run test without conntrack-tools, try # dnf install -y conntrack-tools" exit 4 fi if ! make sctp_test; then echo "SKIP: Failed to compile sctp_test.c" exit 4 fi # clean up ip netns del oamcm > /dev/null 2>&1 ip netns del oambb > /dev/null 2>&1 ip link del link1 > /dev/null 2>&1 ip link del link2 > /dev/null 2>&1 conntrack -D -p sctp > /dev/null 2>&1 iptables -F # setup the topo ip netns add oamcm ip netns add oambb ip link add link1 type veth peer name link0 netns oamcm ip link add link2 type veth peer name link3 netns oambb ip -n oamcm link set link0 up ip -n oamcm addr add 198.51.100.1/24 dev link0 ip -n oamcm route add 198.51.200.1 dev link0 via 198.51.100.2 ip link set link1 up ip link set link2 up ip addr add 198.51.100.2/24 dev link1 ip addr add 198.51.200.2/24 dev link2 sysctl -wq net.ipv4.ip_forward=1 ip -n oambb link set link3 up ip -n oambb addr add 198.51.200.1/24 dev link3 ip -n oambb route add 198.51.100.1 dev link3 via 198.51.200.2 # simulate the delay on OVS upcall by setting up a delay for INIT_ACK with tc on oamcm side tc -n oamcm qdisc add dev link0 root handle 1: htb tc -n oamcm class add dev link0 parent 1: classid 1:1 htb rate 100mbit tc -n oamcm filter add dev link0 parent 1: protocol ip u32 match ip protocol 132 0xff match u8 2 0xff at 32 flowid 1:1 tc -n oamcm qdisc add dev link0 parent 1:1 handle 10: netem delay 1200ms # simulate the ctstate check on OVS nf_conntrack iptables -A FORWARD -m state --state INVALID,UNTRACKED -j DROP iptables -A INPUT -p sctp -j DROP # use a smaller number for assoc's max_retrans to reproduce the issue modprobe sctp ip netns exec oambb sysctl -wq net.sctp.association_max_retrans=3 # NOTE: one way to work around the issue is set a smaller hb_interval # ip netns exec oambb sysctl -wq net.sctp.hb_interval=3500 # run the test case echo "Test Started (wait up to 25s):" ip net exec oamcm ./sctp_test server 198.51.100.1 1234 198.51.200.1 1234 & ip net exec oambb ./sctp_test client 198.51.200.1 1234 198.51.100.1 1234 && echo "PASS!" && exit 0 echo "FAIL!" && exit 1