| /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved. | 
 |  * Portions Copyright © 1997-1999 Vita Nuova Limited | 
 |  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited | 
 |  *                                (www.vitanuova.com) | 
 |  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | 
 |  * | 
 |  * Modified for the Akaros operating system: | 
 |  * Copyright (c) 2013-2014 The Regents of the University of California | 
 |  * Copyright (c) 2013-2015 Google Inc. | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 |  * copies of the Software, and to permit persons to whom the Software is | 
 |  * furnished to do so, subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in | 
 |  * all copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
 |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
 |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
 |  * SOFTWARE. */ | 
 |  | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 | #include <net/ip.h> | 
 |  | 
 | enum { | 
 | 	Nlog = 4 * 1024, | 
 | }; | 
 |  | 
 | /* | 
 |  *  action log | 
 |  */ | 
 | struct Netlog { | 
 | 	spinlock_t lock; | 
 | 	int opens; | 
 | 	char *buf; | 
 | 	char *end; | 
 | 	char *rptr; | 
 | 	int len; | 
 |  | 
 | 	int logmask;			/* mask of things to debug */ | 
 | 	uint8_t iponly[IPaddrlen];	/* ip address to print debugging for */ | 
 | 	int iponlyset; | 
 |  | 
 | 	qlock_t qlock; | 
 | 	struct rendez r; | 
 | }; | 
 |  | 
 | typedef struct Netlogflag { | 
 | 	char *name; | 
 | 	int mask; | 
 | } Netlogflag; | 
 |  | 
 | static Netlogflag flags[] = { | 
 | 	{"ppp", Logppp,}, | 
 | 	{"ip", Logip,}, | 
 | 	{"fs", Logfs,}, | 
 | 	{"tcp", Logtcp,}, | 
 | 	{"il", Logil,}, | 
 | 	{"icmp", Logicmp,}, | 
 | 	{"udp", Logudp,}, | 
 | 	{"compress", Logcompress,}, | 
 | 	{"ilmsg", Logil | Logilmsg,}, | 
 | 	{"gre", Loggre,}, | 
 | 	{"tcpreset", Logtcp | Logtcpreset,}, | 
 | 	{"tcprxmt", Logtcp | Logtcprxmt,}, | 
 | 	{"tcpall", Logtcp | Logtcpreset | Logtcprxmt | Logtcpverbose,}, | 
 | 	{"udpmsg", Logudp | Logudpmsg,}, | 
 | 	{"ipmsg", Logip | Logipmsg,}, | 
 | 	{"esp", Logesp,}, | 
 | 	{NULL, 0,}, | 
 | }; | 
 |  | 
 | enum { | 
 | 	CMset, | 
 | 	CMclear, | 
 | 	CMonly, | 
 | }; | 
 |  | 
 | static struct cmdtab routecmd[] = { | 
 | 	{CMset, "set", 0}, | 
 | 	{CMclear, "clear", 0}, | 
 | 	{CMonly, "only", 0}, | 
 | }; | 
 |  | 
 | void netloginit(struct Fs *f) | 
 | { | 
 | 	f->alog = kzmalloc(sizeof(struct Netlog), 0); | 
 | 	spinlock_init(&f->alog->lock); | 
 | 	qlock_init(&f->alog->qlock); | 
 | 	rendez_init(&f->alog->r); | 
 | } | 
 |  | 
 | void netlogopen(struct Fs *f) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	spin_lock(&f->alog->lock); | 
 | 	if (waserror()) { | 
 | 		spin_unlock(&f->alog->lock); | 
 | 		nexterror(); | 
 | 	} | 
 | 	if (f->alog->opens == 0) { | 
 | 		if (f->alog->buf == NULL) | 
 | 			f->alog->buf = kzmalloc(Nlog, 0); | 
 | 		f->alog->rptr = f->alog->buf; | 
 | 		f->alog->end = f->alog->buf + Nlog; | 
 | 	} | 
 | 	f->alog->opens++; | 
 | 	spin_unlock(&f->alog->lock); | 
 | 	poperror(); | 
 | } | 
 |  | 
 | void netlogclose(struct Fs *f) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	spin_lock(&f->alog->lock); | 
 | 	if (waserror()) { | 
 | 		spin_unlock(&f->alog->lock); | 
 | 		nexterror(); | 
 | 	} | 
 | 	f->alog->opens--; | 
 | 	if (f->alog->opens == 0) { | 
 | 		kfree(f->alog->buf); | 
 | 		f->alog->buf = NULL; | 
 | 	} | 
 | 	spin_unlock(&f->alog->lock); | 
 | 	poperror(); | 
 | } | 
 |  | 
 | static int netlogready(void *a) | 
 | { | 
 | 	struct Fs *f = a; | 
 |  | 
 | 	return f->alog->len; | 
 | } | 
 |  | 
 | long netlogread(struct Fs *f, void *a, uint32_t unused, long n) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	int i, d; | 
 | 	char *p, *rptr; | 
 |  | 
 | 	qlock(&f->alog->qlock); | 
 | 	if (waserror()) { | 
 | 		qunlock(&f->alog->qlock); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	for (;;) { | 
 | 		spin_lock(&f->alog->lock); | 
 | 		if (f->alog->len) { | 
 | 			if (n > f->alog->len) | 
 | 				n = f->alog->len; | 
 | 			d = 0; | 
 | 			rptr = f->alog->rptr; | 
 | 			f->alog->rptr += n; | 
 | 			if (f->alog->rptr >= f->alog->end) { | 
 | 				d = f->alog->rptr - f->alog->end; | 
 | 				f->alog->rptr = f->alog->buf + d; | 
 | 			} | 
 | 			f->alog->len -= n; | 
 | 			spin_unlock(&f->alog->lock); | 
 |  | 
 | 			i = n - d; | 
 | 			p = a; | 
 | 			memmove(p, rptr, i); | 
 | 			memmove(p + i, f->alog->buf, d); | 
 | 			break; | 
 | 		} else | 
 | 			spin_unlock(&f->alog->lock); | 
 |  | 
 | 		rendez_sleep(&f->alog->r, netlogready, f); | 
 | 	} | 
 |  | 
 | 	qunlock(&f->alog->qlock); | 
 | 	poperror(); | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | void netlogctl(struct Fs *f, char *s, size_t n) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	int i, set = 0; | 
 | 	Netlogflag *fp; | 
 | 	struct cmdbuf *cb; | 
 | 	struct cmdtab *ct; | 
 |  | 
 | 	cb = parsecmd(s, n); | 
 | 	if (waserror()) { | 
 | 		kfree(cb); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	if (cb->nf < 2) | 
 | 		error(EINVAL, ERROR_FIXME); | 
 |  | 
 | 	ct = lookupcmd(cb, routecmd, ARRAY_SIZE(routecmd)); | 
 |  | 
 | 	switch (ct->index) { | 
 | 	case CMset: | 
 | 		set = 1; | 
 | 		break; | 
 |  | 
 | 	case CMclear: | 
 | 		set = 0; | 
 | 		break; | 
 |  | 
 | 	case CMonly: | 
 | 		parseip(f->alog->iponly, cb->f[1]); | 
 | 		if (ipcmp(f->alog->iponly, IPnoaddr) == 0) | 
 | 			f->alog->iponlyset = 0; | 
 | 		else | 
 | 			f->alog->iponlyset = 1; | 
 | 		kfree(cb); | 
 | 		poperror(); | 
 | 		return; | 
 |  | 
 | 	default: | 
 | 		cmderror(cb, "unknown ip control message"); | 
 | 	} | 
 |  | 
 | 	for (i = 1; i < cb->nf; i++) { | 
 | 		for (fp = flags; fp->name; fp++) | 
 | 			if (strcmp(fp->name, cb->f[i]) == 0) | 
 | 				break; | 
 | 		if (fp->name == NULL) | 
 | 			continue; | 
 | 		if (set) | 
 | 			f->alog->logmask |= fp->mask; | 
 | 		else | 
 | 			f->alog->logmask &= ~fp->mask; | 
 | 	} | 
 |  | 
 | 	kfree(cb); | 
 | 	poperror(); | 
 | } | 
 |  | 
 | void netlog(struct Fs *f, int mask, char *fmt, ...) | 
 | { | 
 | 	char buf[256], *t, *fp; | 
 | 	int i, n; | 
 | 	va_list arg; | 
 | 	struct timespec ts_now; | 
 |  | 
 | 	if (!(f->alog->logmask & mask)) | 
 | 		return; | 
 |  | 
 | 	if (f->alog->opens == 0) | 
 | 		return; | 
 |  | 
 | 	/* Same style as trace_printk */ | 
 | 	if (likely(__proc_global_info.tsc_freq)) | 
 | 		ts_now = tsc2timespec(read_tsc()); | 
 | 	n = snprintf(buf, sizeof(buf), "[%lu.%09lu]: ", | 
 | 	             ts_now.tv_sec, ts_now.tv_nsec); | 
 |  | 
 | 	va_start(arg, fmt); | 
 | 	n += vsnprintf(buf + n, sizeof(buf) - n, fmt, arg); | 
 | 	va_end(arg); | 
 |  | 
 | 	spin_lock(&f->alog->lock); | 
 | 	i = f->alog->len + n - Nlog; | 
 | 	if (i > 0) { | 
 | 		f->alog->len -= i; | 
 | 		f->alog->rptr += i; | 
 | 		if (f->alog->rptr >= f->alog->end) | 
 | 			f->alog->rptr = f->alog->buf + (f->alog->rptr - | 
 | 							f->alog->end); | 
 | 	} | 
 | 	t = f->alog->rptr + f->alog->len; | 
 | 	fp = buf; | 
 | 	f->alog->len += n; | 
 | 	while (n-- > 0) { | 
 | 		if (t >= f->alog->end) | 
 | 			t = f->alog->buf + (t - f->alog->end); | 
 | 		*t++ = *fp++; | 
 | 	} | 
 | 	spin_unlock(&f->alog->lock); | 
 |  | 
 | 	rendez_wakeup(&f->alog->r); | 
 | } |