From patchwork Tue May 4 15:48:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Doucha X-Patchwork-Id: 1473905 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.linux.it (client-ip=2001:1418:10:5::2; helo=picard.linux.it; envelope-from=ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it; receiver=) Received: from picard.linux.it (picard.linux.it [IPv6:2001:1418:10:5::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4FZPR44rRGz9sPf for ; Wed, 5 May 2021 01:49:20 +1000 (AEST) Received: from picard.linux.it (localhost [IPv6:::1]) by picard.linux.it (Postfix) with ESMTP id 0EE8F3C5868 for ; Tue, 4 May 2021 17:49:18 +0200 (CEST) X-Original-To: ltp@lists.linux.it Delivered-To: ltp@picard.linux.it Received: from in-2.smtp.seeweb.it (in-2.smtp.seeweb.it [IPv6:2001:4b78:1:20::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by picard.linux.it (Postfix) with ESMTPS id A16F33C582A for ; Tue, 4 May 2021 17:48:37 +0200 (CEST) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by in-2.smtp.seeweb.it (Postfix) with ESMTPS id AADD7600A4C for ; Tue, 4 May 2021 17:48:36 +0200 (CEST) Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 450ABAFF8 for ; Tue, 4 May 2021 15:48:36 +0000 (UTC) From: Martin Doucha To: ltp@lists.linux.it Date: Tue, 4 May 2021 17:48:34 +0200 Message-Id: <20210504154835.24881-5-mdoucha@suse.cz> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210504154835.24881-1-mdoucha@suse.cz> References: <20210504154835.24881-1-mdoucha@suse.cz> MIME-Version: 1.0 X-Virus-Scanned: clamav-milter 0.102.4 at in-2.smtp.seeweb.it X-Virus-Status: Clean X-Spam-Status: No, score=0.0 required=7.0 tests=SPF_HELO_NONE,SPF_PASS autolearn=disabled version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on in-2.smtp.seeweb.it Subject: [LTP] [PATCH v2 5/6] Add helper functions for managing network interfaces X-BeenThere: ltp@lists.linux.it X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Test Project List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ltp-bounces+incoming=patchwork.ozlabs.org@lists.linux.it Sender: "ltp" The library currently supports: - creating a virtual ethernet device pair - removing network interfaces - enabling or disabling a network interface - managing interface addresses - managing routing table entries - moving network interfaces between network namespaces Signed-off-by: Martin Doucha --- Changes since v1: - renamed tst_netdevice_index() to tst_netdev_index_by_name() - renamed tst_netdevice_activate() to tst_netdev_set_state() - shortened tst_netdevice_*() to tst_netdev_*() - use inline struct initialization where possible - use SAFE_IOCTL_() - added modify_route_inet() internal helper function Changes I've decided not to do: - moving *_address_inet() and *_route_inet() functions to header file - breaking long lines like if(foo & tst_rtnl_add_attr(...)) before the function call instead of in the argument list, the result would be too long include/tst_netdevice.h | 118 ++++++++++ lib/tst_netdevice.c | 463 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 581 insertions(+) create mode 100644 include/tst_netdevice.h create mode 100644 lib/tst_netdevice.c diff --git a/include/tst_netdevice.h b/include/tst_netdevice.h new file mode 100644 index 000000000..3a6698731 --- /dev/null +++ b/include/tst_netdevice.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (c) 2021 Linux Test Project + */ + +#ifndef TST_NETDEVICE_H +#define TST_NETDEVICE_H + +/* Find device index for given network interface name. */ +int tst_netdev_index_by_name(const char *file, const int lineno, + const char *ifname); +#define NETDEV_INDEX_BY_NAME(ifname) \ + tst_netdev_index_by_name(__FILE__, __LINE__, (ifname)) + +/* Activate or deactivate network interface */ +int tst_netdev_set_state(const char *file, const int lineno, + const char *ifname, int up); +#define NETDEV_SET_STATE(ifname, up) \ + tst_netdev_set_state(__FILE__, __LINE__, (ifname), (up)) + +/* Create a connected pair of virtual network devices */ +int tst_create_veth_pair(const char *file, const int lineno, + const char *ifname1, const char *ifname2); +#define CREATE_VETH_PAIR(ifname1, ifname2) \ + tst_create_veth_pair(__FILE__, __LINE__, (ifname1), (ifname2)) + +int tst_remove_netdev(const char *file, const int lineno, const char *ifname); +#define REMOVE_NETDEV(ifname) tst_remove_netdev(__FILE__, __LINE__, (ifname)) + +int tst_netdev_add_address(const char *file, const int lineno, + const char *ifname, unsigned int family, const void *address, + unsigned int prefix, size_t addrlen, unsigned int flags); +#define NETDEV_ADD_ADDRESS(ifname, family, address, prefix, addrlen, flags) \ + tst_netdev_add_address(__FILE__, __LINE__, (ifname), (family), \ + (address), (prefix), (addrlen), (flags)) + +int tst_netdev_add_address_inet(const char *file, const int lineno, + const char *ifname, in_addr_t address, unsigned int prefix, + unsigned int flags); +#define NETDEV_ADD_ADDRESS_INET(ifname, address, prefix, flags) \ + tst_netdev_add_address_inet(__FILE__, __LINE__, (ifname), (address), \ + (prefix), (flags)) + +int tst_netdev_remove_address(const char *file, const int lineno, + const char *ifname, unsigned int family, const void *address, + size_t addrlen); +#define NETDEV_REMOVE_ADDRESS(ifname, family, address, addrlen) \ + tst_netdev_remove_address(__FILE__, __LINE__, (ifname), (family), \ + (address), (addrlen)) + +int tst_netdev_remove_address_inet(const char *file, const int lineno, + const char *ifname, in_addr_t address); +#define NETDEV_REMOVE_ADDRESS_INET(ifname, address) \ + tst_netdev_remove_address_inet(__FILE__, __LINE__, (ifname), (address)) + +int tst_netdev_change_ns_fd(const char *file, const int lineno, + const char *ifname, int nsfd); +#define NETDEV_CHANGE_NS_FD(ifname, nsfd) \ + tst_netdev_change_ns_fd(__FILE__, __LINE__, (ifname), (nsfd)) + +int tst_netdev_change_ns_pid(const char *file, const int lineno, + const char *ifname, pid_t nspid); +#define NETDEV_CHANGE_NS_PID(ifname, nspid) \ + tst_netdev_change_ns_pid(__FILE__, __LINE__, (ifname), (nspid)) + +/* + * Add new static entry to main routing table. If you specify gateway address, + * the interface name is optional. + */ +int tst_netdev_add_route(const char *file, const int lineno, + const char *ifname, unsigned int family, const void *srcaddr, + unsigned int srcprefix, size_t srclen, const void *dstaddr, + unsigned int dstprefix, size_t dstlen, const void *gateway, + size_t gatewaylen); +#define NETDEV_ADD_ROUTE(ifname, family, srcaddr, srcprefix, srclen, dstaddr, \ + dstprefix, dstlen, gateway, gatewaylen) \ + tst_netdev_add_route(__FILE__, __LINE__, (ifname), (family), \ + (srcaddr), (srcprefix), (srclen), (dstaddr), (dstprefix), \ + (dstlen), (gateway), (gatewaylen)) + +/* + * Simplified function for adding IPv4 static route. If you set srcprefix + * or dstprefix to 0, the corresponding address will be ignored. Interface + * name is optional if gateway address is non-zero. + */ +int tst_netdev_add_route_inet(const char *file, const int lineno, + const char *ifname, in_addr_t srcaddr, unsigned int srcprefix, + in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway); +#define NETDEV_ADD_ROUTE_INET(ifname, srcaddr, srcprefix, dstaddr, dstprefix, \ + gateway) \ + tst_netdev_add_route_inet(__FILE__, __LINE__, (ifname), (srcaddr), \ + (srcprefix), (dstaddr), (dstprefix), (gateway)) + +/* + * Remove static entry from main routing table. + */ +int tst_netdev_remove_route(const char *file, const int lineno, + const char *ifname, unsigned int family, const void *srcaddr, + unsigned int srcprefix, size_t srclen, const void *dstaddr, + unsigned int dstprefix, size_t dstlen, const void *gateway, + size_t gatewaylen); +#define NETDEV_REMOVE_ROUTE(ifname, family, srcaddr, srcprefix, srclen, \ + dstaddr, dstprefix, dstlen, gateway, gatewaylen) \ + tst_netdev_remove_route(__FILE__, __LINE__, (ifname), (family), \ + (srcaddr), (srcprefix), (srclen), (dstaddr), (dstprefix), \ + (dstlen), (gateway), (gatewaylen)) + +/* + * Simplified function for removing IPv4 static route. + */ +int tst_netdev_remove_route_inet(const char *file, const int lineno, + const char *ifname, in_addr_t srcaddr, unsigned int srcprefix, + in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway); +#define NETDEV_REMOVE_ROUTE_INET(ifname, srcaddr, srcprefix, dstaddr, \ + dstprefix, gateway) \ + tst_netdev_remove_route_inet(__FILE__, __LINE__, (ifname), (srcaddr), \ + (srcprefix), (dstaddr), (dstprefix), (gateway)) + +#endif /* TST_NETDEVICE_H */ diff --git a/lib/tst_netdevice.c b/lib/tst_netdevice.c new file mode 100644 index 000000000..93019a140 --- /dev/null +++ b/lib/tst_netdevice.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2021 Linux Test Project + */ + +#include +#include +#include +#include +#include +#include +#define TST_NO_DEFAULT_MAIN +#include "tst_test.h" +#include "tst_rtnetlink.h" +#include "tst_netdevice.h" + +static struct tst_rtnl_context *create_request(const char *file, + const int lineno, unsigned int type, unsigned int flags, + const void *payload, size_t psize) +{ + struct tst_rtnl_context *ctx; + struct nlmsghdr header = { + .nlmsg_type = type, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags, + }; + + ctx = tst_rtnl_create_context(file, lineno); + + if (!ctx) + return NULL; + + if (!tst_rtnl_add_message(file, lineno, ctx, &header, payload, psize)) { + tst_rtnl_destroy_context(file, lineno, ctx); + return NULL; + } + + return ctx; +} + +int tst_netdev_index_by_name(const char *file, const int lineno, + const char *ifname) +{ + struct ifreq ifr; + int sock, ret; + + if (strlen(ifname) >= IFNAMSIZ) { + tst_brk_(file, lineno, TBROK, + "Network device name \"%s\" too long", ifname); + return -1; + } + + sock = safe_socket(file, lineno, NULL, AF_INET, SOCK_DGRAM, 0); + + if (sock < 0) + return -1; + + strcpy(ifr.ifr_name, ifname); + ret = SAFE_IOCTL_(file, lineno, sock, SIOCGIFINDEX, &ifr); + safe_close(file, lineno, NULL, sock); + return ret ? -1 : ifr.ifr_ifindex; +} + +int tst_netdev_set_state(const char *file, const int lineno, + const char *ifname, int up) +{ + struct ifreq ifr; + int sock, ret; + + if (strlen(ifname) >= IFNAMSIZ) { + tst_brk_(file, lineno, TBROK, + "Network device name \"%s\" too long", ifname); + return -1; + } + + sock = safe_socket(file, lineno, NULL, AF_INET, SOCK_DGRAM, 0); + + if (sock < 0) + return -1; + + strcpy(ifr.ifr_name, ifname); + ret = SAFE_IOCTL_(file, lineno, sock, SIOCGIFFLAGS, &ifr); + + if (ret) { + safe_close(file, lineno, NULL, sock); + return ret; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + ret = SAFE_IOCTL_(file, lineno, sock, SIOCSIFFLAGS, &ifr); + safe_close(file, lineno, NULL, sock); + return ret; +} + +int tst_create_veth_pair(const char *file, const int lineno, + const char *ifname1, const char *ifname2) +{ + int ret; + struct ifinfomsg info = { .ifi_family = AF_UNSPEC }; + struct tst_rtnl_context *ctx; + struct tst_rtnl_attr_list peerinfo[] = { + {IFLA_IFNAME, ifname2, strlen(ifname2) + 1, NULL}, + {0, NULL, -1, NULL} + }; + struct tst_rtnl_attr_list peerdata[] = { + {VETH_INFO_PEER, &info, sizeof(info), peerinfo}, + {0, NULL, -1, NULL} + }; + struct tst_rtnl_attr_list attrs[] = { + {IFLA_IFNAME, ifname1, strlen(ifname1) + 1, NULL}, + {IFLA_LINKINFO, NULL, 0, (const struct tst_rtnl_attr_list[]){ + {IFLA_INFO_KIND, "veth", 4, NULL}, + {IFLA_INFO_DATA, NULL, 0, peerdata}, + {0, NULL, -1, NULL} + }}, + {0, NULL, -1, NULL} + }; + + if (strlen(ifname1) >= IFNAMSIZ) { + tst_brk_(file, lineno, TBROK, + "Network device name \"%s\" too long", ifname1); + return 0; + } + + if (strlen(ifname2) >= IFNAMSIZ) { + tst_brk_(file, lineno, TBROK, + "Network device name \"%s\" too long", ifname2); + return 0; + } + + ctx = create_request(file, lineno, RTM_NEWLINK, + NLM_F_CREATE | NLM_F_EXCL, &info, sizeof(info)); + + if (!ctx) + return 0; + + if (tst_rtnl_add_attr_list(file, lineno, ctx, attrs) != 2) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + ret = tst_rtnl_send_validate(file, lineno, ctx); + tst_rtnl_destroy_context(file, lineno, ctx); + + if (!ret) { + tst_brk_(file, lineno, TBROK | TTERRNO, + "Failed to create veth interfaces %s+%s", ifname1, + ifname2); + } + + return ret; +} + +int tst_remove_netdev(const char *file, const int lineno, const char *ifname) +{ + struct ifinfomsg info = { .ifi_family = AF_UNSPEC }; + struct tst_rtnl_context *ctx; + int ret; + + if (strlen(ifname) >= IFNAMSIZ) { + tst_brk_(file, lineno, TBROK, + "Network device name \"%s\" too long", ifname); + return 0; + } + + ctx = create_request(file, lineno, RTM_DELLINK, 0, &info, sizeof(info)); + + if (!ctx) + return 0; + + if (!tst_rtnl_add_attr_string(file, lineno, ctx, IFLA_IFNAME, ifname)) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + ret = tst_rtnl_send_validate(file, lineno, ctx); + tst_rtnl_destroy_context(file, lineno, ctx); + + if (!ret) { + tst_brk_(file, lineno, TBROK | TTERRNO, + "Failed to remove netdevice %s", ifname); + } + + return ret; +} + +static int modify_address(const char *file, const int lineno, + unsigned int action, unsigned int nl_flags, const char *ifname, + unsigned int family, const void *address, unsigned int prefix, + size_t addrlen, uint32_t addr_flags) +{ + struct tst_rtnl_context *ctx; + int index, ret; + struct ifaddrmsg info = { + .ifa_family = family, + .ifa_prefixlen = prefix + }; + + index = tst_netdev_index_by_name(file, lineno, ifname); + + if (index < 0) { + tst_brk_(file, lineno, TBROK, "Interface %s not found", ifname); + return 0; + } + + info.ifa_index = index; + ctx = create_request(file, lineno, action, nl_flags, &info, + sizeof(info)); + + if (!ctx) + return 0; + + if (!tst_rtnl_add_attr(file, lineno, ctx, IFA_FLAGS, &addr_flags, + sizeof(uint32_t))) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + if (!tst_rtnl_add_attr(file, lineno, ctx, IFA_LOCAL, address, + addrlen)) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + ret = tst_rtnl_send_validate(file, lineno, ctx); + tst_rtnl_destroy_context(file, lineno, ctx); + + if (!ret) { + tst_brk_(file, lineno, TBROK | TTERRNO, + "Failed to modify %s network address", ifname); + } + + return ret; +} + +int tst_netdev_add_address(const char *file, const int lineno, + const char *ifname, unsigned int family, const void *address, + unsigned int prefix, size_t addrlen, unsigned int flags) +{ + return modify_address(file, lineno, RTM_NEWADDR, + NLM_F_CREATE | NLM_F_EXCL, ifname, family, address, prefix, + addrlen, flags); +} + +int tst_netdev_add_address_inet(const char *file, const int lineno, + const char *ifname, in_addr_t address, unsigned int prefix, + unsigned int flags) +{ + return tst_netdev_add_address(file, lineno, ifname, AF_INET, + &address, prefix, sizeof(address), flags); +} + +int tst_netdev_remove_address(const char *file, const int lineno, + const char *ifname, unsigned int family, const void *address, + size_t addrlen) +{ + return modify_address(file, lineno, RTM_DELADDR, 0, ifname, family, + address, 0, addrlen, 0); +} + +int tst_netdev_remove_address_inet(const char *file, const int lineno, + const char *ifname, in_addr_t address) +{ + return tst_netdev_remove_address(file, lineno, ifname, AF_INET, + &address, sizeof(address)); +} + +static int change_ns(const char *file, const int lineno, const char *ifname, + unsigned short attr, uint32_t value) +{ + struct ifinfomsg info = { .ifi_family = AF_UNSPEC }; + struct tst_rtnl_context *ctx; + int ret; + + if (strlen(ifname) >= IFNAMSIZ) { + tst_brk_(file, lineno, TBROK, + "Network device name \"%s\" too long", ifname); + return 0; + } + + ctx = create_request(file, lineno, RTM_NEWLINK, 0, &info, sizeof(info)); + + if (!tst_rtnl_add_attr_string(file, lineno, ctx, IFLA_IFNAME, ifname)) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + if (!tst_rtnl_add_attr(file, lineno, ctx, attr, &value, + sizeof(uint32_t))) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + ret = tst_rtnl_send_validate(file, lineno, ctx); + tst_rtnl_destroy_context(file, lineno, ctx); + + if (!ret) { + tst_brk_(file, lineno, TBROK | TTERRNO, + "Failed to move %s to another namespace", ifname); + } + + return ret; +} + +int tst_netdev_change_ns_fd(const char *file, const int lineno, + const char *ifname, int nsfd) +{ + return change_ns(file, lineno, ifname, IFLA_NET_NS_FD, nsfd); +} + +int tst_netdev_change_ns_pid(const char *file, const int lineno, + const char *ifname, pid_t nspid) +{ + return change_ns(file, lineno, ifname, IFLA_NET_NS_PID, nspid); +} + +static int modify_route(const char *file, const int lineno, unsigned int action, + unsigned int flags, const char *ifname, unsigned int family, + const void *srcaddr, unsigned int srcprefix, size_t srclen, + const void *dstaddr, unsigned int dstprefix, size_t dstlen, + const void *gateway, size_t gatewaylen) +{ + struct tst_rtnl_context *ctx; + int ret; + int32_t index; + struct rtmsg info = { + .rtm_family = family, + .rtm_dst_len = dstprefix, + .rtm_src_len = srcprefix, + .rtm_table = RT_TABLE_MAIN, + .rtm_protocol = RTPROT_STATIC, + .rtm_type = RTN_UNICAST + }; + + if (!ifname && !gateway) { + tst_brk_(file, lineno, TBROK, + "Interface name or gateway address required"); + return 0; + } + + if (ifname && strlen(ifname) >= IFNAMSIZ) { + tst_brk_(file, lineno, TBROK, + "Network device name \"%s\" too long", ifname); + return 0; + } + + if (ifname) { + index = tst_netdev_index_by_name(file, lineno, ifname); + + if (index < 0) + return 0; + } + + if (action == RTM_DELROUTE) + info.rtm_scope = RT_SCOPE_NOWHERE; + else + info.rtm_scope = RT_SCOPE_UNIVERSE; + + ctx = create_request(file, lineno, action, flags, &info, sizeof(info)); + + if (srcaddr && !tst_rtnl_add_attr(file, lineno, ctx, RTA_SRC, srcaddr, + srclen)) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + if (dstaddr && !tst_rtnl_add_attr(file, lineno, ctx, RTA_DST, dstaddr, + dstlen)) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + if (gateway && !tst_rtnl_add_attr(file, lineno, ctx, RTA_GATEWAY, + gateway, gatewaylen)) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + if (ifname && !tst_rtnl_add_attr(file, lineno, ctx, RTA_OIF, &index, + sizeof(index))) { + tst_rtnl_destroy_context(file, lineno, ctx); + return 0; + } + + ret = tst_rtnl_send_validate(file, lineno, ctx); + tst_rtnl_destroy_context(file, lineno, ctx); + + if (!ret) { + tst_brk_(file, lineno, TBROK | TTERRNO, + "Failed to modify network route"); + } + + return ret; +} + +static int modify_route_inet(const char *file, const int lineno, + unsigned int action, unsigned int flags, const char *ifname, + in_addr_t srcaddr, unsigned int srcprefix, in_addr_t dstaddr, + unsigned int dstprefix, in_addr_t gateway) +{ + void *src = NULL, *dst = NULL, *gw = NULL; + size_t srclen = 0, dstlen = 0, gwlen = 0; + + if (srcprefix) { + src = &srcaddr; + srclen = sizeof(srcaddr); + } + + if (dstprefix) { + dst = &dstaddr; + dstlen = sizeof(dstaddr); + } + + if (gateway) { + gw = &gateway; + gwlen = sizeof(gateway); + } + + return modify_route(file, lineno, action, flags, ifname, AF_INET, src, + srcprefix, srclen, dst, dstprefix, dstlen, gw, gwlen); +} + +int tst_netdev_add_route(const char *file, const int lineno, + const char *ifname, unsigned int family, const void *srcaddr, + unsigned int srcprefix, size_t srclen, const void *dstaddr, + unsigned int dstprefix, size_t dstlen, const void *gateway, + size_t gatewaylen) +{ + return modify_route(file, lineno, RTM_NEWROUTE, + NLM_F_CREATE | NLM_F_EXCL, ifname, family, srcaddr, srcprefix, + srclen, dstaddr, dstprefix, dstlen, gateway, gatewaylen); +} + +int tst_netdev_add_route_inet(const char *file, const int lineno, + const char *ifname, in_addr_t srcaddr, unsigned int srcprefix, + in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway) +{ + return modify_route_inet(file, lineno, RTM_NEWROUTE, + NLM_F_CREATE | NLM_F_EXCL, ifname, srcaddr, srcprefix, dstaddr, + dstprefix, gateway); +} + +int tst_netdev_remove_route(const char *file, const int lineno, + const char *ifname, unsigned int family, const void *srcaddr, + unsigned int srcprefix, size_t srclen, const void *dstaddr, + unsigned int dstprefix, size_t dstlen, const void *gateway, + size_t gatewaylen) +{ + return modify_route(file, lineno, RTM_DELROUTE, 0, ifname, family, + srcaddr, srcprefix, srclen, dstaddr, dstprefix, dstlen, + gateway, gatewaylen); +} + +int tst_netdev_remove_route_inet(const char *file, const int lineno, + const char *ifname, in_addr_t srcaddr, unsigned int srcprefix, + in_addr_t dstaddr, unsigned int dstprefix, in_addr_t gateway) +{ + return modify_route_inet(file, lineno, RTM_DELROUTE, 0, ifname, + srcaddr, srcprefix, dstaddr, dstprefix, gateway); +}