Index: usr.sbin/netdumpsrv/netdumpsrv.c =================================================================== --- usr.sbin/netdumpsrv/netdumpsrv.c (.../head/usr.sbin) (revision 0) +++ usr.sbin/netdumpsrv/netdumpsrv.c (.../projects/sv/usr.sbin) (revision 213584) @@ -0,0 +1,911 @@ +/*- + * Copyright (c) 2005-2010 Sandvine Incorporated. 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_DUMPS 256 /* Dumps per IP before to be cleaned out. */ +#define CLIENT_TIMEOUT 600 /* Clients timeout (secs). */ +#define CLIENT_TPASS 10 /* Clients timeout pass (secs). */ + +#define PFLAGS_ABIND 0x01 +#define PFLAGS_DDIR 0x02 +#define PFLAGS_DEBUG 0x04 +#define PFLAGS_SCRIPT 0x08 + +#define LOGERR(m, ...) \ + (*phook)(LOG_ERR | LOG_DAEMON, (m), ## __VA_ARGS__) +#define LOGERR_PERROR(m) \ + (*phook)(LOG_ERR | LOG_DAEMON, "%s: %s\n", m, strerror(errno)) +#define LOGINFO(m, ...) \ + (*phook)(LOG_INFO | LOG_DAEMON, (m), ## __VA_ARGS__) +#define LOGWARN(m, ...) \ + (*phook)(LOG_WARNING | LOG_DAEMON, (m), ## __VA_ARGS__) + +#define client_ntoa(cl) \ + inet_ntoa((cl)->ip) +#define client_pinfo(cl, f, ...) \ + fprintf((cl)->infofile, (f), ## __VA_ARGS__) + +struct netdump_client { + char infofilename[MAXPATHLEN]; + char corefilename[MAXPATHLEN]; + char hostname[NI_MAXHOST]; + time_t last_msg; + SLIST_ENTRY(netdump_client) iter; + struct in_addr ip; + FILE *infofile; + int corefd; + int sock; + unsigned short printed_port_warning: 1; + unsigned short any_data_rcvd: 1; +}; + +/* Clients list. */ +static SLIST_HEAD(, netdump_client) clients = SLIST_HEAD_INITIALIZER(clients); + +/* Program arguments handlers. */ +static uint32_t pflags; +static char dumpdir[MAXPATHLEN]; +static char *handler_script; +static struct in_addr bindip; + +/* Miscellaneous handlers. */ +static struct pidfh *pfh; +static time_t now; +static time_t last_timeout_check; +static int do_shutdown; +static int sock; + +/* Daemon print functions hook. */ +static void (*phook)(int, const char *, ...); + +static struct netdump_client *alloc_client(struct sockaddr_in *sip); +static void eventloop(void); +static void exec_handler(struct netdump_client *client, + const char *reason); +static void free_client(struct netdump_client *client); +static void handle_finish(struct netdump_client *client, + struct netdump_msg *msg); +static void handle_herald(struct sockaddr_in *from, + struct netdump_client *client, + struct netdump_msg *msg); +static void handle_kdh(struct netdump_client *client, + struct netdump_msg *msg); +static void handle_packet(struct netdump_client *client, + struct sockaddr_in *from, const char *fromstr, + struct netdump_msg *msg); +static void handle_timeout(struct netdump_client *client); +static void handle_vmcore(struct netdump_client *client, + struct netdump_msg *msg); +static void phook_printf(int priority, const char *message, ...); +static int receive_message(int isock, struct sockaddr_in *from, + char *fromstr, size_t fromstrlen, + struct netdump_msg *msg); +static void send_ack(struct netdump_client *client, + struct netdump_msg *msg); +static void signal_shutdown(int sig __unused); +static void timeout_clients(void); +static void usage(const char *cmd); + +static void +usage(const char *cmd) +{ + + fprintf(stderr, "Usage: %s [-a bind_addr] [-d dump_dir] [-i script]\n", + cmd); +} + +static void +phook_printf(int priority, const char *message, ...) +{ + va_list ap; + + va_start(ap, message); + if ((priority & LOG_INFO) != 0) { + assert((priority & (LOG_WARNING | LOG_ERR)) == 0); + vprintf(message, ap); + } else + vfprintf(stderr, message, ap); +} + +static struct netdump_client * +alloc_client(struct sockaddr_in *sip) +{ + struct sockaddr_in saddr; + struct netdump_client *client; + struct in_addr *ip; + char *firstdot; + int i, ecode, fd, bufsz; + + assert(sip != NULL); + + client = calloc(1, sizeof(*client)); + if (client == NULL) { + LOGERR_PERROR("calloc()"); + return (NULL); + } + ip = &sip->sin_addr; + bcopy(ip, &client->ip, sizeof(*ip)); + client->corefd = -1; + client->sock = -1; + client->last_msg = now; + + ecode = getnameinfo((struct sockaddr *)sip, sip->sin_len, + client->hostname, sizeof(client->hostname), NULL, 0, NI_NAMEREQD); + if (ecode != 0) { + + /* Can't resolve, try with a numeric IP. */ + ecode = getnameinfo((struct sockaddr *)sip, sip->sin_len, + client->hostname, sizeof(client->hostname), NULL, 0, 0); + if (ecode != 0) { + LOGERR("getnameinfo(): %s\n", gai_strerror(ecode)); + free(client); + return (NULL); + } + } else { + + /* Strip off the domain name */ + firstdot = strchr(client->hostname, '.'); + if (firstdot) + *firstdot = '\0'; + } + + client->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (client->sock == -1) { + LOGERR_PERROR("socket()"); + free(client); + return (NULL); + } + if (fcntl(client->sock, F_SETFL, O_NONBLOCK) == -1) { + LOGERR_PERROR("fcntl()"); + close(client->sock); + free(client); + return (NULL); + } + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = bindip.s_addr; + saddr.sin_port = htons(0); + if (bind(client->sock, (struct sockaddr *)&saddr, sizeof(saddr))) { + LOGERR_PERROR("bind()"); + close(client->sock); + free(client); + return (NULL); + } + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = ip->s_addr; + saddr.sin_port = htons(NETDUMP_ACKPORT); + if (connect(client->sock, (struct sockaddr *)&saddr, sizeof(saddr))) { + LOGERR_PERROR("connect()"); + close(client->sock); + free(client); + return (NULL); + } + + /* It should be enough to hold approximatively twize the chunk size. */ + bufsz = 131072; + if (setsockopt(client->sock, SOL_SOCKET, SO_RCVBUF, &bufsz, + sizeof(bufsz))) { + LOGERR_PERROR("setsockopt()"); + LOGWARN("May drop packets from %s due to small receive buffer\n", + client->hostname); + } + + /* Try info.host.0 through info.host.255 in sequence. */ + for (i = 0; i < MAX_DUMPS; i++) { + snprintf(client->infofilename, sizeof(client->infofilename), + "%s/info.%s.%d", dumpdir, client->hostname, i); + snprintf(client->corefilename, sizeof(client->corefilename), + "%s/vmcore.%s.%d", dumpdir, client->hostname, i); + + /* Try the info file first. */ + fd = open(client->infofilename, O_WRONLY | O_CREAT | O_EXCL, + DEFFILEMODE); + if (fd == -1) { + if (errno != EEXIST) + LOGERR("open(\"%s\"): %s\n", + client->infofilename, strerror(errno)); + continue; + } + client->infofile = fdopen(fd, "w"); + if (client->infofile == NULL) { + LOGERR_PERROR("fdopen()"); + close(fd); + unlink(client->infofilename); + continue; + } + + /* Next make the core file. */ + fd = open(client->corefilename, O_RDWR | O_CREAT | O_EXCL, + DEFFILEMODE); + if (fd == -1) { + + /* Failed. Keep the numbers in sync. */ + fclose(client->infofile); + unlink(client->infofilename); + client->infofile = NULL; + if (errno != EEXIST) + LOGERR("open(\"%s\"): %s\n", + client->corefilename, strerror(errno)); + continue; + } + client->corefd = fd; + break; + } + + if (client->infofile == NULL || client->corefd == -1) { + LOGERR("Can't create output files for new client %s [%s]\n", + client->hostname, client_ntoa(client)); + if (client->infofile) + fclose(client->infofile); + if (client->corefd != -1) + close(client->corefd); + if (client->sock != -1) + close(client->sock); + free(client); + return (NULL); + } + SLIST_INSERT_HEAD(&clients, client, iter); + return (client); +} + +static void +free_client(struct netdump_client *client) +{ + + assert(client != NULL); + + /* Remove from the list. Ignore errors from close() routines. */ + SLIST_REMOVE(&clients, client, netdump_client, iter); + fclose(client->infofile); + close(client->corefd); + close(client->sock); + free(client); +} + +static void +exec_handler(struct netdump_client *client, const char *reason) +{ + int pid; + + assert(client != NULL); + + /* If no script is specified this is a no-op. */ + if ((pflags & PFLAGS_SCRIPT) == 0) + return; + + pid = fork(); + + /* + * The function is invoked in critical conditions, thus just exiting + * without reporting errors is fine. + */ + if (pid == -1) { + LOGERR_PERROR("fork()"); + return; + } else if (pid != 0) { + close(sock); + pidfile_close(pfh); + if (execl(handler_script, handler_script, reason, + client_ntoa(client), client->hostname, + client->infofilename, client->corefilename, NULL) == -1) { + LOGERR_PERROR("fork()"); + _exit(1); + } + } +} + +static void +handle_timeout(struct netdump_client *client) +{ + + assert(client != NULL); + + LOGINFO("Client %s timed out\n", client_ntoa(client)); + client_pinfo(client, "Dump incomplete: client timed out\n"); + exec_handler(client, "timeout"); + free_client(client); +} + +static void +timeout_clients(void) +{ + struct netdump_client *client, *tmp; + + /* Only time out clients every 10 seconds. */ + if (now - last_timeout_check < CLIENT_TPASS) + return; + last_timeout_check = now; + + /* Traverse the list looking for stale clients. */ + SLIST_FOREACH_SAFE(client, &clients, iter, tmp) { + if (client->last_msg + CLIENT_TIMEOUT < now) { + LOGINFO("Timingout with such values: %jd + %jd < %jd\n", + (intmax_t)client->last_msg, + (intmax_t)CLIENT_TIMEOUT, (intmax_t)now); + handle_timeout(client); + } + } +} + +static void +send_ack(struct netdump_client *client, struct netdump_msg *msg) +{ + struct netdump_ack ack; + int tryagain; + + assert(client != NULL && msg != NULL); + + bzero(&ack, sizeof(ack)); + ack.na_seqno = htonl(msg->nm_hdr.mh_seqno); + do { + tryagain = 0; + if (send(client->sock, &ack, sizeof(ack), 0) == -1) { + if (errno == EINTR) { + tryagain = 1; + continue; + } + + /* + * XXX: On EAGAIN, we should probably queue the packet + * to be sent when the socket is writable but + * that is too much effort, since it is mostly + * harmless to wait for the client to retransmit. + */ + LOGERR_PERROR("send()"); + } + } while (tryagain); +} + +static void +handle_herald(struct sockaddr_in *from, struct netdump_client *client, + struct netdump_msg *msg) +{ + + assert(from != NULL && msg != NULL); + + if (client != NULL) { + if (client->any_data_rcvd == 0) { + + /* Must be a retransmit of the herald packet. */ + send_ack(client, msg); + return; + } + + /* An old connection must have timed out. Clean it up first. */ + handle_timeout(client); + } + + client = alloc_client(from); + if (client == NULL) { + LOGERR("handle_herald(): new client allocation failure\n"); + return; + } + client_pinfo(client, "Dump from %s [%s]\n", client->hostname, + client_ntoa(client)); + LOGINFO("New dump from client %s [%s] (to %s)\n", client->hostname, + client_ntoa(client), client->corefilename); + send_ack(client, msg); +} + +static void +handle_kdh(struct netdump_client *client, struct netdump_msg *msg) +{ + time_t t; + uint64_t dumplen; + struct kerneldumpheader *h; + int parity_check; + + assert(msg != NULL); + + if (client == NULL) + return; + + client->any_data_rcvd = 1; + h = (struct kerneldumpheader *)msg->nm_data; + if (msg->nm_hdr.len < sizeof(struct kerneldumpheader)) { + LOGERR("Bad KDH from %s [%s]: packet too small\n", + client->hostname, client_ntoa(client)); + client_pinfo(client, "Bad KDH: packet too small\n"); + fflush(client->infofile); + send_ack(client, msg); + return; + } + parity_check = kerneldump_parity(h); + + /* Make sure all the strings are null-terminated. */ + h->architecture[sizeof(h->architecture) - 1] = '\0'; + h->hostname[sizeof(h->hostname) - 1] = '\0'; + h->versionstring[sizeof(h->versionstring) - 1] = '\0'; + h->panicstring[sizeof(h->panicstring) - 1] = '\0'; + + client_pinfo(client, " Architecture: %s\n", h->architecture); + client_pinfo(client, " Architecture version: %d\n", + dtoh32(h->architectureversion)); + dumplen = dtoh64(h->dumplength); + client_pinfo(client, " Dump length: %lldB (%lld MB)\n", + (long long)dumplen, (long long)(dumplen >> 20)); + client_pinfo(client, " blocksize: %d\n", dtoh32(h->blocksize)); + t = dtoh64(h->dumptime); + client_pinfo(client, " Dumptime: %s", ctime(&t)); + client_pinfo(client, " Hostname: %s\n", h->hostname); + client_pinfo(client, " Versionstring: %s", h->versionstring); + client_pinfo(client, " Panicstring: %s\n", h->panicstring); + client_pinfo(client, " Header parity check: %s\n", + parity_check ? "Fail" : "Pass"); + fflush(client->infofile); + + LOGINFO("(KDH from %s [%s])", client->hostname, client_ntoa(client)); + send_ack(client, msg); +} + +static void +handle_vmcore(struct netdump_client *client, struct netdump_msg *msg) +{ + + assert(msg != NULL); + + if (client == NULL) + return; + + client->any_data_rcvd = 1; + if (msg->nm_hdr.mh_seqno % 11523 == 0) { + + /* Approximately every 16MB with MTU of 1500 */ + LOGINFO("."); + } + if (pwrite(client->corefd, msg->nm_data, msg->nm_hdr.len, + msg->nm_hdr.mh_offset) == -1) { + LOGERR("pwrite (for client %s [%s]): %s\n", client->hostname, + client_ntoa(client), strerror(errno)); + client_pinfo(client, + "Dump unsuccessful: write error @ offset %08"PRIx64": %s\n", + msg->nm_hdr.mh_offset, strerror(errno)); + exec_handler(client, "error"); + free_client(client); + return; + } + send_ack(client, msg); +} + +static void +handle_finish(struct netdump_client *client, struct netdump_msg *msg) +{ + + assert(msg != NULL); + + if (client == NULL) + return; + + LOGINFO("\nCompleted dump from client %s [%s]\n", client->hostname, + client_ntoa(client)); + client_pinfo(client, "Dump complete\n"); + send_ack(client, msg); + exec_handler(client, "success"); + free_client(client); +} + + +static int +receive_message(int isock, struct sockaddr_in *from, char *fromstr, + size_t fromstrlen, struct netdump_msg *msg) +{ + socklen_t fromlen; + ssize_t len; + + assert(from != NULL && fromstr != NULL && msg != NULL); + + bzero(from, sizeof(*from)); + from->sin_family = AF_INET; + from->sin_len = fromlen = sizeof(*from); + from->sin_port = 0; + from->sin_addr.s_addr = INADDR_ANY; + + len = recvfrom(isock, msg, sizeof(*msg), 0, (struct sockaddr *)from, + &fromlen); + if (len == -1) { + + /* + * As long as some callers may discard the errors printing + * in defined circumstances, leave them the choice and avoid + * any error reporting. + */ + return (-1); + } + + snprintf(fromstr, fromstrlen, "%s:%hu", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + if ((size_t)len < sizeof(struct netdump_msg_hdr)) { + LOGERR("Ignoring runt packet from %s (got %zu)\n", fromstr, + (size_t)len); + return (0); + } + + /* Convert byte order. */ + msg->nm_hdr.mh_type = ntohl(msg->nm_hdr.mh_type); + msg->nm_hdr.mh_seqno = ntohl(msg->nm_hdr.mh_seqno); + msg->nm_hdr.mh_offset = be64toh(msg->nm_hdr.mh_offset); + msg->nm_hdr.mh_len = ntohl(msg->nm_hdr.mh_len); + + if ((size_t)len < sizeof(struct netdump_msg_hdr) + msg->nm_hdr.mh_len) { + LOGERR("Packet too small from %s (got %zu, expected %zu)\n", + fromstr, (size_t)len, + sizeof(struct netdump_msg_hdr) + msg->nm_hdr.mh_len); + return (0); + } + return (len); +} + +static void +handle_packet(struct netdump_client *client, struct sockaddr_in *from, + const char *fromstr, struct netdump_msg *msg) +{ + + assert(from != NULL && fromstr != NULL && msg != NULL); + + if (client != NULL) + client->last_msg = time(NULL); + + switch (msg->nm_hdr.mh_type) { + case NETDUMP_HERALD: + handle_herald(from, client, msg); + break; + case NETDUMP_KDH: + handle_kdh(client, msg); + break; + case NETDUMP_VMCORE: + handle_vmcore(client, msg); + break; + case NETDUMP_FINISHED: + handle_finish(client, msg); + break; + default: + LOGERR("Received unknown message type %d from %s\n", + msg->nm_hdr.mh_type, fromstr); + } +} + +static void +eventloop(void) +{ + struct netdump_msg msg; + char fromstr[INET_ADDRSTRLEN + 6]; + fd_set readfds; + struct sockaddr_in from; + struct timeval tv; + struct netdump_client *client, *tmp; + int len, maxfd; + + while (do_shutdown == 0) { + maxfd = sock + 1; + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + SLIST_FOREACH(client, &clients, iter) { + FD_SET(client->sock, &readfds); + if (maxfd <= client->sock) + maxfd = client->sock+1; + } + + /* So that we time out clients regularly. */ + tv.tv_sec = CLIENT_TPASS; + tv.tv_usec = 0; + if (select(maxfd, &readfds, NULL, NULL, &tv) == -1) { + if (errno == EINTR) + continue; + LOGERR_PERROR("select()"); + + /* + * Errors with select() probably will not go away + * with simple retrying. + */ + pidfile_remove(pfh); + exit(1); + } + now = time(NULL); + if (FD_ISSET(sock, &readfds)) { + len = receive_message(sock, &from, fromstr, + sizeof(fromstr), &msg); + if (len == -1) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + pidfile_remove(pfh); + LOGERR_PERROR("recvfrom()"); + exit(1); + } + } else if (len != 0) { + + /* + * With len == 0 the packet was rejected + * (probably because it was too small) so just + * ignore this case. + */ + + /* Check if they are on the clients list. */ + SLIST_FOREACH(client, &clients, iter) + if (client->ip.s_addr == + from.sin_addr.s_addr) + break; + + /* + * Technically, clients should not be + * responding on the server port, so client + * should be NULL, however, if they insist on + * doing so, it's not really going to hurt + * anything (except maybe fill up the server + * socket's receive buffer), so still + * accept it. The only possibly legitimate case + * is if there's a new dump starting and the + * previous one didn't finish cleanly. Handle + * this by suppressing the error on HERALD + * packets. + */ + if (client != NULL && + msg.nm_hdr.mh_type != NETDUMP_HERALD && + client->printed_port_warning == 0) { + LOGWARN("Client %s responding on server port\n", + client->hostname); + client->printed_port_warning = 1; + } + handle_packet(client, &from, fromstr, &msg); + } + } + + /* + * handle_packet() and handle_timeout() may free the client, + * handle stale pointers. + */ + SLIST_FOREACH_SAFE(client, &clients, iter, tmp) { + if (FD_ISSET(client->sock, &readfds)) { + len = receive_message(client->sock, &from, + fromstr, sizeof(fromstr), &msg); + if (len == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + LOGERR_PERROR("recvfrom()"); + + /* + * Client socket is broken for + * some reason. + */ + handle_timeout(client); + } else if (len != 0) { + + /* + * With len == 0 the packet was + * rejected (probably because it was + * too small) so just ignore this case. + */ + + FD_CLR(client->sock, &readfds); + handle_packet(client, &from, fromstr, + &msg); + } + } + } + timeout_clients(); + } + LOGINFO("Shutting down..."); + + /* + * Clients is the head of the list, so clients != NULL iff the list + * is not empty. Call it a timeout so that the scripts get run. + */ + while (!SLIST_EMPTY(&clients)) + handle_timeout(SLIST_FIRST(&clients)); +} + +static void +signal_shutdown(int sig __unused) +{ + + do_shutdown = 1; +} + +int +main(int argc, char **argv) +{ + struct stat statbuf; + struct sockaddr_in bindaddr; + struct sigaction sa; + int ch; + + pfh = pidfile_open(NULL, 0600, NULL); + if (pfh == NULL) { + if (errno == EEXIST) + printf("Instance of netdump already running\n"); + else + printf("Impossible to open the pid file\n"); + exit(1); + } + + while ((ch = getopt(argc, argv, "a:Dd:i:")) != -1) { + switch (ch) { + case 'a': + pflags |= PFLAGS_ABIND; + if (!inet_aton(optarg, &bindip)) { + pidfile_remove(pfh); + fprintf(stderr, "Invalid bind IP specified\n"); + exit(1); + } + printf("Listening on IP %s\n", optarg); + break; + case 'D': + pflags |= PFLAGS_DEBUG; + break; + case 'd': + pflags |= PFLAGS_DDIR; + assert(dumpdir[0] == '\0'); + strncpy(dumpdir, optarg, sizeof(dumpdir) - 1); + break; + case 'i': + pflags |= PFLAGS_SCRIPT; + + /* + * When suddenly closing the process for an error, + * it is unuseful to take care of handler_script + * deallocation as long as the process will _exit(2) + * anyway. + */ + handler_script = strdup(optarg); + if (handler_script == NULL) { + pidfile_remove(pfh); + perror("strdup()"); + fprintf(stderr, "Unable to set script file\n"); + exit(1); + } + if (access(handler_script, F_OK | X_OK)) { + pidfile_remove(pfh); + perror("access()"); + fprintf(stderr, + "Unable to access script file\n"); + exit(1); + } + break; + default: + pidfile_remove(pfh); + usage(argv[0]); + exit(1); + } + } + if ((pflags & PFLAGS_ABIND) == 0) { + bindip.s_addr = INADDR_ANY; + printf("Default: listening on all interfaces\n"); + } + if ((pflags & PFLAGS_DDIR) == 0) { + strcpy(dumpdir, "/var/crash"); + printf("Default: dumping on /var/crash/\n"); + } + if ((pflags & PFLAGS_DEBUG) == 0) + phook = syslog; + else + phook = phook_printf; + + /* Further sanity checks on dump location. */ + if (stat(dumpdir, &statbuf)) { + pidfile_remove(pfh); + perror("stat()"); + fprintf(stderr, "Invalid dump location specified\n"); + exit(1); + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + pidfile_remove(pfh); + fprintf(stderr, "Dump location is not a directory\n"); + exit(1); + } + if (access(dumpdir, F_OK | W_OK)) { + fprintf(stderr, + "Warning: May be unable to write into dump location: %s\n", + strerror(errno)); + } + + if ((pflags & PFLAGS_DEBUG) == 0 && daemon(0, 0) == -1) { + pidfile_remove(pfh); + perror("daemon()"); + fprintf(stderr, "Impossible to demonize the process\n"); + exit(1); + } + pidfile_write(pfh); + + /* Set up the server socket. */ + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + pidfile_remove(pfh); + LOGERR_PERROR("socket()"); + exit(1); + } + bzero(&bindaddr, sizeof(bindaddr)); + bindaddr.sin_len = sizeof(bindaddr); + bindaddr.sin_family = AF_INET; + bindaddr.sin_addr.s_addr = bindip.s_addr; + bindaddr.sin_port = htons(NETDUMP_PORT); + if (bind(sock, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) { + pidfile_remove(pfh); + close(sock); + LOGERR_PERROR("bind()"); + exit(1); + } + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + pidfile_remove(pfh); + close(sock); + LOGERR_PERROR("fcntl()"); + exit(1); + } + + /* Override some signal handlers. */ + bzero(&sa, sizeof(sa)); + sa.sa_handler = signal_shutdown; + if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL)) { + pidfile_remove(pfh); + close(sock); + LOGERR_PERROR("sigaction(SIGINT | SIGTERM)"); + exit(1); + } + bzero(&sa, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + if (sigaction(SIGCHLD, &sa, NULL)) { + pidfile_remove(pfh); + LOGERR_PERROR("sigaction(SIGCHLD)"); + close(sock); + exit(1); + } + + LOGINFO("Waiting for clients.\n"); + eventloop(); + + pidfile_remove(pfh); + return (0); +} Index: usr.sbin/netdumpsrv/Makefile =================================================================== --- usr.sbin/netdumpsrv/Makefile (.../head/usr.sbin) (revision 0) +++ usr.sbin/netdumpsrv/Makefile (.../projects/sv/usr.sbin) (revision 213584) @@ -0,0 +1,13 @@ +# $FreeBSD$ + +PROG= netdumpsrv +MAN= netdumpsrv.8 + +CFLAGS+= -DNDEBUG -I${.CURDIR} + +WARNS?= 6 + +DPADD= ${LIBUTIL} +LDADD= -lutil + +.include Index: usr.sbin/netdumpsrv/netdumpsrv.8 =================================================================== --- usr.sbin/netdumpsrv/netdumpsrv.8 (.../head/usr.sbin) (revision 0) +++ usr.sbin/netdumpsrv/netdumpsrv.8 (.../projects/sv/usr.sbin) (revision 213584) @@ -0,0 +1,76 @@ +.\" Copyright (c) 2010 Sandvine Incorporated. 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. +.\" +.\" $FreeBSD$ +.\" +.Dd August 23, 2010 +.Dt NETDUMPSRV 8 +.Os +.Sh NAME +.Nm netdumpsrv +.Nd receive kernel core dumps over the network +.Sh SYNOPSIS +.Nm +.Op Fl a Ar addr +.Op Fl D +.Op Fl d Ar dumpdir +.Op Fl i Ar script +.Sh DESCRIPTION +The +.Nm +utility listens on a UDP socket for incoming connections +from a +.Fx +kernel core dumping over the network. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +Bind the daemon to the given address +.Dq Pa addr . +.It Fl D +Run the utility in debugging mode. +The daemon version is not entered while the output +is printed entirely on the console. +.It Fl d +Save the core dumps to the specified +.Dq Pa dumpdir +directory. +.It Fl i +Execute the script +.Dq Pa script +after each dump received. +The script accepts the following strings as parameters: +its name, +reasons for invokation, +the client address, +the client hostname, +the info file name and the core dump file name. +.El +.Sh SEE ALSO +.Xr dumpon 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 9.0 . Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile (.../head/usr.sbin) (revision 213582) +++ usr.sbin/Makefile (.../projects/sv/usr.sbin) (revision 213584) @@ -47,6 +47,7 @@ SUBDIR= adduser \ mptutil \ mtest \ mtree \ + netdumpsrv \ newsyslog \ nfscbd \ nfsd \ Index: sys/arm/arm/minidump_machdep.c =================================================================== --- sys/arm/arm/minidump_machdep.c (.../head/sys) (revision 213582) +++ sys/arm/arm/minidump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -276,14 +276,21 @@ minidumpsys(struct dumperinfo *di) dumpsize += PAGE_SIZE; - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; } - - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; progress = dumpsize; /* Initialize mdhdr */ Index: sys/arm/arm/dump_machdep.c =================================================================== --- sys/arm/arm/dump_machdep.c (.../head/sys) (revision 213582) +++ sys/arm/arm/dump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -303,13 +303,20 @@ dumpsys(struct dumperinfo *di) dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_ARM_VERSION, dumpsize, di->blocksize); Index: sys/sparc64/sparc64/dump_machdep.c =================================================================== --- sys/sparc64/sparc64/dump_machdep.c (.../head/sys) (revision 213582) +++ sys/sparc64/sparc64/dump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -157,18 +157,23 @@ dumpsys(struct dumperinfo *di) DEV_BSIZE); size += hdrsize; - totsize = size + 2 * sizeof(kdh); - if (totsize > di->mediasize) { - printf("Insufficient space on device (need %ld, have %ld), " - "refusing to dump.\n", (long)totsize, - (long)di->mediasize); - error = ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + totsize = size + 2 * sizeof(kdh); + if (totsize > di->mediasize) { + printf("Insufficient space on device (need %ld, " + "have %ld), refusing to dump.\n", (long)totsize, + (long)di->mediasize); + error = ENOSPC; + goto fail; + } + + /* Determine dump offset on device. */ + dumplo = di->mediaoffset + di->mediasize - totsize; } - /* Determine dump offset on device. */ - dumplo = di->mediaoffset + di->mediasize - totsize; - mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_SPARC64_VERSION, size, di->blocksize); printf("Dumping %lu MB (%d chunks)\n", (u_long)(size >> 20), nreg); Index: sys/conf/files =================================================================== --- sys/conf/files (.../head/sys) (revision 213582) +++ sys/conf/files (.../projects/sv/sys) (revision 213584) @@ -2593,6 +2593,7 @@ netinet/ip_ipsec.c optional inet ipsec netinet/ip_mroute.c optional mrouting inet | mrouting inet6 netinet/ip_options.c optional inet netinet/ip_output.c optional inet +netinet/netdump_client.c optional inet netdump_client netinet/raw_ip.c optional inet netinet/sctp_asconf.c optional inet sctp netinet/sctp_auth.c optional inet sctp Index: sys/conf/options =================================================================== --- sys/conf/options (.../head/sys) (revision 213582) +++ sys/conf/options (.../projects/sv/sys) (revision 213584) @@ -281,6 +281,10 @@ NFS_ROOT opt_nfsroot.h # SMB/CIFS requester NETSMB opt_netsmb.h +# Netdump client kernel support +NETDUMP_CLIENT opt_netdump.h +NETDUMP_CLIENT_DEBUG opt_netdump.h + # Options used only in subr_param.c. HZ opt_param.h MAXFILES opt_param.h Index: sys/kern/kern_shutdown.c =================================================================== --- sys/kern/kern_shutdown.c (.../head/sys) (revision 213582) +++ sys/kern/kern_shutdown.c (.../projects/sv/sys) (revision 213584) @@ -130,8 +130,8 @@ int rebooting; /* system is rebooting */ static struct dumperinfo dumper; /* our selected dumper */ /* Context information for dump-debuggers. */ -static struct pcb dumppcb; /* Registers. */ -static lwpid_t dumptid; /* Thread ID. */ +struct pcb dumppcb; /* Registers. */ +lwpid_t dumptid; /* Thread ID. */ static void boot(int) __dead2; static void poweroff_wait(void *, int); @@ -689,7 +689,9 @@ dump_write(struct dumperinfo *di, void *virtual, v off_t offset, size_t length) { - if (length != 0 && (offset < di->mediaoffset || + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) != 0 && length != 0 && + (offset < di->mediaoffset || offset - di->mediaoffset + length > di->mediasize)) { printf("Attempt to write outside dump device boundaries.\n"); return (ENXIO); Index: sys/ia64/ia64/dump_machdep.c =================================================================== --- sys/ia64/ia64/dump_machdep.c (.../head/sys) (revision 213582) +++ sys/ia64/ia64/dump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -231,13 +231,20 @@ dumpsys(struct dumperinfo *di) dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_IA64_VERSION, dumpsize, di->blocksize); Index: sys/netinet/netdump.h =================================================================== --- sys/netinet/netdump.h (.../head/sys) (revision 0) +++ sys/netinet/netdump.h (.../projects/sv/sys) (revision 213584) @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2005-2010 Sandvine Incorporated + * Copyright (c) 2000 Darrell Anderson + * 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. + * + * $FreeBSD$ + */ + +#ifndef _NETINET_NETDUMP_H_ +#define _NETINET_NETDUMP_H_ + +#include + +#define NETDUMP_PORT 20023 /* Server udp port number for data. */ +#define NETDUMP_ACKPORT 20024 /* Client udp port number for acks. */ + +#define NETDUMP_HERALD 1 /* Broadcast before starting a dump. */ +#define NETDUMP_FINISHED 2 /* Send after finishing a dump. */ +#define NETDUMP_VMCORE 3 /* Contains dump datas. */ +#define NETDUMP_KDH 4 /* Contains kernel dump header. */ + +#define NETDUMP_DATASIZE 8192 /* Packets payload. */ + +struct netdump_msg_hdr { + uint32_t mh_type; /* NETDUMP_HERALD, _FINISHED, _VMCORE, _KDH. */ + uint32_t mh_seqno; /* Match acks with msgs. */ + uint64_t mh_offset; /* vmcore offset (bytes). */ + uint32_t mh_len; /* Attached data (bytes). */ + uint32_t mh__pad; /* Pad space matching 32- and 64-bits archs. */ +}; + +struct netdump_ack { + uint32_t na_seqno; /* Match acks with msgs. */ +}; + +struct netdump_msg { + struct netdump_msg_hdr nm_hdr; + uint8_t nm_data[NETDUMP_DATASIZE]; +}; + +#ifdef _KERNEL + +typedef void ndumplock_handler_t(struct ifnet *); + +struct netdump_methods { + poll_handler_t *ne_poll_locked; + poll_handler_t *ne_poll_unlocked; + ndumplock_handler_t *ne_disable_intr; + ndumplock_handler_t *ne_enable_intr; +}; + +#endif + +#endif /* !_NETINET_NETDUMP_H_ */ Index: sys/netinet/netdump_client.c =================================================================== --- sys/netinet/netdump_client.c (.../head/sys) (revision 0) +++ sys/netinet/netdump_client.c (.../projects/sv/sys) (revision 213584) @@ -0,0 +1,1355 @@ +/*- + * Copyright (c) 2005 Sandvine Incorporated. All rights reserved. + * Copyright (c) 2000 Darrell Anderson + * 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_kdb.h" +#include "opt_netdump.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef DDB +#include +#endif + +#ifdef VIMAGE +#error "Netdump kernel support cannot be compiled with VIMAGE option" +#endif + +#ifdef NETDUMP_CLIENT_DEBUG +#define NETDDEBUG(f, ...) printf((f), ## __VA_ARGS__) +#define NETDDEBUG_IF(i, f, ...) if_printf((i), (f), ## __VA_ARGS__) +#if NETDUMP_CLIENT_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 + +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_CLIENT_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 */ + +/* Tunables storages. */ +static char nd_server_tun[INET_ADDRSTRLEN]; +static char nd_client_tun[INET_ADDRSTRLEN]; +static char nd_gw_tun[INET_ADDRSTRLEN]; +static char nd_nic_tun[IFNAMSIZ]; + +/* + * [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_ndumpfuncs != 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 { + IFNET_RLOCK_NOSLEEP(); + if ((ifn = TAILQ_FIRST(&V_ifnet)) != NULL) do { + if (!strcmp(ifn->if_xname, buf)) break; + } while ((ifn = TAILQ_NEXT(ifn, if_link)) != NULL); + IFNET_RUNLOCK_NOSLEEP(); + + if (!ifn) return ENODEV; + if (!netdump_supported_nic(ifn)) return EINVAL; + } + + (*(struct ifnet **)arg1) = ifn; + } + + return error; +} + +#ifdef NETDUMP_CLIENT_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: + critical_enter(); + panic("Forcing spourious critical section"); + break; + case 4: + 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"); +#ifdef NETDUMP_CLIENT_DEBUG +SYSCTL_NODE(_debug, OID_AUTO, netdump, CTLFLAG_RW, NULL, "Netdump debugging"); +SYSCTL_PROC(_debug_netdump, OID_AUTO, crash, CTLTYPE_INT|CTLFLAG_RW, 0, + sizeof(int), sysctl_force_crash, "I", "force crashing"); +#endif +TUNABLE_STR("net.dump.server", nd_server_tun, sizeof(nd_server_tun)); +TUNABLE_STR("net.dump.client", nd_client_tun, sizeof(nd_client_tun)); +TUNABLE_STR("net.dump.gateway", nd_gw_tun, sizeof(nd_gw_tun)); +TUNABLE_STR("net.dump.nic", nd_nic_tun, sizeof(nd_nic_tun)); +TUNABLE_INT("net.dump.enable", &nd_enable); + +/*- + * 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 *); + memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); + memcpy(eh->ether_dhost, dst.octet, 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; + + /* Fill-up a broadcast address. */ + memset(&bcast, 0xFF, ETHER_ADDR_LEN); + 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); + memcpy(ar_sha(ah), IF_LLADDR(nd_nic), 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->mh_seqno = htonl(nd_seqno+i); + nd_msg_hdr->mh_type = htonl(type); + nd_msg_hdr->mh_offset = htobe64(offset + sent_so_far); + nd_msg_hdr->mh_len = htonl(pktlen); + nd_msg_hdr->mh__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, 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 */ + 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, NULL); + 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 *); + + 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->na_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(ah->ar_hrd) != ARPHRD_IEEE802 && + ntohs(ah->ar_hrd) != ARPHRD_ARCNET && + ntohs(ah->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); + memcpy(&isaddr, ar_spa(ah), sizeof(isaddr)); + memcpy(&itaddr, ar_tpa(ah), 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; + } + memcpy(nd_gw_mac.octet, ar_sha(ah), + 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; + } + + memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); + memcpy(ar_sha(ah), enaddr, ah->ar_hln); + memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); + memcpy(ar_spa(ah), &itaddr, 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; + + memcpy(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 + * Identifies the packet type (IP or ARP) and passes it along to one of the + * helper functions nd_handle_ip or nd_handle_arp. + * + * It needs to replicate partially the behaviour of ether_input() and + * ether_demux(). + * + * Parameters: + * ifp the interface the packet came from (should be nd_nic) + * m an mbuf containing the packet received + * + * Return value: + * void + */ +static void +netdump_pkt_in(struct ifnet *ifp, struct mbuf *m) +{ + struct ether_header *eh; + u_short etype; + + /* Ethernet processing */ + if ((m->m_flags & M_PKTHDR) == 0) { + NETDDEBUG_IF(ifp, "discard frame w/o packet header\n"); + goto done; + } + if (m->m_len < ETHER_HDR_LEN) { + NETDDEBUG_IF(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 ((m->m_flags & M_VLANTAG) != 0 || etype == ETHERTYPE_VLAN) { + NETDDEBUG_IF(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: + NETDDEBUG_IF(ifp, "dropping unknown ethertype %hu\n", + etype); + break; + } + +done: + if (m) m_freem(m); +} + +/* + * [netdump_network_poll] + * + * after trapping, instead of assuming that most of the network stack is sane + * just poll the driver directly for packets + * + * Parameters: + * void + * + * Returns: + * void + */ +static void +netdump_network_poll() +{ + +#if defined(KDB) && !defined(KDB_UNATTENDED) + if (panicstr != NULL) + nd_nic->if_ndumpfuncs->ne_poll_unlocked(nd_nic, + POLL_AND_CHECK_STATUS, 1000); + else +#endif + nd_nic->if_ndumpfuncs->ne_poll_locked(nd_nic, + POLL_AND_CHECK_STATUS, 1000); +} + +/*- + * Dumping specific primitives. + */ + +/* + * [netdump_dumper] + * + * Callback from dumpsys() to dump a chunk of memory + * Copies it out to our static buffer then sends it across the network + * Detects the initial KDH and makes sure it's given a special packet type + * + * Parameters: + * priv Unused. Optional private pointer. + * virtual Virtual address (where to read the data from) + * physical Unused. Physical memory address. + * offset Offset from start of core file + * length Data length + * + * Return value: + * 0 on success + * errno on error + */ +static int +netdump_dumper(void *priv, void *virtual, vm_offset_t physical, off_t offset, + size_t length) +{ + int err; + int msgtype = NETDUMP_VMCORE; + + (void)priv; + + NETDDEBUGV("netdump_dumper(%p, %p, %"PRIxPTR", %"PRIx64", %zu)\n", + priv, virtual, physical, (uint64_t)offset, length); + + if (length > sizeof(buf)) + return ENOSPC; + /* + * The first write (at offset 0) is the kernel dump header. Flag it + * for the server to treat specially. XXX: This doesn't strip out the + * footer KDH, although it shouldn't hurt anything. + */ + if (offset == 0 && length > 0) + msgtype = NETDUMP_KDH; + else if (offset > 0) + offset -= sizeof(struct kerneldumpheader); + + memcpy(buf, virtual, length); + err=netdump_send(msgtype, offset, buf, length); + if (err) { + dump_failed=1; + return err; + } + + return 0; +} + +/* + * [netdump_trigger] + * + * called from kern_shutdown during "boot" (invoked on panic). perform a + * network dump, and if successful cancel the normal disk dump. + * + * Parameters: + * arg unused + * howto boot flags (only dump if RB_DUMP set) + * + * Returns: + * void + */ +static void +netdump_trigger(void *arg, int howto) +{ + struct dumperinfo dumper; + void (*old_if_input)(struct ifnet *, struct mbuf *)=NULL; + + if ((howto&(RB_HALT|RB_DUMP))!=RB_DUMP || !nd_enable || cold || + dumping) + return; + + if (!nd_nic) { + printf("netdump_trigger: Can't netdump: no NIC given\n"); + return; + } + + if (nd_server.s_addr == INADDR_ANY) { + printf("netdump_trigger: Can't netdump; no server IP given\n"); + return; + } + if (nd_client.s_addr == INADDR_ANY) { + printf("netdump_trigger: Can't netdump; no client IP given\n"); + return; + } + + /* + * netdump is invoked as a pre-sync handler instead of as + * a real dumpdev dump routine (that is because shutdown handlers + * run as post-sync handlers, earlier than dumping routines + * taking place, and thus network and devices may not be further + * available). + * Make sure, artificially, the dump context is set so a debugger + * can find the stack trace. + */ + savectx(&dumppcb); + dumptid = curthread->td_tid; + dumping++; + + /* + * nd_server_port could have switched after the first ack the + * first time it gets called. Adjust it accordingly. + */ + nd_server_port = NETDUMP_PORT; + if ((nd_nic->if_capenable & IFCAP_POLLING) == 0) { +#if defined(KDB) && !defined(KDB_UNATTENDED) + if (panicstr == NULL) +#endif + nd_nic->if_ndumpfuncs->ne_disable_intr(nd_nic); + } + + /* Make the card use *our* receive callback */ + old_if_input = nd_nic->if_input; + nd_nic->if_input = netdump_pkt_in; + + if (nd_gw.s_addr == INADDR_ANY) { + nd_gw.s_addr = nd_server.s_addr; + } + printf("\n-----------------------------------\n"); + printf("netdump in progress. searching for server.. "); + if (netdump_arp_server()) { + printf("Failed to locate server MAC address\n"); + goto trig_abort; + } + if (netdump_send(NETDUMP_HERALD, 0, NULL, 0) != 0) { + printf("Failed to contact netdump server\n"); + goto trig_abort; + } + printf("dumping to %s (%6D)\n", inet_ntoa(nd_server), + nd_gw_mac.octet, ":"); + printf("-----------------------------------\n"); + + /* + * dump memory. + */ + dumper.dumper = netdump_dumper; + dumper.priv = NULL; + dumper.blocksize = NETDUMP_DATASIZE; + dumper.mediasize = 0; + dumper.mediaoffset = 0; + + /* in dump_machdep.c */ + dumpsys(&dumper); + + if (dump_failed) { + printf("Failed to dump the actual raw datas\n"); + goto trig_abort; + } + + if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) != 0) { + printf("Failed to close the transaction\n"); + goto trig_abort; + } + printf("\nnetdump finished.\n"); + printf("cancelling normal dump\n"); + set_dumper(NULL); +trig_abort: + if (old_if_input) + nd_nic->if_input = old_if_input; + if ((nd_nic->if_capenable & IFCAP_POLLING) == 0) { +#if defined(KDB) && !defined(KDB_UNATTENDED) + if (panicstr == NULL) +#endif + nd_nic->if_ndumpfuncs->ne_enable_intr(nd_nic); + } + dumping--; +} + +/*- + * KLD specific code. + */ + +/* + * [netdump_config_defaults] + * + * Called upon module load. Initializes the sysctl variables to sane defaults + * (locates the first available NIC and uses the first IPv4 IP on that card as + * the client IP). Leaves the server IP unconfigured. + * + * Parameters: + * void + * + * Returns: + * void + */ +static void +netdump_config_defaults() +{ + struct ifnet *ifn; + int found; + + nd_nic = NULL; + nd_server.s_addr = INADDR_ANY; + nd_client.s_addr = INADDR_ANY; + nd_gw.s_addr = INADDR_ANY; + + if (nd_server_tun[0] != '\0') + inet_aton(nd_server_tun, &nd_server); + if (nd_client_tun[0] != '\0') + inet_aton(nd_client_tun, &nd_client); + if (nd_gw_tun[0] != '\0') + inet_aton(nd_gw_tun, &nd_gw); + if (nd_nic_tun[0] != '\0') { + found = 0; + IFNET_RLOCK_NOSLEEP(); + TAILQ_FOREACH(ifn, &V_ifnet, if_link) { + if (!strcmp(ifn->if_xname, nd_nic_tun)) { + found = 1; + break; + } + } + IFNET_RUNLOCK_NOSLEEP(); + if (found != 0 && netdump_supported_nic(ifn)) + nd_nic = ifn; + } +} + +static int +netdump_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + netdump_config_defaults(); + + /* PRI_FIRST happens before the networks are disabled */ + nd_tag = EVENTHANDLER_REGISTER(shutdown_pre_sync, + netdump_trigger, NULL, + SHUTDOWN_PRI_FIRST); + +#ifdef NETDUMP_CLIENT_DEBUG + if (!nd_nic) + printf("netdump: Warning: No default interface " + "found. Manual configuration required.\n"); + else { + char buf[INET_ADDRSTRLEN]; + inet_ntoa_r(nd_client, buf); + printf("netdump: Using interface %s; client IP " + "%s\n", nd_nic->if_xname, buf); + } +#endif + + printf("netdump initialized\n"); + break; + case MOD_UNLOAD: + if (nd_tag) { + EVENTHANDLER_DEREGISTER(shutdown_pre_sync, nd_tag); + nd_tag = NULL; + } + printf("netdump unloaded\n"); + break; + default: + break; + } + return 0; +} +static moduledata_t netdump_mod = {"netdump", netdump_modevent, 0}; +DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PROTO_END, SI_ORDER_ANY); + +#ifdef DDB +DB_COMMAND(netdump, ddb_force_netdump) +{ + + netdump_trigger(NULL, RB_DUMP); +} +#endif + Index: sys/dev/e1000/if_igb.c =================================================================== --- sys/dev/e1000/if_igb.c (.../head/sys) (revision 213582) +++ sys/dev/e1000/if_igb.c (.../projects/sv/sys) (revision 213584) @@ -37,6 +37,7 @@ #include "opt_device_polling.h" #include "opt_inet.h" #include "opt_altq.h" +#include "opt_netdump.h" #endif #include @@ -78,6 +79,9 @@ #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include #include @@ -91,6 +95,27 @@ #include "e1000_82575.h" #include "if_igb.h" +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define IGB_CORE_LOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + IGB_CORE_LOCK(adapter); \ +} while (0) +#define IGB_CORE_UNLOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + IGB_CORE_UNLOCK(adapter); \ +} while (0) +#define IGB_TX_LOCK_COND(txr, locking) do { \ + if ((locking) != 0) \ + IGB_TX_LOCK(txr); \ +} while (0) +#define IGB_TX_UNLOCK_COND(txr, locking) do { \ + if ((locking) != 0) \ + IGB_TX_UNLOCK(txr); \ +} while (0) + +#endif + /********************************************************************* * Set this to one to display debug statistics *********************************************************************/ @@ -252,14 +277,32 @@ static void igb_handle_link(void *context, int pen static void igb_msix_que(void *); static void igb_msix_link(void *); -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _igb_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, + int count, int locking); static poll_handler_t igb_poll; -#endif /* POLLING */ +#endif +#ifdef NETDUMP_CLIENT +static poll_handler_t igb_poll_unlocked; +static ndumplock_handler_t igb_ndump_disable_intr; +static ndumplock_handler_t igb_ndump_enable_intr; +#endif /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ +#ifdef NETDUMP_CLIENT + +static struct netdump_methods igb_ndump_methods = { + .ne_poll_locked = igb_poll, + .ne_poll_unlocked = igb_poll_unlocked, + .ne_disable_intr = igb_ndump_disable_intr, + .ne_enable_intr = igb_ndump_enable_intr +}; + +#endif + static device_method_t igb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, igb_probe), @@ -1354,7 +1397,7 @@ igb_irq_fast(void *arg) return FILTER_HANDLED; } -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) /********************************************************************* * * Legacy polling routine : if using this code you MUST be sure that @@ -1368,7 +1411,7 @@ static int #define POLL_RETURN_COUNT(a) static void #endif -igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +_igb_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int locking) { struct adapter *adapter = ifp->if_softc; struct igb_queue *que = adapter->queues; @@ -1377,9 +1420,9 @@ static void u32 loop = IGB_MAX_LOOP; bool more; - IGB_CORE_LOCK(adapter); + IGB_CORE_LOCK_COND(adapter, locking); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - IGB_CORE_UNLOCK(adapter); + IGB_CORE_UNLOCK_COND(adapter, locking); return POLL_RETURN_COUNT(rx_done); } @@ -1392,11 +1435,11 @@ static void if (reg_icr & E1000_ICR_RXO) adapter->rx_overruns++; } - IGB_CORE_UNLOCK(adapter); + IGB_CORE_UNLOCK_COND(adapter, locking); igb_rxeof(que, count, &rx_done); - IGB_TX_LOCK(txr); + IGB_TX_LOCK_COND(txr, locking); do { more = igb_txeof(txr); } while (loop-- && more); @@ -1407,11 +1450,49 @@ static void if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) igb_start_locked(txr, ifp); #endif - IGB_TX_UNLOCK(txr); + IGB_TX_UNLOCK_COND(txr, locking); return POLL_RETURN_COUNT(rx_done); } -#endif /* DEVICE_POLLING */ +static int +igb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_igb_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +igb_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_igb_poll_generic(ifp, cmd, count, 0)); +} + +static void +igb_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + IGB_CORE_LOCK(adapter); + igb_disable_intr(adapter); + IGB_CORE_UNLOCK(adapter); +} + +static void +igb_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + IGB_CORE_LOCK(adapter); + igb_enable_intr(adapter); + IGB_CORE_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ + /********************************************************************* * * MSIX TX Interrupt Service routine @@ -2713,6 +2794,9 @@ igb_setup_interface(device_t dev, struct adapter * ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = igb_ioctl; ifp->if_start = igb_start; +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs = &igb_ndump_methods; +#endif #if __FreeBSD_version >= 800000 ifp->if_transmit = igb_mq_start; ifp->if_qflush = igb_qflush; Index: sys/dev/e1000/if_lem.c =================================================================== --- sys/dev/e1000/if_lem.c (.../head/sys) (revision 213582) +++ sys/dev/e1000/if_lem.c (.../projects/sv/sys) (revision 213584) @@ -35,6 +35,7 @@ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #include "opt_inet.h" +#include "opt_netdump.h" #endif #include @@ -72,6 +73,9 @@ #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include @@ -83,6 +87,27 @@ #include "e1000_api.h" #include "if_lem.h" +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define EM_CORE_LOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + EM_CORE_LOCK(adapter); \ +} while (0) +#define EM_CORE_UNLOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + EM_CORE_UNLOCK(adapter); \ +} while (0) +#define EM_TX_LOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + EM_TX_LOCK(adapter); \ +} while (0) +#define EM_TX_UNLOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + EM_TX_UNLOCK(adapter); \ +} while (0) + +#endif + /********************************************************************* * Legacy Em Driver version: *********************************************************************/ @@ -255,14 +280,32 @@ static void lem_add_rx_process_limit(struct adapte const char *, int *, int); #endif /* ~EM_LEGACY_IRQ */ -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _lem_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, + int count, int locking); static poll_handler_t lem_poll; -#endif /* POLLING */ +#endif +#ifdef NETDUMP_CLIENT +static poll_handler_t lem_poll_unlocked; +static ndumplock_handler_t lem_ndump_disable_intr; +static ndumplock_handler_t lem_ndump_enable_intr; +#endif /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ +#ifdef NETDUMP_CLIENT + +static struct netdump_methods lem_ndump_methods = { + .ne_poll_locked = lem_poll, + .ne_poll_unlocked = lem_poll_unlocked, + .ne_disable_intr = lem_ndump_disable_intr, + .ne_enable_intr = lem_ndump_enable_intr +}; + +#endif + static device_method_t lem_methods[] = { /* Device interface */ DEVMETHOD(device_probe, lem_probe), @@ -1229,21 +1272,21 @@ lem_init(void *arg) } -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) /********************************************************************* * * Legacy polling routine * *********************************************************************/ static int -lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +_lem_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int locking) { struct adapter *adapter = ifp->if_softc; u32 reg_icr, rx_done = 0; - EM_CORE_LOCK(adapter); + EM_CORE_LOCK_COND(adapter, locking); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - EM_CORE_UNLOCK(adapter); + EM_CORE_UNLOCK_COND(adapter, locking); return (rx_done); } @@ -1257,19 +1300,57 @@ static int lem_local_timer, adapter); } } - EM_CORE_UNLOCK(adapter); + EM_CORE_UNLOCK_COND(adapter, locking); lem_rxeof(adapter, count, &rx_done); - EM_TX_LOCK(adapter); + EM_TX_LOCK_COND(adapter, locking); lem_txeof(adapter); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) lem_start_locked(ifp); - EM_TX_UNLOCK(adapter); + EM_TX_UNLOCK_COND(adapter, locking); return (rx_done); } -#endif /* DEVICE_POLLING */ +static int +lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_lem_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +lem_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_lem_poll_generic(ifp, cmd, count, 0)); +} + +static void +lem_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + EM_CORE_LOCK(adapter); + lem_disable_intr(adapter); + EM_CORE_UNLOCK(adapter); +} + +static void +lem_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + EM_CORE_LOCK(adapter); + lem_enable_intr(adapter); + EM_CORE_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ + #ifdef EM_LEGACY_IRQ /********************************************************************* * @@ -2401,6 +2482,9 @@ lem_setup_interface(device_t dev, struct adapter * ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = lem_ioctl; ifp->if_start = lem_start; +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs = &lem_ndump_methods; +#endif IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1); ifp->if_snd.ifq_drv_maxlen = adapter->num_tx_desc - 1; IFQ_SET_READY(&ifp->if_snd); Index: sys/dev/e1000/if_em.c =================================================================== --- sys/dev/e1000/if_em.c (.../head/sys) (revision 213582) +++ sys/dev/e1000/if_em.c (.../projects/sv/sys) (revision 213584) @@ -35,6 +35,7 @@ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #include "opt_inet.h" +#include "opt_netdump.h" #endif #include @@ -73,6 +74,9 @@ #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include @@ -85,6 +89,27 @@ #include "e1000_82571.h" #include "if_em.h" +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define EM_CORE_LOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + EM_CORE_LOCK(adapter); \ +} while (0) +#define EM_CORE_UNLOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + EM_CORE_UNLOCK(adapter); \ +} while (0) +#define EM_TX_LOCK_COND(txr, locking) do { \ + if ((locking) != 0) \ + EM_TX_LOCK(txr); \ +} while (0) +#define EM_TX_UNLOCK_COND(txr, locking) do { \ + if ((locking) != 0) \ + EM_TX_UNLOCK(txr); \ +} while (0) + +#endif + /********************************************************************* * Set this to one to display debug statistics *********************************************************************/ @@ -285,14 +310,32 @@ static void em_add_rx_process_limit(struct adapter static __inline void em_rx_discard(struct rx_ring *, int); -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _em_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, + int count, int locking); static poll_handler_t em_poll; -#endif /* POLLING */ +#endif +#ifdef NETDUMP_CLIENT +static poll_handler_t em_poll_unlocked; +static ndumplock_handler_t em_ndump_disable_intr; +static ndumplock_handler_t em_ndump_enable_intr; +#endif /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ +#ifdef NETDUMP_CLIENT + +static struct netdump_methods em_ndump_methods = { + .ne_poll_locked = em_poll, + .ne_poll_unlocked = em_poll_unlocked, + .ne_disable_intr = em_ndump_disable_intr, + .ne_enable_intr = em_ndump_enable_intr +}; + +#endif + static device_method_t em_methods[] = { /* Device interface */ DEVMETHOD(device_probe, em_probe), @@ -1346,14 +1389,14 @@ em_init(void *arg) } -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) /********************************************************************* * * Legacy polling routine: note this only works with single queue * *********************************************************************/ static int -em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +_em_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, int locking) { struct adapter *adapter = ifp->if_softc; struct tx_ring *txr = adapter->tx_rings; @@ -1361,9 +1404,9 @@ static int u32 reg_icr; int rx_done; - EM_CORE_LOCK(adapter); + EM_CORE_LOCK_COND(adapter, locking); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { - EM_CORE_UNLOCK(adapter); + EM_CORE_UNLOCK_COND(adapter, locking); return (0); } @@ -1377,11 +1420,11 @@ static int em_local_timer, adapter); } } - EM_CORE_UNLOCK(adapter); + EM_CORE_UNLOCK_COND(adapter, locking); em_rxeof(rxr, count, &rx_done); - EM_TX_LOCK(txr); + EM_TX_LOCK_COND(txr, locking); em_txeof(txr); #ifdef EM_MULTIQUEUE if (!drbr_empty(ifp, txr->br)) @@ -1390,13 +1433,50 @@ static int if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) em_start_locked(ifp, txr); #endif - EM_TX_UNLOCK(txr); + EM_TX_UNLOCK_COND(txr, locking); return (rx_done); } -#endif /* DEVICE_POLLING */ +static int +em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + return (_em_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +em_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_em_poll_generic(ifp, cmd, count, 0)); +} + +static void +em_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + EM_CORE_LOCK(adapter); + em_disable_intr(adapter); + EM_CORE_UNLOCK(adapter); +} + +static void +em_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + EM_CORE_LOCK(adapter); + em_enable_intr(adapter); + EM_CORE_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ + /********************************************************************* * * Fast Legacy/MSI Combined Interrupt Service routine @@ -2781,6 +2861,9 @@ em_setup_interface(device_t dev, struct adapter *a ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = em_ioctl; ifp->if_start = em_start; +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs = &em_ndump_methods; +#endif IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 1); ifp->if_snd.ifq_drv_maxlen = adapter->num_tx_desc - 1; IFQ_SET_READY(&ifp->if_snd); Index: sys/dev/ixgbe/ixgbe.c =================================================================== --- sys/dev/ixgbe/ixgbe.c (.../head/sys) (revision 213582) +++ sys/dev/ixgbe/ixgbe.c (.../projects/sv/sys) (revision 213584) @@ -34,10 +34,24 @@ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" +#include "opt_netdump.h" #endif #include "ixgbe.h" +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define IXGBE_TX_LOCK_COND(txr, locking) do { \ + if ((locking) != 0) \ + IXGBE_TX_LOCK(txr); \ +} while (0) +#define IXGBE_TX_UNLOCK_COND(txr, locking) do { \ + if ((locking) != 0) \ + IXGBE_TX_UNLOCK(txr); \ +} while (0) + +#endif + /********************************************************************* * Set this to one to display debug statistics *********************************************************************/ @@ -141,7 +155,7 @@ static void ixgbe_enable_intr(struct adapter * static void ixgbe_disable_intr(struct adapter *); static void ixgbe_update_stats_counters(struct adapter *); static bool ixgbe_txeof(struct tx_ring *); -static bool ixgbe_rxeof(struct ix_queue *, int); +static bool ixgbe_rxeof(struct ix_queue *, int, int *); static void ixgbe_rx_checksum(u32, struct mbuf *, u32); static void ixgbe_set_promisc(struct adapter *); static void ixgbe_disable_promisc(struct adapter *); @@ -195,10 +209,32 @@ static void ixgbe_atr(struct tx_ring *, struct mbu static void ixgbe_reinit_fdir(void *, int); #endif +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _ixgbe_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, + int count, int locking); +static poll_handler_t ixgbe_poll; +#endif +#ifdef NETDUMP_CLIENT +static poll_handler_t ixgbe_poll_unlocked; +static ndumplock_handler_t ixgbe_ndump_disable_intr; +static ndumplock_handler_t ixgbe_ndump_enable_intr; +#endif + /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ +#ifdef NETDUMP_CLIENT + +static struct netdump_methods ixgbe_ndump_methods = { + .ne_poll_locked = ixgbe_poll, + .ne_poll_unlocked = ixgbe_poll_unlocked, + .ne_disable_intr = ixgbe_ndump_disable_intr, + .ne_enable_intr = ixgbe_ndump_enable_intr +}; + +#endif + static device_method_t ixgbe_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ixgbe_probe), @@ -675,6 +711,11 @@ ixgbe_detach(device_t dev) return (EBUSY); } +#ifdef DEVICE_POLLING + if ((adapter->ifp->if_capenable & IFCAP_POLLING) != 0) + ether_poll_deregister(adapter->ifp); +#endif + IXGBE_CORE_LOCK(adapter); ixgbe_stop(adapter); IXGBE_CORE_UNLOCK(adapter); @@ -979,6 +1020,25 @@ ixgbe_ioctl(struct ifnet * ifp, u_long command, ca { int mask = ifr->ifr_reqcap ^ ifp->if_capenable; IOCTL_DEBUGOUT("ioctl: SIOCSIFCAP (Set Capabilities)"); +#ifdef DEVICE_POLLING + if ((mask & IFCAP_POLLING) != 0) { + if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) { + error = ether_poll_register(ixgbe_poll, ifp); + if (error != 0) + return (error); + IXGBE_CORE_LOCK(adapter); + ixgbe_disable_intr(adapter); + ifp->if_capenable |= IFCAP_POLLING; + IXGBE_CORE_UNLOCK(adapter); + } else { + error = ether_poll_deregister(ifp); + IXGBE_CORE_LOCK(adapter); + ixgbe_enable_intr(adapter); + ifp->if_capenable &= ~IFCAP_POLLING; + IXGBE_CORE_UNLOCK(adapter); + } + } +#endif /* !DEVICE_POLLING */ if (mask & IFCAP_HWCSUM) ifp->if_capenable ^= IFCAP_HWCSUM; if (mask & IFCAP_TSO4) @@ -1199,8 +1259,13 @@ ixgbe_init_locked(struct adapter *adapter) /* Config/Enable Link */ ixgbe_config_link(adapter); - /* And now turn on interrupts */ - ixgbe_enable_intr(adapter); +#ifdef DEVICE_POLLING + /* Disable interrupts if polling is on, enable otherwise. */ + if ((ifp->if_capenable & IFCAP_POLLING) != 0) + ixgbe_disable_intr(adapter); + else +#endif + ixgbe_enable_intr(adapter); /* Now inform the stack we're ready */ ifp->if_drv_flags |= IFF_DRV_RUNNING; @@ -1294,7 +1359,7 @@ ixgbe_handle_que(void *context, int pending) bool more; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { - more = ixgbe_rxeof(que, adapter->rx_process_limit); + more = ixgbe_rxeof(que, adapter->rx_process_limit, NULL); IXGBE_TX_LOCK(txr); ixgbe_txeof(txr); #if __FreeBSD_version >= 800000 @@ -1333,6 +1398,10 @@ ixgbe_legacy_irq(void *arg) bool more_tx, more_rx; u32 reg_eicr, loop = MAX_LOOP; +#ifdef DEVICE_POLLING + if ((adapter->ifp->if_capenable & IFCAP_POLLING) != 0) + return; +#endif reg_eicr = IXGBE_READ_REG(hw, IXGBE_EICR); @@ -1342,7 +1411,7 @@ ixgbe_legacy_irq(void *arg) return; } - more_rx = ixgbe_rxeof(que, adapter->rx_process_limit); + more_rx = ixgbe_rxeof(que, adapter->rx_process_limit, NULL); IXGBE_TX_LOCK(txr); do { @@ -1387,13 +1456,13 @@ ixgbe_msix_que(void *arg) ++que->irqs; - more_rx = ixgbe_rxeof(que, adapter->rx_process_limit); + more_rx = ixgbe_rxeof(que, adapter->rx_process_limit, NULL); IXGBE_TX_LOCK(txr); more_tx = ixgbe_txeof(txr); IXGBE_TX_UNLOCK(txr); - more_rx = ixgbe_rxeof(que, adapter->rx_process_limit); + more_rx = ixgbe_rxeof(que, adapter->rx_process_limit, NULL); /* Do AIM now? */ @@ -2401,6 +2470,9 @@ ixgbe_setup_interface(device_t dev, struct adapter ifp->if_qflush = ixgbe_qflush; #endif ifp->if_snd.ifq_maxlen = adapter->num_tx_desc - 2; +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs = &ixgbe_ndump_methods; +#endif ether_ifattach(ifp, adapter->hw.mac.addr); @@ -2417,6 +2489,9 @@ ixgbe_setup_interface(device_t dev, struct adapter ifp->if_capabilities |= IFCAP_JUMBO_MTU | IFCAP_LRO; ifp->if_capenable = ifp->if_capabilities; +#ifdef DEVICE_POLLING + ifp->if_capabilities |= IFCAP_POLLING; +#endif /* * Specify the media types supported by this adapter and register @@ -3262,6 +3337,91 @@ ixgbe_atr(struct tx_ring *txr, struct mbuf *mp) } #endif +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int +_ixgbe_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, int count, + int locking) +{ + struct adapter *adapter; + struct tx_ring *txr; + struct ix_queue *que; + struct ixgbe_hw *hw; + u32 loop, reg_eicr; + int rx_npkts; + bool more_tx; + + adapter = ifp->if_softc; + txr = adapter->tx_rings; + que = adapter->queues; + hw = &adapter->hw; + loop = MAX_LOOP; + rx_npkts = 0; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return (rx_npkts); + + if (cmd == POLL_AND_CHECK_STATUS) { + reg_eicr = IXGBE_READ_REG(hw, IXGBE_EICR); + + /* Link status change */ + if ((reg_eicr & IXGBE_EICR_LSC) != 0) + taskqueue_enqueue(adapter->tq, &adapter->link_task); + } + ixgbe_rxeof(que, count, &rx_npkts); + IXGBE_TX_LOCK_COND(txr, locking); + do { + more_tx = ixgbe_txeof(txr); + } while (loop-- && more_tx); +#if __FreeBSD_version >= 800000 + if (!drbr_empty(ifp, txr->br)) + ixgbe_mq_start_locked(ifp, txr, NULL); +#else + if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) + ixgbe_start_locked(txr, ifp); +#endif + IXGBE_TX_UNLOCK_COND(txr, locking); + return (rx_npkts); +} + +static int +ixgbe_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_ixgbe_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +ixgbe_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_ixgbe_poll_generic(ifp, cmd, count, 0)); +} + +static void +ixgbe_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + IXGBE_CORE_LOCK(adapter); + ixgbe_disable_intr(adapter); + IXGBE_CORE_UNLOCK(adapter); +} + +static void +ixgbe_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + IXGBE_CORE_LOCK(adapter); + ixgbe_enable_intr(adapter); + IXGBE_CORE_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ + /********************************************************************** * * Examine each tx_buffer in the used queue. If the hardware is done @@ -4062,14 +4222,14 @@ ixgbe_rx_discard(struct rx_ring *rxr, int i) * Return TRUE for more work, FALSE for all clean. *********************************************************************/ static bool -ixgbe_rxeof(struct ix_queue *que, int count) +ixgbe_rxeof(struct ix_queue *que, int count, int *rx_npktsp) { struct adapter *adapter = que->adapter; struct rx_ring *rxr = que->rxr; struct ifnet *ifp = adapter->ifp; struct lro_ctrl *lro = &rxr->lro; struct lro_entry *queued; - int i, nextp, processed = 0; + int i, nextp, processed = 0, rx_npkts = 0; u32 staterr = 0; union ixgbe_adv_rx_desc *cur; struct ixgbe_rx_buf *rbuf, *nbuf; @@ -4273,8 +4433,10 @@ next_desc: i = 0; /* Now send to the stack or do LRO */ - if (sendmp != NULL) + if (sendmp != NULL) { ixgbe_rx_input(rxr, ifp, sendmp, ptype); + rx_npkts++; + } /* Every 8 descriptors we go to refresh mbufs */ if (processed == 8) { @@ -4307,9 +4469,13 @@ next_desc: */ if ((staterr & IXGBE_RXD_STAT_DD) != 0) { ixgbe_rearm_queues(adapter, (u64)(1 << que->msix)); + if (rx_npktsp != NULL) + *rx_npktsp = rx_npkts; return (TRUE); } + if (rx_npktsp != NULL) + *rx_npktsp = rx_npkts; return (FALSE); } Index: sys/dev/ixgbe/ixgbe.h =================================================================== --- sys/dev/ixgbe/ixgbe.h (.../head/sys) (revision 213582) +++ sys/dev/ixgbe/ixgbe.h (.../projects/sv/sys) (revision 213584) @@ -66,6 +66,9 @@ #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include #include Index: sys/dev/ixgb/if_ixgb.c =================================================================== --- sys/dev/ixgb/if_ixgb.c (.../head/sys) (revision 213582) +++ sys/dev/ixgb/if_ixgb.c (.../projects/sv/sys) (revision 213584) @@ -35,10 +35,24 @@ POSSIBILITY OF SUCH DAMAGE. #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" +#include "opt_netdump.h" #endif #include +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) + +#define IXGB_LOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + IXGB_LOCK(adapter); \ +} while (0) +#define IXGB_UNLOCK_COND(adapter, locking) do { \ + if ((locking) != 0) \ + IXGB_UNLOCK(adapter); \ +} while (0) + +#endif + /********************************************************************* * Set this to one to display debug statistics *********************************************************************/ @@ -145,14 +159,32 @@ static int ixgb_dma_malloc(struct adapter *, bus_size_t, struct ixgb_dma_alloc *, int); static void ixgb_dma_free(struct adapter *, struct ixgb_dma_alloc *); -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) +static int _ixgb_poll_generic(struct ifnet *ifp, enum poll_cmd cmd, + int count, int locking); static poll_handler_t ixgb_poll; #endif +#ifdef NETDUMP_CLIENT +static poll_handler_t ixgb_poll_unlocked; +static ndumplock_handler_t ixgb_ndump_disable_intr; +static ndumplock_handler_t ixgb_ndump_enable_intr; +#endif /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ +#ifdef NETDUMP_CLIENT + +static struct netdump_methods ixgb_ndump_methods = { + .ne_poll_locked = ixgb_poll, + .ne_poll_unlocked = ixgb_poll_unlocked, + .ne_disable_intr = ixgb_ndump_disable_intr, + .ne_enable_intr = ixgb_ndump_enable_intr +}; + +#endif + static device_method_t ixgb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ixgb_probe), @@ -750,7 +782,7 @@ ixgb_init(void *arg) return; } -#ifdef DEVICE_POLLING +#if defined(DEVICE_POLLING) || defined(NETDUMP_CLIENT) static int ixgb_poll_locked(struct ifnet * ifp, enum poll_cmd cmd, int count) { @@ -776,19 +808,58 @@ ixgb_poll_locked(struct ifnet * ifp, enum poll_cmd } static int -ixgb_poll(struct ifnet * ifp, enum poll_cmd cmd, int count) +_ixgb_poll_generic(struct ifnet * ifp, enum poll_cmd cmd, int count, + int locking) { struct adapter *adapter = ifp->if_softc; int rx_npkts = 0; - IXGB_LOCK(adapter); + IXGB_LOCK_COND(adapter, locking); if (ifp->if_drv_flags & IFF_DRV_RUNNING) rx_npkts = ixgb_poll_locked(ifp, cmd, count); - IXGB_UNLOCK(adapter); + IXGB_UNLOCK_COND(adapter, locking); return (rx_npkts); } -#endif /* DEVICE_POLLING */ +static int +ixgb_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_ixgb_poll_generic(ifp, cmd, count, 1)); +} +#endif /* !DEVICE_POLLING && !NETDUMP_CLIENT */ + +#ifdef NETDUMP_CLIENT +static int +ixgb_poll_unlocked(struct ifnet *ifp, enum poll_cmd cmd, int count) +{ + + return (_ixgb_poll_generic(ifp, cmd, count, 0)); +} + +static void +ixgb_ndump_disable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + IXGB_LOCK(adapter); + ixgb_disable_intr(adapter); + IXGB_UNLOCK(adapter); +} + +static void +ixgb_ndump_enable_intr(struct ifnet *ifp) +{ + struct adapter *adapter; + + adapter = ifp->if_softc; + IXGB_LOCK(adapter); + ixgb_enable_intr(adapter); + IXGB_UNLOCK(adapter); +} +#endif /* !NETDUMP_CLIENT */ + /********************************************************************* * * Interrupt Service routine @@ -1362,6 +1433,9 @@ ixgb_setup_interface(device_t dev, struct adapter ifp->if_ioctl = ixgb_ioctl; ifp->if_start = ixgb_start; ifp->if_snd.ifq_maxlen = adapter->num_tx_desc - 1; +#ifdef NETDUMP_CLIENT + ifp->if_ndumpfuncs = &ixgb_ndump_methods; +#endif #if __FreeBSD_version < 500000 ether_ifattach(ifp, ETHER_BPF_SUPPORTED); Index: sys/dev/ixgb/if_ixgb.h =================================================================== --- sys/dev/ixgb/if_ixgb.h (.../head/sys) (revision 213582) +++ sys/dev/ixgb/if_ixgb.h (.../projects/sv/sys) (revision 213584) @@ -60,6 +60,9 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#ifdef NETDUMP_CLIENT +#include +#endif #include #include Index: sys/sun4v/sun4v/dump_machdep.c =================================================================== --- sys/sun4v/sun4v/dump_machdep.c (.../head/sys) (revision 213582) +++ sys/sun4v/sun4v/dump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -160,18 +160,23 @@ dumpsys(struct dumperinfo *di) DEV_BSIZE); size += hdrsize; - totsize = size + 2 * sizeof(kdh); - if (totsize > di->mediasize) { - printf("Insufficient space on device (need %ld, have %ld), " - "refusing to dump.\n", (long)totsize, - (long)di->mediasize); - error = ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + totsize = size + 2 * sizeof(kdh); + if (totsize > di->mediasize) { + printf("Insufficient space on device (need %ld, " + "have %ld), refusing to dump.\n", (long)totsize, + (long)di->mediasize); + error = ENOSPC; + goto fail; + } + + /* Determine dump offset on device. */ + dumplo = di->mediaoffset + di->mediasize - totsize; } - /* Determine dump offset on device. */ - dumplo = di->mediaoffset + di->mediasize - totsize; - mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_SPARC64_VERSION, size, di->blocksize); printf("Dumping %lu MB (%d chunks)\n", (u_long)(size >> 20), nreg); Index: sys/net/if_var.h =================================================================== --- sys/net/if_var.h (.../head/sys) (revision 213582) +++ sys/net/if_var.h (.../projects/sv/sys) (revision 213584) @@ -72,6 +72,7 @@ struct carp_if; struct ifvlantrunk; struct route; struct vnet; +struct netdump_methods; #endif #include /* get TAILQ macros */ @@ -204,7 +205,8 @@ struct ifnet { */ char if_cspare[3]; char *if_description; /* interface description */ - void *if_pspare[7]; + struct netdump_methods *if_ndumpfuncs; /* netdump virtual methods */ + void *if_pspare[6]; int if_ispare[4]; }; @@ -889,10 +891,16 @@ void if_deregister_com_alloc(u_char type); #define IF_LLADDR(ifp) \ LLADDR((struct sockaddr_dl *)((ifp)->if_addr->ifa_addr)) -#ifdef DEVICE_POLLING +/* + * Keep enum poll_cmd and poll_handler_t specification unconditional from + * DEVICE_POLLING because other modules may be needing them as well + * (where the most notable one is netdump). + */ enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS }; typedef int poll_handler_t(struct ifnet *ifp, enum poll_cmd cmd, int count); + +#ifdef DEVICE_POLLING int ether_poll_register(poll_handler_t *h, struct ifnet *ifp); int ether_poll_deregister(struct ifnet *ifp); #endif /* DEVICE_POLLING */ Index: sys/i386/i386/minidump_machdep.c =================================================================== --- sys/i386/i386/minidump_machdep.c (.../head/sys) (revision 213582) +++ sys/i386/i386/minidump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -242,13 +242,19 @@ minidumpsys(struct dumperinfo *di) } dumpsize += PAGE_SIZE; - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; progress = dumpsize; /* Initialize mdhdr */ Index: sys/i386/i386/dump_machdep.c =================================================================== --- sys/i386/i386/dump_machdep.c (.../head/sys) (revision 213582) +++ sys/i386/i386/dump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -296,13 +296,20 @@ dumpsys(struct dumperinfo *di) dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_I386_VERSION, dumpsize, di->blocksize); Index: sys/amd64/amd64/minidump_machdep.c =================================================================== --- sys/amd64/amd64/minidump_machdep.c (.../head/sys) (revision 213582) +++ sys/amd64/amd64/minidump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -242,13 +242,20 @@ minidumpsys(struct dumperinfo *di) } dumpsize += PAGE_SIZE; - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; progress = dumpsize; /* Initialize mdhdr */ Index: sys/amd64/amd64/dump_machdep.c =================================================================== --- sys/amd64/amd64/dump_machdep.c (.../head/sys) (revision 213582) +++ sys/amd64/amd64/dump_machdep.c (.../projects/sv/sys) (revision 213584) @@ -296,13 +296,20 @@ dumpsys(struct dumperinfo *di) dumpsize += fileofs; hdrgap = fileofs - DEV_ALIGN(hdrsz); - /* Determine dump offset on device. */ - if (di->mediasize < SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { - error = ENOSPC; - goto fail; + /* If the upper bound is 0, dumper likely will not use disks. */ + if ((di->mediaoffset + di->mediasize) == 0) + dumplo = 0; + else { + + /* Determine dump offset on device. */ + if (di->mediasize < + SIZEOF_METADATA + dumpsize + sizeof(kdh) * 2) { + error = ENOSPC; + goto fail; + } + dumplo = di->mediaoffset + di->mediasize - dumpsize; + dumplo -= sizeof(kdh) * 2; } - dumplo = di->mediaoffset + di->mediasize - dumpsize; - dumplo -= sizeof(kdh) * 2; mkdumpheader(&kdh, KERNELDUMPMAGIC, KERNELDUMP_AMD64_VERSION, dumpsize, di->blocksize); Index: sys/sys/conf.h =================================================================== --- sys/sys/conf.h (.../head/sys) (revision 213582) +++ sys/sys/conf.h (.../projects/sv/sys) (revision 213584) @@ -330,8 +330,11 @@ struct dumperinfo { int set_dumper(struct dumperinfo *); int dump_write(struct dumperinfo *, void *, vm_offset_t, off_t, size_t); void dumpsys(struct dumperinfo *); -extern int dumping; /* system is dumping */ +extern int dumping; +extern struct pcb dumppcb; +extern lwpid_t dumptid; + #endif /* _KERNEL */ #endif /* !_SYS_CONF_H_ */