From 78c896b3b3127515478090c19447e27dc406427e Mon Sep 17 00:00:00 2001 From: Jianfeng Tan Date: Mon, 18 Nov 2019 06:59:50 +0000 Subject: TLDKv2 Signed-off-by: Jianfeng Tan Signed-off-by: Jielong Zhou Signed-off-by: Jian Zhang Signed-off-by: Chen Zhao Change-Id: I55c39de4c6cd30f991f35631eb507f770230f08e --- test/packetdrill/wire_server_netdev.c | 204 ++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 test/packetdrill/wire_server_netdev.c (limited to 'test/packetdrill/wire_server_netdev.c') diff --git a/test/packetdrill/wire_server_netdev.c b/test/packetdrill/wire_server_netdev.c new file mode 100644 index 0000000..cee64e7 --- /dev/null +++ b/test/packetdrill/wire_server_netdev.c @@ -0,0 +1,204 @@ +/* + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +/* + * Author: ncardwell@google.com (Neal Cardwell) + * + * Server-side network device code for remote on-the-wire testing + * using a real NIC. + */ + +#include "wire_server_netdev.h" + +#include +#include +#include +#include + +#include "logging.h" +#include "net_utils.h" +#include "packet.h" +#include "packet_socket.h" +#include "packet_parser.h" + +struct wire_server_netdev { + struct netdev netdev; /* "inherit" from netdev */ + + char *name; /* copy of the interface name (owned) */ + struct config *config; /* this test's config (not owned) */ + + struct ether_addr client_ether_addr; + struct ether_addr server_ether_addr; + + struct packet_socket *psock; /* for sniffing packets (owned) */ +}; + +struct netdev_ops wire_server_netdev_ops; + +/* "Downcast" an abstract netdev to our flavor. */ +static inline struct wire_server_netdev *to_server_netdev( + struct netdev *netdev) +{ + return (struct wire_server_netdev *)netdev; +} + +void wire_server_netdev_init(const char *netdev_name) +{ +#ifdef linux + char *command = NULL; + + /* If Large Receive Offload (LRO) or Generic Receive Offload + * (GRO) is enabled, then disable them both, so that we are + * sniffing packets as seen on the wire, not packets + * aggregated by LRO or GRO. + * + * TOOD(ncardwell): if netdev_name is not a bonding interface, + * then we should just disable LRO/GRO on that interface; if + * netdev_name is a bonding interface then we should + * programmatically figure out all the slave interfaces for + * the given netdev_name, instead of using this overly broad + * approach. + */ + asprintf(&command, + "(ethtool --offload eth0 lro off gro off; " + " ethtool --offload eth1 lro off gro off; " + " ethtool --offload eth2 lro off gro off) " + " > /dev/null 2>&1"); + /* For now, intentionally ignoring errors rather than figuring + * out how many Ethernet interfaces there are. TODO: clean up. + */ + system(command); + free(command); + + /* Block outgoing IPv6 "destination unreachable" messages, to + * block the "destination unreachable, unreachable route" + * messages we would otherwise send the kernel under test. + * That would cause the kernel under test to delete the TCP + * socket under test and send a RST. + */ + asprintf(&command, + "ip6tables -F OUTPUT; " + "ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 1 -j DROP"); + /* For now, intentionally ignoring. TODO: clean up. */ + system(command); + free(command); +#endif +} + +struct netdev *wire_server_netdev_new( + struct config *config, + const char *wire_server_device, + const struct ether_addr *client_ether_addr, + const struct ether_addr *server_ether_addr) +{ + DEBUGP("wire_server_netdev_new\n"); + + struct wire_server_netdev *netdev = + calloc(1, sizeof(struct wire_server_netdev)); + + netdev->netdev.ops = &wire_server_netdev_ops; + netdev->name = strdup(wire_server_device); + netdev->config = config; + ether_copy(&netdev->client_ether_addr, client_ether_addr); + ether_copy(&netdev->server_ether_addr, server_ether_addr); + + /* Add the gateway IP to our NIC, so it answers ARP or + * neighbor discovery requests, so we can receive packets from + * the client. TODO(ncardwell): support multiple concurrent + * tests, by perhaps ref-counting the gateway IPs we need to + * be using. TODO(ncardwell): make sure we don't delete our + * primary host IP (the one matching our hostname). + */ + net_setup_dev_address(netdev->name, + &config->live_gateway_ip, + config->live_prefix_len); + + netdev->psock = packet_socket_new(netdev->name); + + /* Make sure we only see packets from the machine under test. */ + packet_socket_set_filter(netdev->psock, + client_ether_addr, + &config->live_local_ip); /* client IP */ + + return (struct netdev *)netdev; +} + +static void wire_server_netdev_free(struct netdev *a_netdev) +{ + struct wire_server_netdev *netdev = to_server_netdev(a_netdev); + + DEBUGP("wire_server_netdev_free\n"); + + net_del_dev_address(netdev->name, + &netdev->config->live_gateway_ip, + netdev->config->live_prefix_len); + + free(netdev->name); + if (netdev->psock) + packet_socket_free(netdev->psock); + + memset(netdev, 0, sizeof(*netdev)); /* paranoia */ + free(netdev); +} + +static int wire_server_netdev_send(struct netdev *a_netdev, + struct packet *packet) +{ + struct wire_server_netdev *netdev = to_server_netdev(a_netdev); + struct ether_header ether; + struct iovec ether_frame[2]; + int address_family = packet_address_family(packet); + int result = STATUS_ERR; + + DEBUGP("wire_server_netdev_send\n"); + + /* Prepend an ethernet header. */ + ether_copy(ether.ether_dhost, &netdev->client_ether_addr); + ether_copy(ether.ether_shost, &netdev->server_ether_addr); + ether.ether_type = htons(ether_type_for_family(address_family)); + ether_frame[0].iov_base = ðer; + ether_frame[0].iov_len = sizeof(ether); + + /* Then after that we have the IP datagram. */ + ether_frame[1].iov_base = packet_start(packet); + ether_frame[1].iov_len = packet->ip_bytes; + + result = packet_socket_writev(netdev->psock, + ether_frame, ARRAY_SIZE(ether_frame)); + + return result; +} + +static int wire_server_netdev_receive(struct netdev *a_netdev, + struct packet **packet, char **error) +{ + struct wire_server_netdev *netdev = to_server_netdev(a_netdev); + int num_packets = 0; + + DEBUGP("wire_server_netdev_receive\n"); + + return netdev_receive_loop(netdev->psock, PACKET_LAYER_2_ETHERNET, + DIRECTION_INBOUND, packet, &num_packets, + error); +} + +struct netdev_ops wire_server_netdev_ops = { + .free = wire_server_netdev_free, + .send = wire_server_netdev_send, + .receive = wire_server_netdev_receive, +}; -- cgit 1.2.3-korg