Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 7 Sep 2010 17:24:05 +0000 (UTC)
From:      Attilio Rao <attilio@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r212297 - in projects/sv/sys: net netinet
Message-ID:  <201009071724.o87HO5tM008117@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: attilio
Date: Tue Sep  7 17:24:05 2010
New Revision: 212297
URL: http://svn.freebsd.org/changeset/base/212297

Log:
  Move the netdump file to sys/netinet/ rather than sys/net/ as it is
  mostly specific to TCP/IP suite.

Added:
  projects/sv/sys/netinet/netdump_client.c
     - copied unchanged from r212294, projects/sv/sys/net/netdump_client.c
Deleted:
  projects/sv/sys/net/netdump_client.c

Copied: projects/sv/sys/netinet/netdump_client.c (from r212294, projects/sv/sys/net/netdump_client.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/sv/sys/netinet/netdump_client.c	Tue Sep  7 17:24:05 2010	(r212297, copy of r212294, projects/sv/sys/net/netdump_client.c)
@@ -0,0 +1,1349 @@
+/*-
+ * Copyright (c) 2005 Sandvine Incorporated. All rights reserved.
+ * Copyright (c) 2000 Darrell Anderson <anderson@cs.duke.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * netdump_client.c
+ * FreeBSD kernel module supporting netdump network dumps.
+ * netdump_server must be running to accept client dumps.
+ * XXX: This should be split into machdep and non-machdep parts
+ *
+*/
+
+#include "opt_ddb.h"
+#include "opt_device_polling.h"
+#include "opt_netdump.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/reboot.h>
+#include <sys/eventhandler.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/kerneldump.h>
+#include <sys/smp.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/ethernet.h>
+#include <net/if_var.h>
+#include <net/if_vlan_var.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <machine/md_var.h>
+#include <machine/pcb.h>
+#include <machine/in_cksum.h>
+#include <machine/smp.h>
+#include <machine/elf.h>
+#include <machine/md_var.h>
+#include <machine/_inttypes.h>
+#include <net/if_media.h>
+#include <net/if_mib.h>
+#include <net/netdump.h>
+#include <machine/clock.h>
+
+#ifdef DDB
+#include <ddb/ddb.h>
+#endif
+
+#ifdef NETDUMP_DEBUG
+#define	NETDDEBUG(f, ...)		printf((f), ## __VA_ARGS__)
+#define	NETDDEBUG_IF(i, f, ...)		if_printf((i), (f), ## __VA_ARGS__)
+#if NETDUMP_DEBUG > 1
+#define	NETDDEBUGV(f, ...)		printf((f), ## __VA_ARGS__)
+#define	NETDDEBUGV_IF(i, f, ...)	if_printf((i), (f), ## __VA_ARGS__)
+#else
+#define	NETDDEBUGV(f, ...)
+#define	NETDDEBUGV_IF(i, f, ...)
+#endif
+#else
+#define	NETDDEBUG(f, ...)
+#define	NETDDEBUG_IF(i, f, ...)
+#define	NETDDEBUGV(f, ...)
+#define	NETDDEBUGV_IF(i, f, ...)
+#endif
+
+#define	NETDUMP_BROKEN_STATE_BUFFER_SIZE	(5 * sizeof(struct mtx))
+
+static void	 nd_handle_arp(struct mbuf **mb);
+static void	 nd_handle_ip(struct mbuf **mb);
+static int	 netdump_arp_server(void);
+static void	 netdump_config_defaults(void);
+static int	 netdump_dumper(void *priv, void *virtual,
+		    vm_offset_t physical, off_t offset, size_t length);
+static int	 netdump_ether_output(struct mbuf *m, struct ifnet *ifp, 
+		    struct ether_addr dst, u_short etype);
+static void	 netdump_mbuf_nop(void *ptr, void *opt_args);
+static int	 netdump_modevent(module_t mod, int type, void *unused); 
+static void	 netdump_network_poll(void);
+static void	 netdump_pkt_in(struct ifnet *ifp, struct mbuf *m);
+static int	 netdump_send(uint32_t type, off_t offset, unsigned char *data,
+		    uint32_t datalen);
+static int	 netdump_send_arp(void);
+static void	 netdump_trigger(void *arg, int howto);
+static int	 netdump_udp_output(struct mbuf *m);
+
+#ifdef NETDUMP_DEBUG
+static int	 sysctl_force_crash(SYSCTL_HANDLER_ARGS);
+#endif
+static int	 sysctl_ip(SYSCTL_HANDLER_ARGS);
+static int	 sysctl_nic(SYSCTL_HANDLER_ARGS);
+
+static eventhandler_tag nd_tag = NULL;       /* record of our shutdown event */
+static uint32_t nd_seqno = 1;		     /* current sequence number */
+static uint64_t rcvd_acks;		     /* flags for out of order acks */
+static int dump_failed, have_server_mac;
+static uint16_t nd_server_port = NETDUMP_PORT; /* port to respond on */
+static unsigned char buf[MAXDUMPPGS*PAGE_SIZE]; /* Must be at least as big as
+						 * the chunks dumpsys() gives
+						 * us */
+static struct ether_addr nd_gw_mac;
+
+static int nd_enable = 0;  /* if we should perform a network dump */
+static struct in_addr nd_server = {INADDR_ANY}; /* server address */
+static struct in_addr nd_client = {INADDR_ANY}; /* client (our) address */
+static struct in_addr nd_gw = {INADDR_ANY}; /* gw, if set */
+struct ifnet *nd_nic = NULL;
+static int nd_polls=10000; /* Times to poll the NIC (0.5ms each poll) before
+			    * assuming packetloss occurred: 5s by default */
+static int nd_retries=10; /* Times to retransmit lost packets */
+
+/*
+ * [netdump_supported_nic]
+ *
+ * Checks for netdump support on a network interface
+ *
+ * Parameters:
+ *	ifn	The network interface that is being tested for support
+ *
+ * Returns:
+ *	int	1 if the interface is supported, 0 if not
+ */
+static __inline int
+netdump_supported_nic(struct ifnet *ifn)
+{
+	return ifn->if_netdump != NULL;
+}
+
+/*-
+ * Sysctls specific code.
+ */
+
+/*
+ * [sysctl_ip]
+ *
+ * sysctl handler to deal with converting a string sysctl to/from an in_addr
+ *
+ * Parameters:
+ *	SYSCTL_HANDLER_ARGS
+ *	 - arg1 is a pointer to the struct in_addr holding the IP
+ *	 - arg2 is unused
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+static int
+sysctl_ip(SYSCTL_HANDLER_ARGS)
+{
+	struct in_addr addr;
+	char buf[INET_ADDRSTRLEN];
+	int error;
+	int len=req->newlen - req->newidx;
+
+	inet_ntoa_r(*(struct in_addr *)arg1, buf);
+	error = SYSCTL_OUT(req, buf, strlen(buf)+1);
+
+	if (error || !req->newptr)
+		return error;
+
+	if (len >= INET_ADDRSTRLEN) {
+		error = EINVAL;
+	} else {
+		error = SYSCTL_IN(req, buf, len);
+		buf[len]='\0';
+		if (error)
+			return error;
+		if (!inet_aton(buf, &addr))
+			return EINVAL;
+		*(struct in_addr *)arg1 = addr;
+	}
+
+	return error;
+}
+
+/*
+ * [sysctl_nic]
+ *
+ * sysctl handler to deal with converting a string sysctl to/from an interface
+ *
+ * Parameters:
+ *	SYSCTL_HANDLER_ARGS
+ *	 - arg1 is a pointer to the struct ifnet to the interface
+ *	 - arg2 is the maximum string length (IFNAMSIZ)
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+static int
+sysctl_nic(SYSCTL_HANDLER_ARGS)
+{
+	struct ifnet *ifn;
+	char buf[arg2+1];
+	int error;
+	int len;
+
+	if (*(struct ifnet **)arg1) {
+		error = SYSCTL_OUT(req,
+				(*(struct ifnet **)arg1)->if_xname,
+				strlen((*(struct ifnet **)arg1)->if_xname));
+	} else {
+		error = SYSCTL_OUT(req, "none", 5);
+	}
+
+	if (error || !req->newptr)
+		return error;
+
+	len = req->newlen - req->newidx;
+	if (len >= arg2) {
+		error = EINVAL;
+	} else {
+		error = SYSCTL_IN(req, buf, len);
+		buf[len]='\0';
+		if (error)
+			return error;
+
+		if (!strcmp(buf, "none")) {
+			ifn = NULL;
+		} else {
+			if ((ifn = TAILQ_FIRST(&ifnet)) != NULL) do {
+				if (!strcmp(ifn->if_xname, buf)) break;
+			} while ((ifn = TAILQ_NEXT(ifn, if_link)) != NULL);
+
+			if (!ifn) return ENODEV;
+			if (!netdump_supported_nic(ifn)) return EINVAL;
+		}
+
+		(*(struct ifnet **)arg1) = ifn;
+	}
+
+	return error;
+}
+
+#ifdef NETDUMP_DEBUG
+static int
+sysctl_force_crash(SYSCTL_HANDLER_ARGS) 
+{
+	int error, force_crash;
+
+	force_crash = 0;
+	error = sysctl_handle_int(oidp, &force_crash, force_crash, req);
+	if (error || req->newptr == NULL)
+		return error;
+
+	switch (force_crash) {
+		case 1:
+			printf("\nLivelocking system...\n");
+			for (;;);
+			break;
+		case 2:
+			printf("\nPanic'ing system...\n");
+			panic("netdump forced crash");
+			break;
+		case 3:
+			printf("\nLivelocking system while holding the "
+			    "interface lock\n");
+			nd_nic->if_netdump->test_get_lock(nd_nic);
+			for (;;);
+			break;
+		case 5:
+			critical_enter();
+			panic("Forcing spourious critical section");
+			break;
+		case 6:
+			critical_enter();
+			printf("\nLivelocking in a critical section\n");
+			for (;;);
+		default:
+			return EINVAL;
+	}
+	return 0;
+}
+#endif
+
+SYSCTL_NODE(_net, OID_AUTO, dump, CTLFLAG_RW, 0, "netdump");
+SYSCTL_PROC(_net_dump, OID_AUTO, server, CTLTYPE_STRING|CTLFLAG_RW, &nd_server,
+	0, sysctl_ip, "A", "dump server");
+SYSCTL_PROC(_net_dump, OID_AUTO, client, CTLTYPE_STRING|CTLFLAG_RW, &nd_client,
+	0, sysctl_ip, "A", "dump client");
+SYSCTL_PROC(_net_dump, OID_AUTO, gateway, CTLTYPE_STRING|CTLFLAG_RW, &nd_gw,
+	0, sysctl_ip, "A", "dump default gateway");
+SYSCTL_PROC(_net_dump, OID_AUTO, nic, CTLTYPE_STRING|CTLFLAG_RW, &nd_nic,
+	IFNAMSIZ, sysctl_nic, "A", "NIC to dump on");
+SYSCTL_INT(_net_dump, OID_AUTO, polls, CTLTYPE_INT|CTLFLAG_RW, &nd_polls, 0,
+	"times to poll NIC per retry");
+SYSCTL_INT(_net_dump, OID_AUTO, retries, CTLTYPE_INT|CTLFLAG_RW, &nd_retries, 0,
+	"times to retransmit lost packets");
+SYSCTL_INT(_net_dump, OID_AUTO, enable, CTLTYPE_INT|CTLFLAG_RW, &nd_enable,
+	0, "enable network dump");
+TUNABLE_INT("net.dump.enable", &nd_enable);
+#ifdef NETDUMP_DEBUG
+SYSCTL_DECL(_debug_netdump);
+SYSCTL_PROC(_debug_netdump, OID_AUTO, crash, CTLTYPE_INT|CTLFLAG_RW, 0,
+    sizeof(int), sysctl_force_crash, "I", "force crashing");
+#endif
+
+/*-
+ * Network specific primitives.
+ * Following down the code they are divided ordered as:
+ * - Output primitives
+ * - Input primitives
+ * - Polling primitives
+ */
+
+/*
+ * [netdump_mbuf_nop]
+ *
+ * netdump wraps external mbufs around address ranges.  unlike most sane
+ * counterparts, netdump uses a stop-and-wait approach to flow control and
+ * retransmission, so the ack obviates the need for mbuf reference
+ * counting.  we still need to tell other mbuf handlers not to do anything
+ * special with our mbufs, so specify this nop handler.
+ *
+ * Parameters:
+ *	ptr	 data to free (ignored)
+ *	opt_args callback pointer (ignored)
+ *
+ * Returns:
+ *	void
+ */
+static void
+netdump_mbuf_nop(void *ptr, void *opt_args)
+{
+	;
+}
+
+/*
+ * [netdump_ether_output]
+ *
+ * Handles creation of the ethernet header, then places outgoing packets into
+ * the tx buffer for the NIC
+ *
+ * Parameters:
+ *	m	The mbuf containing the packet to be sent (will be freed by
+ *		this function or the NIC driver)
+ *	ifp	The interface to send on
+ *	dst	The destination ethernet address (source address will be looked
+ *		up using ifp)
+ *	etype	The ETHERTYPE_* value for the protocol that is being sent
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+static int
+netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst,
+		     u_short etype)
+{
+	struct ether_header *eh;
+
+	/* fill in the ethernet header */
+	M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
+	if (m == 0) {
+		printf("netdump_ether_output: Out of mbufs\n");
+		return ENOBUFS;
+	}
+	eh = mtod(m, struct ether_header *);
+	bcopy(IF_LLADDR(ifp), eh->ether_shost, ETHER_ADDR_LEN);
+	bcopy(dst.octet, eh->ether_dhost, ETHER_ADDR_LEN);
+	eh->ether_type = htons(etype);
+
+	if (((ifp->if_flags & (IFF_MONITOR|IFF_UP)) != IFF_UP) ||
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) {
+		if_printf(ifp, "netdump_ether_output: Interface isn't up\n");
+		m_freem(m);
+		return ENETDOWN;
+	}
+
+	if (_IF_QFULL(&ifp->if_snd)) {
+		if_printf(ifp, "netdump_ether_output: TX queue full\n");
+		m_freem(m);
+		return ENOBUFS;
+	}
+
+	_IF_ENQUEUE(&ifp->if_snd, m);
+	return 0;
+}
+
+/*
+ * [netdump_udp_output]
+ *
+ * unreliable transmission of an mbuf chain to the netdump server
+ * Note: can't handle fragmentation; fails if the packet is larger than
+ *	 nd_nic->if_mtu after adding the UDP/IP headers
+ *
+ * Parameters:
+ *	m	mbuf chain
+ *
+ * Returns:
+ *	int	see errno.h, 0 for success
+ */
+static int
+netdump_udp_output(struct mbuf *m)
+{
+	struct udpiphdr *ui;
+	struct ip *ip;
+
+	M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT);
+	if (m == 0) {
+		printf("netdump_udp_output: Out of mbufs\n");
+		return ENOBUFS;
+	}
+	ui = mtod(m, struct udpiphdr *);
+	bzero(ui->ui_x1, sizeof(ui->ui_x1));
+	ui->ui_pr = IPPROTO_UDP;
+	ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip));
+	ui->ui_ulen = ui->ui_len;
+	ui->ui_src = nd_client;
+	ui->ui_dst = nd_server;
+	/* Use this src port so that the server can connect() the socket */
+	ui->ui_sport = htons(NETDUMP_ACKPORT);
+	ui->ui_dport = htons(nd_server_port);
+	ui->ui_sum = 0;
+	if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0)
+		ui->ui_sum = 0xffff;
+
+	ip = mtod(m, struct ip *);
+	ip->ip_v = IPVERSION;
+	ip->ip_hl = sizeof(struct ip) >> 2;
+	ip->ip_tos = 0;
+	ip->ip_len = htons(m->m_pkthdr.len);
+	ip->ip_id = 0;
+	ip->ip_off = htons(IP_DF);
+	ip->ip_ttl = 32;
+	ip->ip_sum = 0;
+	ip->ip_sum = in_cksum(m, sizeof(struct ip));
+
+	if (m->m_pkthdr.len > nd_nic->if_mtu) {
+		/* Whoops. The packet is too big. */
+		printf("netdump_udp_output: Packet is too big: "
+		       "%u > MTU %lu\n", m->m_pkthdr.len, nd_nic->if_mtu);
+		m_freem(m);
+		return ENOBUFS;
+	}
+
+	return netdump_ether_output(m, nd_nic, nd_gw_mac, ETHERTYPE_IP);
+}
+
+/*
+ * [netdump_send_arp]
+ *
+ * Builds and sends a single ARP request to locate the server
+ *
+ * Parameters:
+ *	void
+ *
+ * Return value:
+ *	0 on success
+ *	errno on error
+ */
+static int
+netdump_send_arp()
+{
+	struct mbuf *m;
+	int pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr));
+	struct arphdr *ah;
+	struct ether_addr bcast;
+
+	ETHER_SET_BROADCAST(&bcast);
+
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if (m == NULL) {
+		printf("netdump_send_arp: Out of mbufs");
+		return ENOBUFS;
+	}
+	m->m_pkthdr.len = m->m_len = pktlen;
+	MH_ALIGN(m, pktlen); /* Make room for ethernet header */
+	ah = mtod(m, struct arphdr *);
+	ah->ar_hrd = htons(ARPHRD_ETHER);
+	ah->ar_pro = htons(ETHERTYPE_IP);
+	ah->ar_hln = ETHER_ADDR_LEN;
+	ah->ar_pln = sizeof(struct in_addr);
+	ah->ar_op = htons(ARPOP_REQUEST);
+	bcopy(IF_LLADDR(nd_nic), ar_sha(ah), ETHER_ADDR_LEN);
+	((struct in_addr *)ar_spa(ah))->s_addr = nd_client.s_addr;
+	bzero(ar_tha(ah), ETHER_ADDR_LEN);
+	((struct in_addr *)ar_tpa(ah))->s_addr = nd_gw.s_addr;
+
+	return netdump_ether_output(m, nd_nic, bcast, ETHERTYPE_ARP);
+}
+
+/*
+ * [netdump_arp_server]
+ *
+ * Sends ARP requests to locate the server and waits for a response
+ *
+ * Parameters:
+ *	void
+ *
+ * Return value:
+ *	0 on success
+ *	errno on error
+ */
+static int
+netdump_arp_server()
+{
+	int err, polls, retries;
+
+	for (retries=0; retries < nd_retries && !have_server_mac; retries++) {
+		err = netdump_send_arp();
+
+		if (err)
+			return err;
+
+		for (polls=0; polls < nd_polls && !have_server_mac; polls++) {
+			netdump_network_poll();
+			DELAY(500); /* 0.5 ms */
+		}
+
+		if (!have_server_mac) printf("(ARP retry)");
+	}
+
+	if (have_server_mac)
+		return 0;
+
+	printf("\nARP timed out.\n");
+
+	return ETIMEDOUT;
+}
+
+/*
+ * [netdump_send]
+ *
+ * construct and reliably send a netdump packet.  may fail from a resource
+ * shortage or extreme number of unacknowledged retransmissions.  wait for
+ * an acknowledgement before returning.  splits packets into chunks small
+ * enough to be sent without fragmentation (looks up the interface MTU)
+ *
+ * Parameters:
+ *	type	netdump packet type (HERALD, FINISHED, or VMCORE)
+ *	offset	vmcore data offset (bytes)
+ *	data	vmcore data
+ *	datalen	vmcore data size (bytes)
+ *
+ * Returns:
+ *	int see errno.h, 0 for success
+ */
+static int
+netdump_send(uint32_t type, off_t offset, 
+	     unsigned char *data, uint32_t datalen)
+{
+	struct netdump_msg_hdr *nd_msg_hdr;
+	struct mbuf *m, *m2;
+	int retries = 0, polls, error;
+	uint32_t i, sent_so_far;
+	uint64_t want_acks=0;
+
+	rcvd_acks = 0;
+
+retransmit:
+	/* We might get chunks too big to fit in packets. Yuck. */
+	for (i=sent_so_far=0; sent_so_far < datalen || (i==0 && datalen==0);
+		i++) {
+		uint32_t pktlen = datalen-sent_so_far;
+		/* First bound: the packet structure */
+		pktlen = min(pktlen, NETDUMP_DATASIZE);
+		/* Second bound: the interface MTU (assume no IP options) */
+		pktlen = min(pktlen, nd_nic->if_mtu -
+				sizeof(struct udpiphdr) -
+				sizeof(struct netdump_msg_hdr));
+
+		/* Check if we're retransmitting and this has been ACKed
+		 * already */
+		if ((rcvd_acks & (1 << i)) != 0) {
+			sent_so_far += pktlen;
+			continue;
+		}
+
+		/*
+		 * get and fill a header mbuf, then chain data as an extended
+		 * mbuf.
+		 */
+		MGETHDR(m, M_DONTWAIT, MT_DATA);
+		if (m == NULL) {
+			printf("netdump_send: Out of mbufs!\n");
+			return ENOBUFS;
+		}
+		m->m_pkthdr.len = m->m_len = sizeof(struct netdump_msg_hdr);
+		/* leave room for udpip */
+		MH_ALIGN(m, sizeof(struct netdump_msg_hdr));
+		nd_msg_hdr = mtod(m, struct netdump_msg_hdr *);
+		nd_msg_hdr->seqno = htonl(nd_seqno+i);
+		nd_msg_hdr->type = htonl(type);
+		nd_msg_hdr->offset = htonll(offset+sent_so_far);
+		nd_msg_hdr->len = htonl(pktlen);
+		nd_msg_hdr->_pad = 0;
+
+		if (pktlen) {
+			if ((m2 = m_get(M_DONTWAIT, MT_DATA)) == NULL) {
+				m_freem(m);
+				printf("netdump_send: Out of mbufs!\n");
+				return ENOBUFS;
+			}
+			MEXTADD(m2, data+sent_so_far, pktlen, netdump_mbuf_nop,
+				NULL, M_RDONLY, EXT_MOD_TYPE);
+			m2->m_len = pktlen;
+			m->m_next = m2;
+			m->m_pkthdr.len += m2->m_len;
+		}
+
+		if ((error = netdump_udp_output(m)) != 0) {
+			return error;
+		}
+
+		/* Note that we're waiting for this packet in the bitfield */
+		want_acks |= 1 << i;
+
+		sent_so_far += pktlen;
+	}
+
+	if (i >= sizeof(want_acks)*8) {
+		printf("Warning: Sent more than %zd packets (%d). "
+		       "Acknowledgements will fail unless the size of "
+		       "rcvd_acks/want_acks is increased.\n",
+		       sizeof(want_acks)*8, i);
+	}
+
+	/*
+	 * wait for acks. a *real* window would speed things up considerably.
+	 */
+	polls = 0;
+	while (rcvd_acks != want_acks) {		
+		if (polls++ > nd_polls) {
+			if (retries++ > nd_retries) {
+				return ETIMEDOUT; /* 10 s, no ack */
+			}
+			printf(". ");
+			goto retransmit; /* 1 s, no ack */
+		}
+		/*
+		 * this is not always necessary, but does no harm.
+		 */
+		netdump_network_poll();
+		DELAY(500); /* 0.5 ms */
+	}
+	nd_seqno += i;
+	return 0;
+}
+
+/*
+ * [nd_handle_ip]
+ *
+ * Handler for IP packets: checks their sanity and then processes any netdump
+ * ACK packets it finds.
+ *
+ * It needs to replicate partially the behaviour of ip_input() and
+ * udp_input().
+ *
+ * Parameters:
+ *	mb	a pointer to an mbuf * containing the packet received
+ *		Updates *mb if m_pullup et al change the pointer
+ *		Assumes the calling function will take care of freeing the mbuf
+ *
+ * Return value:
+ *	void
+ */
+static void
+nd_handle_ip(struct mbuf **mb)
+{
+	unsigned short hlen;
+	struct ip *ip;
+	struct udpiphdr *udp;
+	struct netdump_ack *nd_ack;
+	struct mbuf *m;
+	int rcv_ackno;
+
+	/* IP processing */
+
+	NETDDEBUGV("nd_handle_ip: Processing IP packet...\n");
+
+	m = *mb;
+	if (m->m_pkthdr.len < sizeof(struct ip)) {
+		NETDDEBUG("nd_handle_ip: dropping packet too small for IP "
+		    "header\n");
+		return;
+	}
+	if (m->m_len < sizeof(struct ip) &&
+	    (*mb = m = m_pullup(m, sizeof(struct ip))) == NULL) {
+		NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+		return;
+	}
+
+	ip = mtod(m, struct ip *);
+
+	/* IP version */
+	if (ip->ip_v != IPVERSION) {
+		NETDDEBUG("nd_handle_ip: Bad IP version %d\n", ip->ip_v);
+		return;
+	}
+
+	/* Header length */
+	hlen = ip->ip_hl << 2;
+	if (hlen < sizeof(struct ip)) {
+		NETDDEBUG("nd_handle_ip: Bad IP header length (%hu)\n", hlen);
+		return;
+	}
+	if (hlen > m->m_len) {
+		if ((*mb = m = m_pullup(m, hlen)) == NULL) {
+			NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+			return;
+		}
+		ip = mtod(m, struct ip *);
+	}
+
+#ifdef INVARIANTS
+	if (((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
+	    (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) &&
+	    (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) {
+		NETDDEBUG("nd_handle_ip: Bad IP header (RFC1122)\n");
+		return;
+	}
+#endif
+
+	/* Checksum */
+	if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) {
+		if (!(m->m_pkthdr.csum_flags & CSUM_IP_VALID)) {
+			NETDDEBUG("nd_handle_ip: Bad IP checksum\n");
+			return;
+		}
+	} else
+		NETDDEBUG("nd_handle_ip: HW didn't check IP cksum\n");
+
+	/* Convert fields to host byte order */
+	ip->ip_len = ntohs(ip->ip_len);
+	if (ip->ip_len < hlen) {
+		NETDDEBUG("nd_handle_ip: IP packet smaller (%hu) than "
+		    "header (%hu)\n", ip->ip_len, hlen);
+		return;
+	}
+	ip->ip_off = ntohs(ip->ip_off);
+
+	if (m->m_pkthdr.len < ip->ip_len) {
+		NETDDEBUG("nd_handle_ip: IP packet bigger (%hu) than "
+		    "ethernet packet (%hu)\n", ip->ip_len, m->m_pkthdr.len);
+		return;
+	}
+	if (m->m_pkthdr.len > ip->ip_len) {
+		/* Truncate the packet to the IP length */
+		if (m->m_len == m->m_pkthdr.len) {
+			m->m_len = ip->ip_len;
+			m->m_pkthdr.len = ip->ip_len;
+		} else
+			m_adj(m, ip->ip_len - m->m_pkthdr.len);
+	}
+
+	/* We would process IP options here, but we'll ignore them instead. */
+	/* Strip IP options */
+	if (hlen > sizeof(struct ip)) {
+		ip_stripoptions(m, (struct mbuf *)0);
+		hlen = sizeof(struct ip);
+	}
+
+	/* Check that the source is the server's IP */
+	if (ip->ip_src.s_addr != nd_server.s_addr) {
+		NETDDEBUG("nd_handle_ip: Drop packet not from server\n");
+		return;
+	}
+
+	/* Check if the destination IP is ours */
+	if (ip->ip_dst.s_addr != nd_client.s_addr) {
+		NETDDEBUGV("nd_handle_ip: Drop packet not to our IP\n");
+		return;
+	}
+
+	if (ip->ip_p != IPPROTO_UDP) {
+		NETDDEBUG("nd_handle_ip: Drop non-UDP packet\n");
+		return;
+	}
+
+	/* Let's not deal with fragments */
+	if (ip->ip_off & (IP_MF | IP_OFFMASK)) {
+		NETDDEBUG("nd_handle_ip: Drop fragmented packet\n");
+		return;
+	}
+	/* UDP custom is to have packet length not include IP header */
+	ip->ip_len -= hlen;
+
+	/* IP done */
+	/* UDP processing */
+
+	/* Get IP and UDP headers together, along with the netdump packet */
+	if (m->m_pkthdr.len <
+	    sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) {
+		NETDDEBUG("nd_handle_ip: Ignoring small packet\n");
+		return;
+	}
+	if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack) &&
+	    (*mb = m = m_pullup(m, sizeof(struct udpiphdr) +
+	    sizeof(struct netdump_ack))) == NULL) {
+		NETDDEBUG("nd_handle_ip: m_pullup failed\n");
+		return;
+	}
+	udp = mtod(m, struct udpiphdr *);
+
+	NETDDEBUG("nd_handle_ip: Processing packet...");
+
+	if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) {
+		NETDDEBUG("not on the netdump port.\n");
+		return;
+	}
+
+	/* UDP done */
+	/* Netdump processing */
+
+	/*
+	 * packet is meant for us.  extract the ack sequence number.
+	 * if it's the first ack, extract the port number as well
+	 */
+	nd_ack = (struct netdump_ack *)
+		(mtod(m, caddr_t) + sizeof(struct udpiphdr));
+	rcv_ackno = ntohl(nd_ack->seqno);
+
+	if (nd_server_port == NETDUMP_PORT) {
+	    nd_server_port = ntohs(udp->ui_u.uh_sport);
+	}
+
+	if (rcv_ackno >= nd_seqno+64) {
+		printf("nd_handle_ip: ACK %d too far in future!\n", rcv_ackno);
+	} else if (rcv_ackno < nd_seqno) {
+		/* Do nothing: A duplicated past ACK */
+	} else {
+		/* We're interested in this ack. Record it. */
+		rcvd_acks |= 1 << (rcv_ackno-nd_seqno);
+	}
+}
+
+/*
+ * [nd_handle_arp]
+ *
+ * Handler for ARP packets: checks their sanity and then
+ * 1. If the ARP is a request for our IP, respond with our MAC address
+ * 2. If the ARP is a response from our server, record its MAC address
+ *
+ * It needs to replicate partially the behaviour of arpintr() and
+ * in_arpinput().
+ *
+ * Parameters:
+ *	mb	a pointer to an mbuf * containing the packet received
+ *		Updates *mb if m_pullup et al change the pointer
+ *		Assumes the calling function will take care of freeing the mbuf
+ *
+ * Return value:
+ *	void
+ */
+static void
+nd_handle_arp(struct mbuf **mb)
+{
+	struct mbuf *m;
+	struct arphdr *ah;
+	struct ifnet *ifp;
+	int req_len, op;
+	struct in_addr isaddr, itaddr, myaddr;
+	uint8_t *enaddr;
+	struct ether_addr dst;
+
+	m = *mb;
+	ifp = m->m_pkthdr.rcvif;
+	if (m->m_len < sizeof(struct arphdr) && ((*mb = m = m_pullup(m,
+	    sizeof(struct arphdr))) == NULL)) {
+		NETDDEBUG("nd_handle_arp: runt packet: m_pullup failed\n");
+		return;
+	}
+	ah = mtod(m, struct arphdr *);
+
+	if (ntohs(ah->ar_hrd) != ARPHRD_ETHER &&
+	    ntohs(ar->ar_hrd) != ARPHRD_IEEE802 &&
+	    ntohs(ar->ar_hrd) != ARPHRD_ARCNET &&
+	    ntohs(ar->ar_hrd) != ARPHRD_IEEE1394) {
+		NETDDEBUG("nd_handle_arp: unknown hardware address fmt "
+		    "0x%2D)\n", (unsigned char *)&ah->ar_hrd, "");
+		return;
+	}
+
+	if (ntohs(ah->ar_pro) != ETHERTYPE_IP) {
+		NETDDEBUG("nd_handle_arp: Drop ARP for unknown "
+		    "protocol %d\n", ntohs(ah->ar_pro));
+		return;
+	}
+
+	req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr));
+	if (m->m_len < req_len && (*mb = m = m_pullup(m, req_len)) == NULL) {
+		NETDDEBUG("nd_handle_arp: runt packet: m_pullup failed\n");
+		return;
+	}
+	ah = mtod(m, struct arphdr *);
+
+	op = ntohs(ah->ar_op);
+	bcopy(ar_spa(ah), &isaddr, sizeof(isaddr));
+	bcopy(ar_tpa(ah), &itaddr, sizeof(itaddr));
+	enaddr = (uint8_t *)IF_LLADDR(ifp);
+	myaddr = nd_client;
+
+	if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) {
+		NETDDEBUG("nd_handle_arp: ignoring ARP from myself\n");
+		return;
+	}
+
+#ifdef INVARIANTS
+	if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) {
+		NETDDEBUG("nd_handle_arp: ignoring ARP as link address is "
+		    "broadcast.\n");
+		return;
+	}
+#endif
+
+	if (isaddr.s_addr == nd_client.s_addr) {
+		printf("nd_handle_arp: %*D is using my IP address %s!\n",
+				ifp->if_addrlen, (u_char *)ar_sha(ah), ":",
+				inet_ntoa(isaddr));
+		return;
+	}
+
+	if (!bcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen)) {
+		NETDDEBUG("nd_handle_arp: ignoring ARP from broadcast "
+		    "address\n");
+		return;
+	}
+
+	if (op == ARPOP_REPLY) {
+		if (isaddr.s_addr != nd_gw.s_addr) {
+			char buf[INET_ADDRSTRLEN];
+			inet_ntoa_r(isaddr, buf);
+			NETDDEBUG("nd_handle_arp: ignoring ARP reply from "
+			    "%s (not netdump server)\n", buf);
+			return;
+		}
+		bcopy(ar_sha(ah), nd_gw_mac.octet,
+				min(ah->ar_hln, ETHER_ADDR_LEN));
+		have_server_mac = 1;
+		NETDDEBUG("\nnd_handle_arp: Got server MAC address %6D\n",
+		    nd_gw_mac.octet, ":");
+		return;
+	}
+
+	if (op != ARPOP_REQUEST) {
+		NETDDEBUG("nd_handle_arp: Ignoring non-request/non-reply "
+		    "ARP\n");
+		return;
+	}
+
+	if (itaddr.s_addr != nd_client.s_addr) {
+		NETDDEBUG("nd_handle_arp: ignoring ARP not to our IP\n");
+		return;
+	}
+
+	bcopy(ar_sha(ah), ar_tha(ah), ah->ar_hln);
+	bcopy(enaddr, ar_sha(ah), ah->ar_hln);
+	bcopy(ar_spa(ah), ar_tpa(ah), ah->ar_pln);
+	bcopy(&itaddr, ar_spa(ah), ah->ar_pln);
+	ah->ar_op = htons(ARPOP_REPLY);
+	ah->ar_pro = htons(ETHERTYPE_IP); /* let's be sure! */
+	m->m_flags &= ~(M_BCAST|M_MCAST); /* never reply by broadcast */
+	m->m_len = sizeof(*ah) + (2 * ah->ar_pln) + (2 * ah->ar_hln);
+	m->m_pkthdr.len = m->m_len;
+
+	bcopy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN);
+	netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP);
+	*mb = NULL; /* Don't m_free upon return */
+}
+
+/*
+ * [netdump_pkt_in]
+ *
+ * Handler for incoming packets directly from the network adapter

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201009071724.o87HO5tM008117>