1293a2caeSAugustin Cavalier/*-
28244a9baSAugustin Cavalier * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
38244a9baSAugustin Cavalier *
4293a2caeSAugustin Cavalier * Copyright (c) 2011 Adrian Chadd, Xenion Lty Ltd
5293a2caeSAugustin Cavalier * All rights reserved.
6293a2caeSAugustin Cavalier *
7293a2caeSAugustin Cavalier * Redistribution and use in source and binary forms, with or without
8293a2caeSAugustin Cavalier * modification, are permitted provided that the following conditions
9293a2caeSAugustin Cavalier * are met:
10293a2caeSAugustin Cavalier * 1. Redistributions of source code must retain the above copyright
11293a2caeSAugustin Cavalier *    notice, this list of conditions and the following disclaimer.
12293a2caeSAugustin Cavalier * 2. Redistributions in binary form must reproduce the above copyright
13293a2caeSAugustin Cavalier *    notice, this list of conditions and the following disclaimer in the
14293a2caeSAugustin Cavalier *    documentation and/or other materials provided with the distribution.
15293a2caeSAugustin Cavalier *
16293a2caeSAugustin Cavalier * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17293a2caeSAugustin Cavalier * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18293a2caeSAugustin Cavalier * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19293a2caeSAugustin Cavalier * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20293a2caeSAugustin Cavalier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21293a2caeSAugustin Cavalier * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22293a2caeSAugustin Cavalier * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23293a2caeSAugustin Cavalier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24293a2caeSAugustin Cavalier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25293a2caeSAugustin Cavalier * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26293a2caeSAugustin Cavalier */
27293a2caeSAugustin Cavalier
28293a2caeSAugustin Cavalier#include <sys/cdefs.h>
29293a2caeSAugustin Cavalier#ifdef __FreeBSD__
308244a9baSAugustin Cavalier__FBSDID("$FreeBSD: releng/12.0/sys/net80211/ieee80211_alq.c 326272 2017-11-27 15:23:17Z pfg $");
31293a2caeSAugustin Cavalier#endif
32293a2caeSAugustin Cavalier
33293a2caeSAugustin Cavalier/*
34293a2caeSAugustin Cavalier * net80211 fast-logging support, primarily for debugging.
35293a2caeSAugustin Cavalier *
36293a2caeSAugustin Cavalier * This implements a single debugging queue which includes
37293a2caeSAugustin Cavalier * per-device enumeration where needed.
38293a2caeSAugustin Cavalier */
39293a2caeSAugustin Cavalier
40293a2caeSAugustin Cavalier#include "opt_wlan.h"
41293a2caeSAugustin Cavalier
42293a2caeSAugustin Cavalier#include <sys/param.h>
43293a2caeSAugustin Cavalier#include <sys/systm.h>
44293a2caeSAugustin Cavalier#include <sys/mbuf.h>
45293a2caeSAugustin Cavalier#include <sys/malloc.h>
46293a2caeSAugustin Cavalier#include <sys/endian.h>
47293a2caeSAugustin Cavalier#include <sys/kernel.h>
48293a2caeSAugustin Cavalier#include <sys/sysctl.h>
49293a2caeSAugustin Cavalier#include <sys/pcpu.h>
50293a2caeSAugustin Cavalier#include <sys/proc.h>
51293a2caeSAugustin Cavalier#include <sys/ucred.h>
52293a2caeSAugustin Cavalier#include <sys/alq.h>
53293a2caeSAugustin Cavalier
54293a2caeSAugustin Cavalier#include <sys/socket.h>
55293a2caeSAugustin Cavalier
56293a2caeSAugustin Cavalier#include <net/if.h>
57293a2caeSAugustin Cavalier#include <net/if_var.h>
58293a2caeSAugustin Cavalier#include <net/if_media.h>
59293a2caeSAugustin Cavalier#include <net/ethernet.h>
60293a2caeSAugustin Cavalier
61293a2caeSAugustin Cavalier#include <net80211/ieee80211_var.h>
62293a2caeSAugustin Cavalier#include <net80211/ieee80211_freebsd.h>
63293a2caeSAugustin Cavalier#include <net80211/ieee80211_alq.h>
64293a2caeSAugustin Cavalier
65293a2caeSAugustin Cavalierstatic struct alq *ieee80211_alq;
66293a2caeSAugustin Cavalierstatic int ieee80211_alq_lost;
67293a2caeSAugustin Cavalierstatic int ieee80211_alq_logged;
68293a2caeSAugustin Cavalierstatic char ieee80211_alq_logfile[MAXPATHLEN] = "/tmp/net80211.log";
69293a2caeSAugustin Cavalierstatic unsigned int ieee80211_alq_qsize = 64*1024;
70293a2caeSAugustin Cavalier
71293a2caeSAugustin Cavalierstatic int
72293a2caeSAugustin Cavalierieee80211_alq_setlogging(int enable)
73293a2caeSAugustin Cavalier{
74293a2caeSAugustin Cavalier	int error;
75293a2caeSAugustin Cavalier
76293a2caeSAugustin Cavalier	if (enable) {
77293a2caeSAugustin Cavalier		if (ieee80211_alq)
78293a2caeSAugustin Cavalier			alq_close(ieee80211_alq);
79293a2caeSAugustin Cavalier
80293a2caeSAugustin Cavalier		error = alq_open(&ieee80211_alq,
81293a2caeSAugustin Cavalier		    ieee80211_alq_logfile,
82293a2caeSAugustin Cavalier		    curthread->td_ucred,
83293a2caeSAugustin Cavalier		    ALQ_DEFAULT_CMODE,
848244a9baSAugustin Cavalier		    ieee80211_alq_qsize, 0);
85293a2caeSAugustin Cavalier		ieee80211_alq_lost = 0;
86293a2caeSAugustin Cavalier		ieee80211_alq_logged = 0;
87293a2caeSAugustin Cavalier		printf("net80211: logging to %s enabled; "
88293a2caeSAugustin Cavalier		    "struct size %d bytes\n",
89293a2caeSAugustin Cavalier		    ieee80211_alq_logfile,
908244a9baSAugustin Cavalier		    (int) sizeof(struct ieee80211_alq_rec));
91293a2caeSAugustin Cavalier	} else {
92293a2caeSAugustin Cavalier		if (ieee80211_alq)
93293a2caeSAugustin Cavalier			alq_close(ieee80211_alq);
94293a2caeSAugustin Cavalier		ieee80211_alq = NULL;
95293a2caeSAugustin Cavalier		printf("net80211: logging disabled\n");
96293a2caeSAugustin Cavalier		error = 0;
97293a2caeSAugustin Cavalier	}
98293a2caeSAugustin Cavalier	return (error);
99293a2caeSAugustin Cavalier}
100293a2caeSAugustin Cavalier
101293a2caeSAugustin Cavalierstatic int
102293a2caeSAugustin Cavaliersysctl_ieee80211_alq_log(SYSCTL_HANDLER_ARGS)
103293a2caeSAugustin Cavalier{
104293a2caeSAugustin Cavalier	int error, enable;
105293a2caeSAugustin Cavalier
106293a2caeSAugustin Cavalier	enable = (ieee80211_alq != NULL);
107293a2caeSAugustin Cavalier	error = sysctl_handle_int(oidp, &enable, 0, req);
108293a2caeSAugustin Cavalier	if (error || !req->newptr)
109293a2caeSAugustin Cavalier		return (error);
110293a2caeSAugustin Cavalier	else
111293a2caeSAugustin Cavalier		return (ieee80211_alq_setlogging(enable));
112293a2caeSAugustin Cavalier}
113293a2caeSAugustin Cavalier
114293a2caeSAugustin CavalierSYSCTL_PROC(_net_wlan, OID_AUTO, alq, CTLTYPE_INT|CTLFLAG_RW,
115293a2caeSAugustin Cavalier	0, 0, sysctl_ieee80211_alq_log, "I", "Enable net80211 alq logging");
116293a2caeSAugustin CavalierSYSCTL_INT(_net_wlan, OID_AUTO, alq_size, CTLFLAG_RW,
1178244a9baSAugustin Cavalier	&ieee80211_alq_qsize, 0, "In-memory log size (bytes)");
118293a2caeSAugustin CavalierSYSCTL_INT(_net_wlan, OID_AUTO, alq_lost, CTLFLAG_RW,
119293a2caeSAugustin Cavalier	&ieee80211_alq_lost, 0, "Debugging operations not logged");
120293a2caeSAugustin CavalierSYSCTL_INT(_net_wlan, OID_AUTO, alq_logged, CTLFLAG_RW,
121293a2caeSAugustin Cavalier	&ieee80211_alq_logged, 0, "Debugging operations logged");
122293a2caeSAugustin Cavalier
123293a2caeSAugustin Cavalierstatic struct ale *
1248244a9baSAugustin Cavalierieee80211_alq_get(size_t len)
125293a2caeSAugustin Cavalier{
126293a2caeSAugustin Cavalier	struct ale *ale;
127293a2caeSAugustin Cavalier
1288244a9baSAugustin Cavalier	ale = alq_getn(ieee80211_alq, len + sizeof(struct ieee80211_alq_rec),
1298244a9baSAugustin Cavalier	    ALQ_NOWAIT);
130293a2caeSAugustin Cavalier	if (!ale)
131293a2caeSAugustin Cavalier		ieee80211_alq_lost++;
132293a2caeSAugustin Cavalier	else
133293a2caeSAugustin Cavalier		ieee80211_alq_logged++;
134293a2caeSAugustin Cavalier	return ale;
135293a2caeSAugustin Cavalier}
136293a2caeSAugustin Cavalier
1378244a9baSAugustin Cavalierint
1388244a9baSAugustin Cavalierieee80211_alq_log(struct ieee80211com *ic, struct ieee80211vap *vap,
1398244a9baSAugustin Cavalier    uint32_t op, uint32_t flags, uint16_t srcid, const uint8_t *src,
1408244a9baSAugustin Cavalier    size_t len)
141293a2caeSAugustin Cavalier{
142293a2caeSAugustin Cavalier	struct ale *ale;
143293a2caeSAugustin Cavalier	struct ieee80211_alq_rec *r;
1448244a9baSAugustin Cavalier	char *dst;
145293a2caeSAugustin Cavalier
1468244a9baSAugustin Cavalier	/* Don't log if we're disabled */
147293a2caeSAugustin Cavalier	if (ieee80211_alq == NULL)
1488244a9baSAugustin Cavalier		return (0);
1498244a9baSAugustin Cavalier
1508244a9baSAugustin Cavalier	if (len > IEEE80211_ALQ_MAX_PAYLOAD)
1518244a9baSAugustin Cavalier		return (ENOMEM);
152293a2caeSAugustin Cavalier
1538244a9baSAugustin Cavalier	ale = ieee80211_alq_get(len);
154293a2caeSAugustin Cavalier	if (! ale)
1558244a9baSAugustin Cavalier		return (ENOMEM);
156293a2caeSAugustin Cavalier
157293a2caeSAugustin Cavalier	r = (struct ieee80211_alq_rec *) ale->ae_data;
1588244a9baSAugustin Cavalier	dst = ((char *) r) + sizeof(struct ieee80211_alq_rec);
1598244a9baSAugustin Cavalier	r->r_timestamp = htobe64(ticks);
1608244a9baSAugustin Cavalier	if (vap != NULL) {
1618244a9baSAugustin Cavalier		r->r_wlan = htobe16(vap->iv_ifp->if_dunit);
1628244a9baSAugustin Cavalier	} else {
1638244a9baSAugustin Cavalier		r->r_wlan = 0xffff;
1648244a9baSAugustin Cavalier	}
1658244a9baSAugustin Cavalier	r->r_src = htobe16(srcid);
1668244a9baSAugustin Cavalier	r->r_flags = htobe32(flags);
1678244a9baSAugustin Cavalier	r->r_op = htobe32(op);
1688244a9baSAugustin Cavalier	r->r_len = htobe32(len + sizeof(struct ieee80211_alq_rec));
1698244a9baSAugustin Cavalier	r->r_threadid = htobe32((uint32_t) curthread->td_tid);
1708244a9baSAugustin Cavalier
1718244a9baSAugustin Cavalier	if (src != NULL)
1728244a9baSAugustin Cavalier		memcpy(dst, src, len);
1738244a9baSAugustin Cavalier
174293a2caeSAugustin Cavalier	alq_post(ieee80211_alq, ale);
1758244a9baSAugustin Cavalier
1768244a9baSAugustin Cavalier	return (0);
177293a2caeSAugustin Cavalier}
178