Date: Wed, 28 Jul 2010 15:10:31 +0000 (UTC) From: Attilio Rao <attilio@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r210561 - projects/sv/sys/net Message-ID: <201007281510.o6SFAV5J052045@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: attilio Date: Wed Jul 28 15:10:31 2010 New Revision: 210561 URL: http://svn.freebsd.org/changeset/base/210561 Log: Initial import of the netdump files. They still need a lot of polishing and cleanup so they might not be considered definitive at all. Added: projects/sv/sys/net/netdump.h (contents, props changed) projects/sv/sys/net/netdump_client.c (contents, props changed) Added: projects/sv/sys/net/netdump.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/sv/sys/net/netdump.h Wed Jul 28 15:10:31 2010 (r210561) @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-2006 Sandvine Incorporated. All rights reserved. + * - Modified by Adrian Dewhurst to work with FreeBSD 5.2, send a dump header, + * and improve performance. + * + * 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. + */ + +#ifndef __NETDUMP_H +#define __NETDUMP_H + +#ifdef _KERNEL + +int netdump_break_lock(struct mtx *lock, const char *name, + int *broke_lock, uint8_t *broken_state, unsigned index, + unsigned broken_state_size); + +#endif + +#endif /* __NETDUMP_H */ Added: projects/sv/sys/net/netdump_client.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/sv/sys/net/netdump_client.c Wed Jul 28 15:10:31 2010 (r210561) @@ -0,0 +1,1486 @@ +/*- + * 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 + * - Modified by Adrian Dewhurst to work with FreeBSD 5.2, send a dump header, + * improve performance, and couple with the em driver. + * +*/ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + +#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 <machine/clock.h> + +#include <ddb/ddb.h> + +#include <netinet/netdump.h> +#include "opt_netdump.h" +#include "opt_ddb.h" + +/* Verbosity, 0 - 2 */ +#ifndef NETDUMP_DEBUG +#define NETDUMP_DEBUG 0 +#endif + +#define NETDUMP_PORT 20023 /* server udp port number for data */ +#define NETDUMP_ACKPORT 20024 /* client udp port number for acks */ + +#define NETDUMP_DATASIZE 8192 /* how big to let the packets be */ + +#define NETDUMP_HERALD 1 /* broadcast before starting a dump */ +#define NETDUMP_FINISHED 2 /* send after finishing a dump */ +#define NETDUMP_VMCORE 3 /* packet contains dump data */ +#define NETDUMP_KDH 4 /* packet contains kernel dump header */ + +struct netdump_msg_hdr { + u_int32_t type; /* NETDUMP_HERALD, _FINISHED, _VMCORE or _KDH */ + u_int32_t seqno; /* match acks with msgs */ + u_int64_t offset; /* vmcore offset, in bytes */ + u_int32_t len; /* attached data, in bytes */ +}; + +struct netdump_msg { + struct netdump_msg_hdr hdr; + unsigned char data[NETDUMP_DATASIZE];/* real message may contain less */ +}; + +struct netdump_ack { + u_int32_t seqno; /* match acks with msgs */ +}; + +extern struct pcb dumppcb; /* cheat. dumppcb is a static! */ + +/* ---------------------------------------------------------------- */ +/* + * private globals. don't touch. + */ +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_server_mac; + +#define NETDUMP_BROKEN_STATE_BUFFER_SIZE (5 * sizeof(struct mtx)) + +/* ---------------------------------------------------------------- */ +/* + * helpers + */ + +/* + * [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 int +netdump_supported_nic(struct ifnet *ifn) +{ + return ifn->if_netdump != NULL; +} + + +/* ---------------------------------------------------------------- */ +/* + * sysctl pokables. + */ + +static int nd_active = 0; +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 */ +struct ifnet *nd_nic = NULL; +static int nd_force_crash=0; +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 */ + +/* + * [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; + + error=0; + + 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; + + error=0; + 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; +} + +/* From the watchdog code and modified */ +static int +sysctl_force_crash(SYSCTL_HANDLER_ARGS) +{ + int error; + error = sysctl_handle_int(oidp, &nd_force_crash, + sizeof(nd_force_crash), req); + if (error) + return error; + + switch (nd_force_crash) { + case 1: + printf("\nCrashing system...\n"); + for (;;); + break; + case 2: + printf("\nPanic'ing system...\n"); + panic("netdump forced crash"); + break; + case 4: + printf("\nStarting netdump then spinning " + "(to test watchdog trigger)\n"); + // nd_force_crash == 4 is checked in netdump_dumper + case 3: + printf("\nDeadlocking system while holding the em lock\n"); + { + nd_nic->if_netdump->test_get_lock(nd_nic); + for (;;); + } + break; + case 5: + critical_enter(); + panic("netdump forced crash in critical section"); + break; + case 6: + critical_enter(); + printf("\nNetdump spinning in a critical section\n"); + for (;;); + default: + return EINVAL; + } + return 0; +} + +SYSCTL_NODE(_net, OID_AUTO, dump, CTLFLAG_RW, 0, "netdump"); +SYSCTL_INT(_net_dump, OID_AUTO, enable, CTLTYPE_INT|CTLFLAG_RW, &nd_enable, 0, + "enable network dump"); +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, nic, CTLTYPE_STRING|CTLFLAG_RW, &nd_nic, + IFNAMSIZ, sysctl_nic, "A", "NIC to dump on"); +SYSCTL_PROC(_net_dump, OID_AUTO, crash, CTLTYPE_INT|CTLFLAG_RW, + 0, sizeof(nd_force_crash), sysctl_force_crash, "I", "force crashing"); +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"); + +/* ---------------------------------------------------------------- */ + +/* + * [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_server_mac, ETHERTYPE_IP); +} + +/* ---------------------------------------------------------------- */ +/* + * this section provides reliable message delivery (in the absence + * of resource shortages). + */ + +/* + * [netdump_network_poll] + * + * after trapping, instead of assuming that most of the network stack is sane + * just poll the em driver directly for packets + * + * Parameters: + * void + * + * Returns: + * void + */ +static void +netdump_network_poll(void) +{ + /* Poll directly from em */ + nd_nic->if_netdump->poll_locked(nd_nic, POLL_AND_CHECK_STATUS, 1000); +} + + +/* + * [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_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); + + 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; +} + +/* ---------------------------------------------------------------- */ + +static void nd_handle_ip(struct mbuf **mb); +static void nd_handle_arp(struct mbuf **mb); +/* + * [netdump_pkt_in] + * + * Handler for incoming packets directly from the network adapter + * Identifies the packet type (IP or ARP) and passes it along to one of the + * helper functions nd_handle_ip or nd_handle_arp. + * + * Parameters: + * ifp the interface the packet came from (should be nd_nic) + * m an mbuf containing the packet received + * + * Return value: + * void + */ +/* Bits from sys/net/if_ethersubr.c:ether_input, + sys/net/if_ethersubr.c:ether_demux */ +static void +netdump_pkt_in(struct ifnet *ifp, struct mbuf *m) +{ + struct ether_header *eh; + u_short etype; + + /* Ethernet processing */ + + if (NETDUMP_DEBUG >= 2) { + if_printf(ifp, "Processing packet...\n"); + } + + if ((m->m_flags & M_PKTHDR) == 0) { + if (NETDUMP_DEBUG) + if_printf(ifp, "discard frame w/o packet header\n"); + goto done; + } + if (m->m_len < ETHER_HDR_LEN) { + if (NETDUMP_DEBUG) + if_printf(ifp, "discard frome w/o leading ethernet " + "header (len %u pkt len %u)\n", m->m_len, + m->m_pkthdr.len); + goto done; + } + if (m->m_flags & M_HASFCS) { + m_adj(m, -ETHER_CRC_LEN); + m->m_flags &= ~M_HASFCS; + } + eh = mtod(m, struct ether_header *); + m->m_pkthdr.header = eh; + etype = ntohs(eh->ether_type); + if ((ifp->if_nvlans && m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL)) + || etype == ETHERTYPE_VLAN) { + if (NETDUMP_DEBUG) + if_printf(ifp, "ignoring vlan packets\n"); + goto done; + } + /* XXX: Probably should check if we're the recipient MAC address */ + /* Done ethernet processing. Strip off the ethernet header */ + m_adj(m, ETHER_HDR_LEN); + + switch (etype) { + case ETHERTYPE_ARP: + nd_handle_arp(&m); + break; + case ETHERTYPE_IP: + nd_handle_ip(&m); + break; + default: + if (NETDUMP_DEBUG) + if_printf(ifp, "dropping unknown ethertype " + "%hu\n", etype); + break; + } + +done: + if (m) m_freem(m); +} + +/* + * [nd_handle_ip] + * + * Handler for IP packets: checks their sanity and then processes any netdump + * ACK packets it finds. + * + * 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 + */ +/* Bits from sys/net/if_ethersubr.c:ether_input, + sys/net/if_ethersubr.c:ether_demux */ + +/* Bits from sys/netinet/ip_input.c:ip_input, + sys/netinet/udp_usrreq.c:udp_input */ +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=*mb; + int rcv_ackno; + + /* IP processing */ + + if (NETDUMP_DEBUG >= 2) { + printf("nd_handle_ip: Processing IP packet...\n"); + } + + if (m->m_pkthdr.len < sizeof(struct ip)) { + if (NETDUMP_DEBUG) + printf("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) { + if (NETDUMP_DEBUG) + printf("nd_handle_ip: m_pullup failed\n"); + return; + } + + ip = mtod(m, struct ip *); + + /* IP version */ + if (ip->ip_v != IPVERSION) { + if (NETDUMP_DEBUG) + printf("nd_handle_ip: Bad IP version %d\n", ip->ip_v); + return; + } + + /* Header length */ + hlen = ip->ip_hl << 2; + if (hlen < sizeof(struct ip)) { + if (NETDUMP_DEBUG) + printf("nd_handle_ip: Bad IP header length (%hu)\n", + hlen); + return; + } + if (hlen > m->m_len) { + if ((*mb = m = m_pullup(m, hlen)) == NULL) { + if (NETDUMP_DEBUG) + printf("nd_handle_ip: m_pullup failed\n"); + return; + } + ip = mtod(m, struct ip *); + } + + /* Checksum */ + if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { + if (!(m->m_pkthdr.csum_flags & CSUM_IP_VALID)) { + if (NETDUMP_DEBUG) + printf("nd_handle_ip: Bad IP checksum\n"); + return; + } + } else { + if (NETDUMP_DEBUG) + printf("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) { + if (NETDUMP_DEBUG) + printf("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) { + if (NETDUMP_DEBUG) + printf("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) { + if (NETDUMP_DEBUG) + printf("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) { + if (NETDUMP_DEBUG >= 2) + printf("nd_handle_ip: Drop packet not to our IP\n"); + return; + } + + if (ip->ip_p != IPPROTO_UDP) { + if (NETDUMP_DEBUG) + printf("nd_handle_ip: Drop non-UDP packet\n"); + return; + } + + /* Let's not deal with fragments */ + if (ip->ip_off & (IP_MF | IP_OFFMASK)) { + if (NETDUMP_DEBUG) + printf("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)) { + if (NETDUMP_DEBUG) + printf("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) { + if (NETDUMP_DEBUG) + printf("nd_handle_ip: m_pullup failed\n"); + return; + } + udp = mtod(m, struct udpiphdr *); + + if (NETDUMP_DEBUG >= 2) + printf("nd_handle_ip: Processing packet..."); + + if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) { + if (NETDUMP_DEBUG) + printf("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 + * + * 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 + */ +/* Bits from sys/netinet/if_ether.c:arpintr, + sys/netinet/if_ether.c:in_arpinput */ +static void +nd_handle_arp(struct mbuf **mb) +{ + struct mbuf *m=*mb; + struct arphdr *ah; + struct ifnet *ifp = m->m_pkthdr.rcvif; + int req_len, op; + struct in_addr isaddr, itaddr, myaddr; + uint8_t *enaddr; + struct ether_addr dst; + + if (m->m_len < sizeof(struct arphdr) && ((*mb = m = m_pullup(m, + sizeof(struct arphdr))) == NULL)) { + if (NETDUMP_DEBUG) + printf("nd_handle_arp: runt packet: m_pullup failed\n"); + return; + } + ah = mtod(m, struct arphdr *); + + if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) { + if (NETDUMP_DEBUG) + printf("nd_handle_arp: unknown hardware address fmt " + "0x%2D)\n", (unsigned char *)&ah->ar_hrd, ""); + return; + } + + if (ntohs(ah->ar_pro) != ETHERTYPE_IP) { + if (NETDUMP_DEBUG) + printf("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) { + if (NETDUMP_DEBUG) + printf("nd_handle_arp: runt packet: m_pullup failed\n"); + return; + } *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201007281510.o6SFAV5J052045>