1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (c) 2014 Red Hat, Inc. 4f08c3bdfSopenharmony_ci * Copyright (c) 2021 Petr Vorel <pvorel@suse.cz> 5f08c3bdfSopenharmony_ci */ 6f08c3bdfSopenharmony_ci 7f08c3bdfSopenharmony_ci/*\ 8f08c3bdfSopenharmony_ci * [Description] 9f08c3bdfSopenharmony_ci * 10f08c3bdfSopenharmony_ci * Tests a netlink interface inside a new network namespace. 11f08c3bdfSopenharmony_ci * 12f08c3bdfSopenharmony_ci * - Unshares a network namespace (so network related actions 13f08c3bdfSopenharmony_ci * have no effect on a real system). 14f08c3bdfSopenharmony_ci * - Forks a child which creates a NETLINK_ROUTE netlink socket 15f08c3bdfSopenharmony_ci * and listens to RTMGRP_LINK (network interface create/delete/up/down) 16f08c3bdfSopenharmony_ci * multicast group. 17f08c3bdfSopenharmony_ci * - Child then waits for parent approval to receive data from socket 18f08c3bdfSopenharmony_ci * - Parent creates a new TAP interface (dummy0) and immediately 19f08c3bdfSopenharmony_ci * removes it (which should generate some data in child's netlink socket). 20f08c3bdfSopenharmony_ci * Then it allows child to continue. 21f08c3bdfSopenharmony_ci * - As the child was listening to RTMGRP_LINK multicast group, it should 22f08c3bdfSopenharmony_ci * detect the new interface creation/deletion (by reading data from netlink 23f08c3bdfSopenharmony_ci * socket), if so, the test passes, otherwise it fails. 24f08c3bdfSopenharmony_ci */ 25f08c3bdfSopenharmony_ci 26f08c3bdfSopenharmony_ci#define _GNU_SOURCE 27f08c3bdfSopenharmony_ci#include <sys/wait.h> 28f08c3bdfSopenharmony_ci#include <asm/types.h> 29f08c3bdfSopenharmony_ci#include <sys/socket.h> 30f08c3bdfSopenharmony_ci#include <linux/netlink.h> 31f08c3bdfSopenharmony_ci#include <linux/rtnetlink.h> 32f08c3bdfSopenharmony_ci#include <unistd.h> 33f08c3bdfSopenharmony_ci#include <stdio.h> 34f08c3bdfSopenharmony_ci#include <stdlib.h> 35f08c3bdfSopenharmony_ci#include <errno.h> 36f08c3bdfSopenharmony_ci 37f08c3bdfSopenharmony_ci#include "tst_test.h" 38f08c3bdfSopenharmony_ci#include "tst_safe_macros.h" 39f08c3bdfSopenharmony_ci#include "lapi/sched.h" 40f08c3bdfSopenharmony_ci 41f08c3bdfSopenharmony_ci#define MAX_TRIES 1000 42f08c3bdfSopenharmony_ci 43f08c3bdfSopenharmony_cistatic void child_func(void) 44f08c3bdfSopenharmony_ci{ 45f08c3bdfSopenharmony_ci int fd, len, event_found, tries; 46f08c3bdfSopenharmony_ci struct sockaddr_nl sa; 47f08c3bdfSopenharmony_ci char buffer[4096]; 48f08c3bdfSopenharmony_ci struct nlmsghdr *nlh; 49f08c3bdfSopenharmony_ci 50f08c3bdfSopenharmony_ci /* child will listen to a network interface create/delete/up/down events */ 51f08c3bdfSopenharmony_ci memset(&sa, 0, sizeof(sa)); 52f08c3bdfSopenharmony_ci sa.nl_family = AF_NETLINK; 53f08c3bdfSopenharmony_ci sa.nl_groups = RTMGRP_LINK; 54f08c3bdfSopenharmony_ci 55f08c3bdfSopenharmony_ci fd = SAFE_SOCKET(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 56f08c3bdfSopenharmony_ci SAFE_BIND(fd, (struct sockaddr *) &sa, sizeof(sa)); 57f08c3bdfSopenharmony_ci 58f08c3bdfSopenharmony_ci /* waits for parent to create an interface */ 59f08c3bdfSopenharmony_ci TST_CHECKPOINT_WAKE_AND_WAIT(0); 60f08c3bdfSopenharmony_ci 61f08c3bdfSopenharmony_ci /* 62f08c3bdfSopenharmony_ci * To get rid of "resource temporarily unavailable" errors 63f08c3bdfSopenharmony_ci * when testing with -i option 64f08c3bdfSopenharmony_ci */ 65f08c3bdfSopenharmony_ci tries = 0; 66f08c3bdfSopenharmony_ci event_found = 0; 67f08c3bdfSopenharmony_ci nlh = (struct nlmsghdr *) buffer; 68f08c3bdfSopenharmony_ci while (tries < MAX_TRIES) { 69f08c3bdfSopenharmony_ci len = recv(fd, nlh, sizeof(buffer), MSG_DONTWAIT); 70f08c3bdfSopenharmony_ci if (len > 0) { 71f08c3bdfSopenharmony_ci /* stop receiving only on interface create/delete event */ 72f08c3bdfSopenharmony_ci if (nlh->nlmsg_type == RTM_NEWLINK || 73f08c3bdfSopenharmony_ci nlh->nlmsg_type == RTM_DELLINK) { 74f08c3bdfSopenharmony_ci event_found++; 75f08c3bdfSopenharmony_ci break; 76f08c3bdfSopenharmony_ci } 77f08c3bdfSopenharmony_ci } 78f08c3bdfSopenharmony_ci usleep(10000); 79f08c3bdfSopenharmony_ci tries++; 80f08c3bdfSopenharmony_ci } 81f08c3bdfSopenharmony_ci 82f08c3bdfSopenharmony_ci SAFE_CLOSE(fd); 83f08c3bdfSopenharmony_ci 84f08c3bdfSopenharmony_ci if (event_found) 85f08c3bdfSopenharmony_ci tst_res(TPASS, "interface changes detected"); 86f08c3bdfSopenharmony_ci else 87f08c3bdfSopenharmony_ci tst_res(TFAIL, "failed to detect interface changes"); 88f08c3bdfSopenharmony_ci 89f08c3bdfSopenharmony_ci exit(0); 90f08c3bdfSopenharmony_ci} 91f08c3bdfSopenharmony_ci 92f08c3bdfSopenharmony_cistatic void test_netns_netlink(void) 93f08c3bdfSopenharmony_ci{ 94f08c3bdfSopenharmony_ci /* unshares the network namespace */ 95f08c3bdfSopenharmony_ci SAFE_UNSHARE(CLONE_NEWNET); 96f08c3bdfSopenharmony_ci 97f08c3bdfSopenharmony_ci if (SAFE_FORK() == 0) 98f08c3bdfSopenharmony_ci child_func(); 99f08c3bdfSopenharmony_ci 100f08c3bdfSopenharmony_ci /* wait until child opens netlink socket */ 101f08c3bdfSopenharmony_ci TST_CHECKPOINT_WAIT(0); 102f08c3bdfSopenharmony_ci 103f08c3bdfSopenharmony_ci /* creates TAP network interface dummy0 */ 104f08c3bdfSopenharmony_ci if (WEXITSTATUS(system("ip tuntap add dev dummy0 mode tap"))) 105f08c3bdfSopenharmony_ci tst_brk(TBROK, "adding interface failed"); 106f08c3bdfSopenharmony_ci 107f08c3bdfSopenharmony_ci /* removes previously created dummy0 device */ 108f08c3bdfSopenharmony_ci if (WEXITSTATUS(system("ip tuntap del mode tap dummy0"))) 109f08c3bdfSopenharmony_ci tst_brk(TBROK, "removing interface failed"); 110f08c3bdfSopenharmony_ci 111f08c3bdfSopenharmony_ci /* allow child to continue */ 112f08c3bdfSopenharmony_ci TST_CHECKPOINT_WAKE(0); 113f08c3bdfSopenharmony_ci 114f08c3bdfSopenharmony_ci tst_reap_children(); 115f08c3bdfSopenharmony_ci} 116f08c3bdfSopenharmony_ci 117f08c3bdfSopenharmony_ci 118f08c3bdfSopenharmony_cistatic struct tst_test test = { 119f08c3bdfSopenharmony_ci .test_all = test_netns_netlink, 120f08c3bdfSopenharmony_ci .needs_checkpoints = 1, 121f08c3bdfSopenharmony_ci .needs_root = 1, 122f08c3bdfSopenharmony_ci .forks_child = 1, 123f08c3bdfSopenharmony_ci .needs_kconfigs = (const char *[]) { 124f08c3bdfSopenharmony_ci "CONFIG_NET_NS=y", 125f08c3bdfSopenharmony_ci "CONFIG_TUN", 126f08c3bdfSopenharmony_ci NULL 127f08c3bdfSopenharmony_ci }, 128f08c3bdfSopenharmony_ci}; 129