1*5b040abeSmaxv /* $NetBSD: ieee80211_netbsd.c,v 1.34 2018/12/22 14:28:56 maxv Exp $ */
2f43b1441Smaxv
3f43b1441Smaxv /*
43cdc4fcdSdyoung * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
53cdc4fcdSdyoung * All rights reserved.
63cdc4fcdSdyoung *
73cdc4fcdSdyoung * Redistribution and use in source and binary forms, with or without
83cdc4fcdSdyoung * modification, are permitted provided that the following conditions
93cdc4fcdSdyoung * are met:
103cdc4fcdSdyoung * 1. Redistributions of source code must retain the above copyright
113cdc4fcdSdyoung * notice, this list of conditions and the following disclaimer.
123cdc4fcdSdyoung * 2. Redistributions in binary form must reproduce the above copyright
133cdc4fcdSdyoung * notice, this list of conditions and the following disclaimer in the
143cdc4fcdSdyoung * documentation and/or other materials provided with the distribution.
153cdc4fcdSdyoung * 3. The name of the author may not be used to endorse or promote products
163cdc4fcdSdyoung * derived from this software without specific prior written permission.
173cdc4fcdSdyoung *
183cdc4fcdSdyoung * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
193cdc4fcdSdyoung * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
203cdc4fcdSdyoung * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
213cdc4fcdSdyoung * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
223cdc4fcdSdyoung * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
233cdc4fcdSdyoung * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
243cdc4fcdSdyoung * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
253cdc4fcdSdyoung * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
263cdc4fcdSdyoung * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
273cdc4fcdSdyoung * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
283cdc4fcdSdyoung */
293cdc4fcdSdyoung
303cdc4fcdSdyoung #include <sys/cdefs.h>
3190634029Sdyoung #ifdef __FreeBSD__
3287515e34Sskrll __FBSDID("$FreeBSD: src/sys/net80211/ieee80211_freebsd.c,v 1.8 2005/08/08 18:46:35 sam Exp $");
3390634029Sdyoung #else
34*5b040abeSmaxv __KERNEL_RCSID(0, "$NetBSD: ieee80211_netbsd.c,v 1.34 2018/12/22 14:28:56 maxv Exp $");
3590634029Sdyoung #endif
363cdc4fcdSdyoung
373cdc4fcdSdyoung /*
389c99eab1Sthorpej * IEEE 802.11 support (NetBSD-specific code)
393cdc4fcdSdyoung */
403cdc4fcdSdyoung #include <sys/param.h>
413cdc4fcdSdyoung #include <sys/kernel.h>
423cdc4fcdSdyoung #include <sys/systm.h>
433cdc4fcdSdyoung #include <sys/mbuf.h>
443cdc4fcdSdyoung #include <sys/proc.h>
453cdc4fcdSdyoung #include <sys/sysctl.h>
469c99eab1Sthorpej #include <sys/once.h>
473cdc4fcdSdyoung
483cdc4fcdSdyoung #include <sys/socket.h>
493cdc4fcdSdyoung
503afd44cfStls #include <sys/cprng.h>
513afd44cfStls
523cdc4fcdSdyoung #include <net/if.h>
533cdc4fcdSdyoung #include <net/if_media.h>
5490634029Sdyoung #include <net/if_ether.h>
553cdc4fcdSdyoung #include <net/route.h>
563cdc4fcdSdyoung
5790634029Sdyoung #include <net80211/ieee80211_netbsd.h>
583cdc4fcdSdyoung #include <net80211/ieee80211_var.h>
5990634029Sdyoung #include <net80211/ieee80211_sysctl.h>
603cdc4fcdSdyoung
6190634029Sdyoung #define LOGICALLY_EQUAL(x, y) (!(x) == !(y))
6290634029Sdyoung
6390634029Sdyoung static void ieee80211_sysctl_fill_node(struct ieee80211_node *,
642bfe9aceSdyoung struct ieee80211_node_sysctl *, int, const struct ieee80211_channel *,
652bfe9aceSdyoung uint32_t);
6690634029Sdyoung static struct ieee80211_node *ieee80211_node_walknext(
6790634029Sdyoung struct ieee80211_node_walk *);
6890634029Sdyoung static struct ieee80211_node *ieee80211_node_walkfirst(
6990634029Sdyoung struct ieee80211_node_walk *, u_short);
7090634029Sdyoung static int ieee80211_sysctl_node(SYSCTLFN_ARGS);
713cdc4fcdSdyoung
723eab37f7Spooka static void ieee80211_sysctl_setup(void);
733eab37f7Spooka
743cdc4fcdSdyoung #ifdef IEEE80211_DEBUG
753cdc4fcdSdyoung int ieee80211_debug = 0;
763cdc4fcdSdyoung #endif
773cdc4fcdSdyoung
7887515e34Sskrll typedef void (*ieee80211_setup_func)(void);
7987515e34Sskrll
8087515e34Sskrll __link_set_decl(ieee80211_funcs, ieee80211_setup_func);
8187515e34Sskrll
82dae53410Syamt static int
ieee80211_init0(void)839c99eab1Sthorpej ieee80211_init0(void)
8487515e34Sskrll {
8587515e34Sskrll ieee80211_setup_func * const *ieee80211_setup, f;
8687515e34Sskrll
873eab37f7Spooka ieee80211_sysctl_setup();
883eab37f7Spooka
895523aadfSmatt if (max_linkhdr < ALIGN(sizeof(struct ieee80211_qosframe_addr4))) {
905523aadfSmatt max_linkhdr = ALIGN(sizeof(struct ieee80211_qosframe_addr4));
915523aadfSmatt }
925523aadfSmatt
9387515e34Sskrll __link_set_foreach(ieee80211_setup, ieee80211_funcs) {
9487515e34Sskrll f = (void*)*ieee80211_setup;
9587515e34Sskrll (*f)();
9687515e34Sskrll }
97dae53410Syamt
98dae53410Syamt return 0;
9987515e34Sskrll }
10087515e34Sskrll
1019c99eab1Sthorpej void
ieee80211_init(void)1029c99eab1Sthorpej ieee80211_init(void)
1039c99eab1Sthorpej {
1049c99eab1Sthorpej static ONCE_DECL(ieee80211_init_once);
1059c99eab1Sthorpej
1069c99eab1Sthorpej RUN_ONCE(&ieee80211_init_once, ieee80211_init0);
1079c99eab1Sthorpej }
1089c99eab1Sthorpej
1093cdc4fcdSdyoung static int
ieee80211_sysctl_inact(SYSCTLFN_ARGS)11090634029Sdyoung ieee80211_sysctl_inact(SYSCTLFN_ARGS)
1113cdc4fcdSdyoung {
11290634029Sdyoung int error, t;
11390634029Sdyoung struct sysctlnode node;
1143cdc4fcdSdyoung
11590634029Sdyoung node = *rnode;
116f43b1441Smaxv
117f43b1441Smaxv /*
118f43b1441Smaxv * sysctl_lookup copies the product from t. Then, it
11990634029Sdyoung * copies the new value onto t.
12090634029Sdyoung */
12190634029Sdyoung t = *(int*)rnode->sysctl_data * IEEE80211_INACT_WAIT;
12290634029Sdyoung node.sysctl_data = &t;
12390634029Sdyoung error = sysctl_lookup(SYSCTLFN_CALL(&node));
12490634029Sdyoung if (error || newp == NULL)
125f43b1441Smaxv return error;
12690634029Sdyoung
127f43b1441Smaxv /*
128f43b1441Smaxv * The new value was in seconds. Convert to inactivity-wait
12990634029Sdyoung * intervals. There are IEEE80211_INACT_WAIT seconds per
13090634029Sdyoung * interval.
13190634029Sdyoung */
13290634029Sdyoung *(int*)rnode->sysctl_data = t / IEEE80211_INACT_WAIT;
13390634029Sdyoung
134f43b1441Smaxv return 0;
1353cdc4fcdSdyoung }
1363cdc4fcdSdyoung
1373cdc4fcdSdyoung static int
ieee80211_sysctl_parent(SYSCTLFN_ARGS)13890634029Sdyoung ieee80211_sysctl_parent(SYSCTLFN_ARGS)
1393cdc4fcdSdyoung {
14090634029Sdyoung struct ieee80211com *ic;
14190634029Sdyoung char pname[IFNAMSIZ];
14290634029Sdyoung struct sysctlnode node;
1433cdc4fcdSdyoung
14490634029Sdyoung node = *rnode;
14590634029Sdyoung ic = node.sysctl_data;
146fe2925feSmaya strlcpy(pname, ic->ic_ifp->if_xname, IFNAMSIZ);
14790634029Sdyoung node.sysctl_data = pname;
14890634029Sdyoung return sysctl_lookup(SYSCTLFN_CALL(&node));
14990634029Sdyoung }
15090634029Sdyoung
15190634029Sdyoung /*
15290634029Sdyoung * Create or get top of sysctl tree net.link.ieee80211.
15390634029Sdyoung */
15490634029Sdyoung static const struct sysctlnode *
ieee80211_sysctl_treetop(struct sysctllog ** log)15590634029Sdyoung ieee80211_sysctl_treetop(struct sysctllog **log)
15690634029Sdyoung {
15790634029Sdyoung int rc;
15890634029Sdyoung const struct sysctlnode *rnode;
15990634029Sdyoung
16090634029Sdyoung if ((rc = sysctl_createv(log, 0, NULL, &rnode,
16190634029Sdyoung CTLFLAG_PERMANENT, CTLTYPE_NODE, "link",
16290634029Sdyoung "link-layer statistics and controls",
1634f6fb3bfSpooka NULL, 0, NULL, 0, CTL_NET, PF_LINK, CTL_EOL)) != 0)
16490634029Sdyoung goto err;
16590634029Sdyoung
16690634029Sdyoung if ((rc = sysctl_createv(log, 0, &rnode, &rnode,
16790634029Sdyoung CTLFLAG_PERMANENT, CTLTYPE_NODE, "ieee80211",
16890634029Sdyoung "IEEE 802.11 WLAN statistics and controls",
16990634029Sdyoung NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
17090634029Sdyoung goto err;
17190634029Sdyoung
17290634029Sdyoung return rnode;
17390634029Sdyoung err:
17490634029Sdyoung printf("%s: sysctl_createv failed, rc = %d\n", __func__, rc);
17590634029Sdyoung return NULL;
1763cdc4fcdSdyoung }
1773cdc4fcdSdyoung
1783cdc4fcdSdyoung void
ieee80211_sysctl_attach(struct ieee80211com * ic)1793cdc4fcdSdyoung ieee80211_sysctl_attach(struct ieee80211com *ic)
1803cdc4fcdSdyoung {
18190634029Sdyoung int rc;
18290634029Sdyoung const struct sysctlnode *cnode, *rnode;
18390634029Sdyoung char num[sizeof("vap") + 14]; /* sufficient for 32 bits */
1843cdc4fcdSdyoung
18590634029Sdyoung if ((rnode = ieee80211_sysctl_treetop(NULL)) == NULL)
1863cdc4fcdSdyoung return;
18790634029Sdyoung
18890634029Sdyoung snprintf(num, sizeof(num), "vap%u", ic->ic_vap);
18990634029Sdyoung
19090634029Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &rnode,
19190634029Sdyoung CTLFLAG_PERMANENT, CTLTYPE_NODE, num, SYSCTL_DESCR("virtual AP"),
19290634029Sdyoung NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
19390634029Sdyoung goto err;
19490634029Sdyoung
19590634029Sdyoung /* control debugging printfs */
19690634029Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &cnode,
19790634029Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READONLY, CTLTYPE_STRING,
19890634029Sdyoung "parent", SYSCTL_DESCR("parent device"),
199e21a34c2Sdsl ieee80211_sysctl_parent, 0, (void *)ic, IFNAMSIZ, CTL_CREATE,
20090634029Sdyoung CTL_EOL)) != 0)
20190634029Sdyoung goto err;
20290634029Sdyoung
2033cdc4fcdSdyoung #ifdef IEEE80211_DEBUG
20490634029Sdyoung /* control debugging printfs */
20590634029Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &cnode,
20690634029Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
20790634029Sdyoung "debug", SYSCTL_DESCR("control debugging printfs"),
20890634029Sdyoung NULL, ieee80211_debug, &ic->ic_debug, 0,
20990634029Sdyoung CTL_CREATE, CTL_EOL)) != 0)
21090634029Sdyoung goto err;
2113cdc4fcdSdyoung #endif
2123cdc4fcdSdyoung /* XXX inherit from tunables */
21390634029Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &cnode,
21490634029Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
21590634029Sdyoung "inact_run", SYSCTL_DESCR("station inactivity timeout (sec)"),
21690634029Sdyoung ieee80211_sysctl_inact, 0, &ic->ic_inact_run, 0,
21790634029Sdyoung CTL_CREATE, CTL_EOL)) != 0)
21890634029Sdyoung goto err;
21990634029Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &cnode,
22090634029Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
22190634029Sdyoung "inact_probe",
22290634029Sdyoung SYSCTL_DESCR("station inactivity probe timeout (sec)"),
22390634029Sdyoung ieee80211_sysctl_inact, 0, &ic->ic_inact_probe, 0,
22490634029Sdyoung CTL_CREATE, CTL_EOL)) != 0)
22590634029Sdyoung goto err;
22690634029Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &cnode,
22790634029Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
22890634029Sdyoung "inact_auth",
22990634029Sdyoung SYSCTL_DESCR("station authentication timeout (sec)"),
23090634029Sdyoung ieee80211_sysctl_inact, 0, &ic->ic_inact_auth, 0,
23190634029Sdyoung CTL_CREATE, CTL_EOL)) != 0)
23290634029Sdyoung goto err;
23390634029Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &cnode,
23490634029Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
23590634029Sdyoung "inact_init",
23690634029Sdyoung SYSCTL_DESCR("station initial state timeout (sec)"),
23790634029Sdyoung ieee80211_sysctl_inact, 0, &ic->ic_inact_init, 0,
23890634029Sdyoung CTL_CREATE, CTL_EOL)) != 0)
23990634029Sdyoung goto err;
24090634029Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &cnode,
24190634029Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
24290634029Sdyoung "driver_caps", SYSCTL_DESCR("driver capabilities"),
24390634029Sdyoung NULL, 0, &ic->ic_caps, 0, CTL_CREATE, CTL_EOL)) != 0)
24490634029Sdyoung goto err;
24525e9e914Sdyoung if ((rc = sysctl_createv(&ic->ic_sysctllog, 0, &rnode, &cnode,
24625e9e914Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
24725e9e914Sdyoung "bmiss_max", SYSCTL_DESCR("consecutive beacon misses before scanning"),
24825e9e914Sdyoung NULL, 0, &ic->ic_bmiss_max, 0, CTL_CREATE, CTL_EOL)) != 0)
24925e9e914Sdyoung goto err;
25090634029Sdyoung
25190634029Sdyoung return;
25290634029Sdyoung err:
25390634029Sdyoung printf("%s: sysctl_createv failed, rc = %d\n", __func__, rc);
2543cdc4fcdSdyoung }
2553cdc4fcdSdyoung
2563cdc4fcdSdyoung void
ieee80211_sysctl_detach(struct ieee80211com * ic)2573cdc4fcdSdyoung ieee80211_sysctl_detach(struct ieee80211com *ic)
2583cdc4fcdSdyoung {
25990634029Sdyoung sysctl_teardown(&ic->ic_sysctllog);
2603cdc4fcdSdyoung }
26190634029Sdyoung
26290634029Sdyoung /*
26390634029Sdyoung * Pointers for testing:
26490634029Sdyoung *
26590634029Sdyoung * If there are no interfaces, or else no 802.11 interfaces,
26690634029Sdyoung * ieee80211_node_walkfirst must return NULL.
26790634029Sdyoung *
26890634029Sdyoung * If there is any single 802.11 interface, ieee80211_node_walkfirst
26990634029Sdyoung * must not return NULL.
27090634029Sdyoung */
27190634029Sdyoung static struct ieee80211_node *
ieee80211_node_walkfirst(struct ieee80211_node_walk * nw,u_short if_index)2729a7ebfddSdyoung ieee80211_node_walkfirst(struct ieee80211_node_walk *nw, u_short if_index)
27390634029Sdyoung {
274f43b1441Smaxv memset(nw, 0, sizeof(*nw));
27590634029Sdyoung
27690634029Sdyoung nw->nw_ifindex = if_index;
27790634029Sdyoung
2789a7ebfddSdyoung LIST_FOREACH(nw->nw_ic, &ieee80211com_head, ic_list) {
2799a7ebfddSdyoung if (if_index != 0 && nw->nw_ic->ic_ifp->if_index != if_index)
28090634029Sdyoung continue;
2819a7ebfddSdyoung if (!TAILQ_EMPTY(&nw->nw_ic->ic_sta.nt_node))
2829a7ebfddSdyoung nw->nw_nt = &nw->nw_ic->ic_sta;
2839a7ebfddSdyoung else if (!TAILQ_EMPTY(&nw->nw_ic->ic_scan.nt_node))
2849a7ebfddSdyoung nw->nw_nt = &nw->nw_ic->ic_scan;
2859a7ebfddSdyoung else if (nw->nw_ic->ic_bss == NULL)
2869a7ebfddSdyoung continue;
28790634029Sdyoung break;
28890634029Sdyoung }
28990634029Sdyoung
2909a7ebfddSdyoung if (nw->nw_ic == NULL)
2919a7ebfddSdyoung return NULL;
2929a7ebfddSdyoung
2939a7ebfddSdyoung if (nw->nw_nt == NULL)
2949a7ebfddSdyoung nw->nw_ni = nw->nw_ic->ic_bss;
2959a7ebfddSdyoung else
2969a7ebfddSdyoung nw->nw_ni = TAILQ_FIRST(&nw->nw_nt->nt_node);
29790634029Sdyoung
29890634029Sdyoung return nw->nw_ni;
29990634029Sdyoung }
30090634029Sdyoung
30190634029Sdyoung static struct ieee80211_node *
ieee80211_node_walknext(struct ieee80211_node_walk * nw)30290634029Sdyoung ieee80211_node_walknext(struct ieee80211_node_walk *nw)
30390634029Sdyoung {
3049a7ebfddSdyoung if (nw->nw_nt != NULL)
30590634029Sdyoung nw->nw_ni = TAILQ_NEXT(nw->nw_ni, ni_list);
3069a7ebfddSdyoung else
3079a7ebfddSdyoung nw->nw_ni = NULL;
30890634029Sdyoung
3099a7ebfddSdyoung while (nw->nw_ni == NULL) {
3109a7ebfddSdyoung if (nw->nw_nt == &nw->nw_ic->ic_sta) {
3119a7ebfddSdyoung nw->nw_nt = &nw->nw_ic->ic_scan;
3129a7ebfddSdyoung nw->nw_ni = TAILQ_FIRST(&nw->nw_nt->nt_node);
3139a7ebfddSdyoung continue;
3149a7ebfddSdyoung } else if (nw->nw_nt == &nw->nw_ic->ic_scan) {
3159a7ebfddSdyoung nw->nw_nt = NULL;
3169a7ebfddSdyoung nw->nw_ni = nw->nw_ic->ic_bss;
3179a7ebfddSdyoung continue;
3189a7ebfddSdyoung }
3199a7ebfddSdyoung KASSERT(nw->nw_nt == NULL);
32090634029Sdyoung if (nw->nw_ifindex != 0)
32190634029Sdyoung return NULL;
32290634029Sdyoung
32390634029Sdyoung nw->nw_ic = LIST_NEXT(nw->nw_ic, ic_list);
32490634029Sdyoung if (nw->nw_ic == NULL)
32590634029Sdyoung return NULL;
32690634029Sdyoung
3279a7ebfddSdyoung nw->nw_nt = &nw->nw_ic->ic_sta;
3289a7ebfddSdyoung nw->nw_ni = TAILQ_FIRST(&nw->nw_nt->nt_node);
32990634029Sdyoung }
33090634029Sdyoung
33190634029Sdyoung return nw->nw_ni;
33290634029Sdyoung }
33390634029Sdyoung
33490634029Sdyoung static void
ieee80211_sysctl_fill_node(struct ieee80211_node * ni,struct ieee80211_node_sysctl * ns,int ifindex,const struct ieee80211_channel * chan0,uint32_t flags)33590634029Sdyoung ieee80211_sysctl_fill_node(struct ieee80211_node *ni,
33690634029Sdyoung struct ieee80211_node_sysctl *ns, int ifindex,
3372bfe9aceSdyoung const struct ieee80211_channel *chan0, uint32_t flags)
33890634029Sdyoung {
339f43b1441Smaxv memset(ns, 0, sizeof(*ns));
340f43b1441Smaxv
34190634029Sdyoung ns->ns_ifindex = ifindex;
34290634029Sdyoung ns->ns_capinfo = ni->ni_capinfo;
3432bfe9aceSdyoung ns->ns_flags = flags;
344f43b1441Smaxv memcpy(ns->ns_macaddr, ni->ni_macaddr, sizeof(ns->ns_macaddr));
345f43b1441Smaxv memcpy(ns->ns_bssid, ni->ni_bssid, sizeof(ns->ns_bssid));
34690634029Sdyoung if (ni->ni_chan != IEEE80211_CHAN_ANYC) {
34790634029Sdyoung ns->ns_freq = ni->ni_chan->ic_freq;
34890634029Sdyoung ns->ns_chanflags = ni->ni_chan->ic_flags;
34990634029Sdyoung ns->ns_chanidx = ni->ni_chan - chan0;
35090634029Sdyoung } else {
35190634029Sdyoung ns->ns_freq = ns->ns_chanflags = 0;
35290634029Sdyoung ns->ns_chanidx = 0;
35390634029Sdyoung }
35490634029Sdyoung ns->ns_rssi = ni->ni_rssi;
35590634029Sdyoung ns->ns_esslen = ni->ni_esslen;
356f43b1441Smaxv memcpy(ns->ns_essid, ni->ni_essid, sizeof(ns->ns_essid));
35790634029Sdyoung ns->ns_erp = ni->ni_erp;
35890634029Sdyoung ns->ns_associd = ni->ni_associd;
35990634029Sdyoung ns->ns_inact = ni->ni_inact * IEEE80211_INACT_WAIT;
36090634029Sdyoung ns->ns_rstamp = ni->ni_rstamp;
36190634029Sdyoung ns->ns_rates = ni->ni_rates;
36290634029Sdyoung ns->ns_txrate = ni->ni_txrate;
36390634029Sdyoung ns->ns_intval = ni->ni_intval;
364f43b1441Smaxv memcpy(ns->ns_tstamp, &ni->ni_tstamp, sizeof(ns->ns_tstamp));
36590634029Sdyoung ns->ns_txseq = ni->ni_txseqs[0];
36690634029Sdyoung ns->ns_rxseq = ni->ni_rxseqs[0];
36790634029Sdyoung ns->ns_fhdwell = ni->ni_fhdwell;
36890634029Sdyoung ns->ns_fhindex = ni->ni_fhindex;
36990634029Sdyoung ns->ns_fails = ni->ni_fails;
37090634029Sdyoung }
37190634029Sdyoung
37290634029Sdyoung /* Between two examinations of the sysctl tree, I expect each
37390634029Sdyoung * interface to add no more than 5 nodes.
37490634029Sdyoung */
37590634029Sdyoung #define IEEE80211_SYSCTL_NODE_GROWTH 5
37690634029Sdyoung
37790634029Sdyoung static int
ieee80211_sysctl_node(SYSCTLFN_ARGS)37890634029Sdyoung ieee80211_sysctl_node(SYSCTLFN_ARGS)
37990634029Sdyoung {
38090634029Sdyoung struct ieee80211_node_walk nw;
38190634029Sdyoung struct ieee80211_node *ni;
38290634029Sdyoung struct ieee80211_node_sysctl ns;
38390634029Sdyoung char *dp;
38490634029Sdyoung u_int cur_ifindex, ifcount, ifindex, last_ifindex, op, arg, hdr_type;
3852bfe9aceSdyoung uint32_t flags;
38690634029Sdyoung size_t len, needed, eltsize, out_size;
387c511c459Sdyoung int error, s, saw_bss = 0, nelt;
38890634029Sdyoung
38990634029Sdyoung if (namelen == 1 && name[0] == CTL_QUERY)
39090634029Sdyoung return (sysctl_query(SYSCTLFN_CALL(rnode)));
39190634029Sdyoung
39290634029Sdyoung if (namelen != IEEE80211_SYSCTL_NODENAMELEN)
39390634029Sdyoung return (EINVAL);
39490634029Sdyoung
39590634029Sdyoung /* ifindex.op.arg.header-type.eltsize.nelt */
39690634029Sdyoung dp = oldp;
39790634029Sdyoung len = (oldp != NULL) ? *oldlenp : 0;
39890634029Sdyoung ifindex = name[IEEE80211_SYSCTL_NODENAME_IF];
39990634029Sdyoung op = name[IEEE80211_SYSCTL_NODENAME_OP];
40090634029Sdyoung arg = name[IEEE80211_SYSCTL_NODENAME_ARG];
40190634029Sdyoung hdr_type = name[IEEE80211_SYSCTL_NODENAME_TYPE];
40290634029Sdyoung eltsize = name[IEEE80211_SYSCTL_NODENAME_ELTSIZE];
40390634029Sdyoung nelt = name[IEEE80211_SYSCTL_NODENAME_ELTCOUNT];
40490634029Sdyoung out_size = MIN(sizeof(ns), eltsize);
40590634029Sdyoung
40690634029Sdyoung if (op != IEEE80211_SYSCTL_OP_ALL || arg != 0 ||
40790634029Sdyoung hdr_type != IEEE80211_SYSCTL_T_NODE || eltsize < 1 || nelt < 0)
40890634029Sdyoung return (EINVAL);
40990634029Sdyoung
41090634029Sdyoung error = 0;
41190634029Sdyoung needed = 0;
41290634029Sdyoung ifcount = 0;
41390634029Sdyoung last_ifindex = 0;
41490634029Sdyoung
41590634029Sdyoung s = splnet();
41690634029Sdyoung
41790634029Sdyoung for (ni = ieee80211_node_walkfirst(&nw, ifindex); ni != NULL;
41890634029Sdyoung ni = ieee80211_node_walknext(&nw)) {
41990634029Sdyoung struct ieee80211com *ic;
42090634029Sdyoung
42190634029Sdyoung ic = nw.nw_ic;
42290634029Sdyoung cur_ifindex = ic->ic_ifp->if_index;
42390634029Sdyoung
42490634029Sdyoung if (cur_ifindex != last_ifindex) {
4250e4587aeSdyoung saw_bss = 0;
42690634029Sdyoung ifcount++;
42790634029Sdyoung last_ifindex = cur_ifindex;
42890634029Sdyoung }
42990634029Sdyoung
43090634029Sdyoung if (nelt <= 0)
43190634029Sdyoung continue;
43290634029Sdyoung
433c511c459Sdyoung if (saw_bss && ni == ic->ic_bss)
434c511c459Sdyoung continue;
4352bfe9aceSdyoung else if (ni == ic->ic_bss) {
436c511c459Sdyoung saw_bss = 1;
4372bfe9aceSdyoung flags = IEEE80211_NODE_SYSCTL_F_BSS;
4382bfe9aceSdyoung } else
4392bfe9aceSdyoung flags = 0;
4402bfe9aceSdyoung if (ni->ni_table == &ic->ic_scan)
4412bfe9aceSdyoung flags |= IEEE80211_NODE_SYSCTL_F_SCAN;
4422bfe9aceSdyoung else if (ni->ni_table == &ic->ic_sta)
4432bfe9aceSdyoung flags |= IEEE80211_NODE_SYSCTL_F_STA;
44490634029Sdyoung if (len >= eltsize) {
44590634029Sdyoung ieee80211_sysctl_fill_node(ni, &ns, cur_ifindex,
4462bfe9aceSdyoung &ic->ic_channels[0], flags);
44790634029Sdyoung error = copyout(&ns, dp, out_size);
44890634029Sdyoung if (error)
44990634029Sdyoung goto cleanup;
45090634029Sdyoung dp += eltsize;
45190634029Sdyoung len -= eltsize;
45290634029Sdyoung }
45390634029Sdyoung needed += eltsize;
45490634029Sdyoung if (nelt != INT_MAX)
45590634029Sdyoung nelt--;
45690634029Sdyoung }
45790634029Sdyoung cleanup:
45890634029Sdyoung splx(s);
45990634029Sdyoung
46090634029Sdyoung *oldlenp = needed;
46190634029Sdyoung if (oldp == NULL)
46290634029Sdyoung *oldlenp += ifcount * IEEE80211_SYSCTL_NODE_GROWTH * eltsize;
46390634029Sdyoung
46490634029Sdyoung return (error);
46590634029Sdyoung }
46690634029Sdyoung
46790634029Sdyoung /*
46890634029Sdyoung * Setup sysctl(3) MIB, net.ieee80211.*
46990634029Sdyoung *
4700efea177Sad * TBD condition CTLFLAG_PERMANENT on being a module or not
47190634029Sdyoung */
4723eab37f7Spooka static struct sysctllog *ieee80211_sysctllog;
4733eab37f7Spooka static void
ieee80211_sysctl_setup(void)4743eab37f7Spooka ieee80211_sysctl_setup(void)
47590634029Sdyoung {
47690634029Sdyoung int rc;
477c2ec5838Schristos const struct sysctlnode *rnode;
47890634029Sdyoung
4793eab37f7Spooka if ((rnode = ieee80211_sysctl_treetop(&ieee80211_sysctllog)) == NULL)
48090634029Sdyoung return;
48190634029Sdyoung
4823eab37f7Spooka if ((rc = sysctl_createv(&ieee80211_sysctllog, 0, &rnode, NULL,
48390634029Sdyoung CTLFLAG_PERMANENT, CTLTYPE_NODE, "nodes", "client/peer stations",
48490634029Sdyoung ieee80211_sysctl_node, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
48590634029Sdyoung goto err;
48690634029Sdyoung
48790634029Sdyoung #ifdef IEEE80211_DEBUG
48890634029Sdyoung /* control debugging printfs */
489c2ec5838Schristos if ((rc = sysctl_createv(&ieee80211_sysctllog, 0, &rnode, NULL,
49090634029Sdyoung CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
49190634029Sdyoung "debug", SYSCTL_DESCR("control debugging printfs"),
49290634029Sdyoung NULL, 0, &ieee80211_debug, 0, CTL_CREATE, CTL_EOL)) != 0)
49390634029Sdyoung goto err;
494f43b1441Smaxv #endif
49590634029Sdyoung
4963eab37f7Spooka ieee80211_rssadapt_sysctl_setup(&ieee80211_sysctllog);
4973eab37f7Spooka
49890634029Sdyoung return;
49990634029Sdyoung err:
50090634029Sdyoung printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
5013cdc4fcdSdyoung }
5023cdc4fcdSdyoung
5033cdc4fcdSdyoung int
ieee80211_node_dectestref(struct ieee80211_node * ni)5043cdc4fcdSdyoung ieee80211_node_dectestref(struct ieee80211_node *ni)
5053cdc4fcdSdyoung {
5062ed4138bSdyoung if (atomic_dec_uint_nv(&ni->ni_refcnt) == 0) {
5072ed4138bSdyoung atomic_inc_uint(&ni->ni_refcnt);
5082ed4138bSdyoung return 1;
50990634029Sdyoung } else
5102ed4138bSdyoung return 0;
51190634029Sdyoung }
51290634029Sdyoung
51390634029Sdyoung void
ieee80211_drain_ifq(struct ifqueue * ifq)514f8d4f721Sdegroote ieee80211_drain_ifq(struct ifqueue *ifq)
515f8d4f721Sdegroote {
516f8d4f721Sdegroote struct ieee80211_node *ni;
517f8d4f721Sdegroote struct mbuf *m;
518f8d4f721Sdegroote
519f8d4f721Sdegroote for (;;) {
520f8d4f721Sdegroote IF_DEQUEUE(ifq, m);
521f8d4f721Sdegroote if (m == NULL)
522f8d4f721Sdegroote break;
523f8d4f721Sdegroote
524cef59834Sozaki-r ni = M_GETCTX(m, struct ieee80211_node *);
525f8d4f721Sdegroote KASSERT(ni != NULL);
526f8d4f721Sdegroote ieee80211_free_node(ni);
527cef59834Sozaki-r M_SETCTX(m, NULL);
528f8d4f721Sdegroote
529f8d4f721Sdegroote m_freem(m);
530f8d4f721Sdegroote }
531f8d4f721Sdegroote }
532f8d4f721Sdegroote
533f8d4f721Sdegroote void
if_printf(struct ifnet * ifp,const char * fmt,...)53490634029Sdyoung if_printf(struct ifnet *ifp, const char *fmt, ...)
53590634029Sdyoung {
53690634029Sdyoung va_list ap;
53790634029Sdyoung va_start(ap, fmt);
53890634029Sdyoung
53990634029Sdyoung printf("%s: ", ifp->if_xname);
54090634029Sdyoung vprintf(fmt, ap);
54190634029Sdyoung
54290634029Sdyoung va_end(ap);
54390634029Sdyoung return;
54490634029Sdyoung }
54590634029Sdyoung
5463cdc4fcdSdyoung /*
5473cdc4fcdSdyoung * Allocate and setup a management frame of the specified
5483cdc4fcdSdyoung * size. We return the mbuf and a pointer to the start
5493cdc4fcdSdyoung * of the contiguous data area that's been reserved based
5503cdc4fcdSdyoung * on the packet length. The data area is forced to 32-bit
5513cdc4fcdSdyoung * alignment and the buffer length to a multiple of 4 bytes.
5523cdc4fcdSdyoung * This is done mainly so beacon frames (that require this)
5533cdc4fcdSdyoung * can use this interface too.
5543cdc4fcdSdyoung */
5553cdc4fcdSdyoung struct mbuf *
ieee80211_getmgtframe(u_int8_t ** frm,u_int pktlen)5563cdc4fcdSdyoung ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
5573cdc4fcdSdyoung {
5583cdc4fcdSdyoung struct mbuf *m;
5593cdc4fcdSdyoung u_int len;
5603cdc4fcdSdyoung
5613cdc4fcdSdyoung /*
5623cdc4fcdSdyoung * NB: we know the mbuf routines will align the data area
5633cdc4fcdSdyoung * so we don't need to do anything special.
5643cdc4fcdSdyoung */
5653cdc4fcdSdyoung /* XXX 4-address frame? */
5663cdc4fcdSdyoung len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
56790634029Sdyoung IASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
568f43b1441Smaxv
56990634029Sdyoung if (len <= MHLEN) {
5703cdc4fcdSdyoung m = m_gethdr(M_NOWAIT, MT_HEADER);
5713cdc4fcdSdyoung /*
5723cdc4fcdSdyoung * Align the data in case additional headers are added.
5733cdc4fcdSdyoung * This should only happen when a WEP header is added
5743cdc4fcdSdyoung * which only happens for shared key authentication mgt
5753cdc4fcdSdyoung * frames which all fit in MHLEN.
5763cdc4fcdSdyoung */
5773cdc4fcdSdyoung if (m != NULL)
578*5b040abeSmaxv m_align(m, len);
579f43b1441Smaxv } else {
5803cdc4fcdSdyoung m = m_getcl(M_NOWAIT, MT_HEADER, M_PKTHDR);
581f43b1441Smaxv }
582f43b1441Smaxv
5833cdc4fcdSdyoung if (m != NULL) {
5843cdc4fcdSdyoung m->m_data += sizeof(struct ieee80211_frame);
5853cdc4fcdSdyoung *frm = m->m_data;
5862bfe9aceSdyoung IASSERT((uintptr_t)*frm % 4 == 0, ("bad beacon boundary"));
5873cdc4fcdSdyoung }
588f43b1441Smaxv
5893cdc4fcdSdyoung return m;
5903cdc4fcdSdyoung }
5913cdc4fcdSdyoung
5923cdc4fcdSdyoung void
get_random_bytes(void * p,size_t n)5933cdc4fcdSdyoung get_random_bytes(void *p, size_t n)
5943cdc4fcdSdyoung {
5953afd44cfStls cprng_fast(p, n);
5963cdc4fcdSdyoung }
5973cdc4fcdSdyoung
5983cdc4fcdSdyoung void
ieee80211_notify_node_join(struct ieee80211com * ic,struct ieee80211_node * ni,int newassoc)599f43b1441Smaxv ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni,
600f43b1441Smaxv int newassoc)
6013cdc4fcdSdyoung {
6023cdc4fcdSdyoung struct ifnet *ifp = ic->ic_ifp;
6033cdc4fcdSdyoung struct ieee80211_join_event iev;
6043cdc4fcdSdyoung
605ef999039Schristos IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, "%snode %s join\n",
606ef999039Schristos (ni == ic->ic_bss) ? "bss " : "",
6079a7ebfddSdyoung ether_sprintf(ni->ni_macaddr));
6089a7ebfddSdyoung
60990634029Sdyoung memset(&iev, 0, sizeof(iev));
61092b58820Sdyoung if (ni == ic->ic_bss) {
6113cdc4fcdSdyoung IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
6123cdc4fcdSdyoung rt_ieee80211msg(ifp, newassoc ?
6133cdc4fcdSdyoung RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
6143cdc4fcdSdyoung &iev, sizeof(iev));
6153cdc4fcdSdyoung if_link_state_change(ifp, LINK_STATE_UP);
6163f5a745eSchristos } else {
6173cdc4fcdSdyoung IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
6183f5a745eSchristos rt_ieee80211msg(ifp, newassoc ?
6193f5a745eSchristos RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN,
6203f5a745eSchristos &iev, sizeof(iev));
6213cdc4fcdSdyoung }
6223cdc4fcdSdyoung }
6233cdc4fcdSdyoung
6243cdc4fcdSdyoung void
ieee80211_notify_node_leave(struct ieee80211com * ic,struct ieee80211_node * ni)6253cdc4fcdSdyoung ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
6263cdc4fcdSdyoung {
6273cdc4fcdSdyoung struct ifnet *ifp = ic->ic_ifp;
6283cdc4fcdSdyoung struct ieee80211_leave_event iev;
6293cdc4fcdSdyoung
630ef999039Schristos IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, "%snode %s leave\n",
631ef999039Schristos (ni == ic->ic_bss) ? "bss " : "",
6329a7ebfddSdyoung ether_sprintf(ni->ni_macaddr));
6339a7ebfddSdyoung
6343cdc4fcdSdyoung if (ni == ic->ic_bss) {
6353cdc4fcdSdyoung rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
6363cdc4fcdSdyoung if_link_state_change(ifp, LINK_STATE_DOWN);
6373cdc4fcdSdyoung } else {
6383cdc4fcdSdyoung /* fire off wireless event station leaving */
6393cdc4fcdSdyoung memset(&iev, 0, sizeof(iev));
6403cdc4fcdSdyoung IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
6413cdc4fcdSdyoung rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev));
6423cdc4fcdSdyoung }
6433cdc4fcdSdyoung }
6443cdc4fcdSdyoung
6453cdc4fcdSdyoung void
ieee80211_notify_scan_done(struct ieee80211com * ic)6463cdc4fcdSdyoung ieee80211_notify_scan_done(struct ieee80211com *ic)
6473cdc4fcdSdyoung {
6483cdc4fcdSdyoung struct ifnet *ifp = ic->ic_ifp;
6493cdc4fcdSdyoung
6503cdc4fcdSdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
651ef999039Schristos "%s", "notify scan done\n");
6523cdc4fcdSdyoung
6533cdc4fcdSdyoung /* dispatch wireless event indicating scan completed */
6543cdc4fcdSdyoung rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
6553cdc4fcdSdyoung }
6563cdc4fcdSdyoung
6573cdc4fcdSdyoung void
ieee80211_notify_replay_failure(struct ieee80211com * ic,const struct ieee80211_frame * wh,const struct ieee80211_key * k,u_int64_t rsc)6583cdc4fcdSdyoung ieee80211_notify_replay_failure(struct ieee80211com *ic,
6593cdc4fcdSdyoung const struct ieee80211_frame *wh, const struct ieee80211_key *k,
6603cdc4fcdSdyoung u_int64_t rsc)
6613cdc4fcdSdyoung {
6623cdc4fcdSdyoung struct ifnet *ifp = ic->ic_ifp;
6633cdc4fcdSdyoung
6643cdc4fcdSdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
66587515e34Sskrll "[%s] %s replay detected <rsc %ju, csc %ju, keyix %u rxkeyix %u>\n",
6663cdc4fcdSdyoung ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name,
66787515e34Sskrll (intmax_t) rsc, (intmax_t) k->wk_keyrsc,
66887515e34Sskrll k->wk_keyix, k->wk_rxkeyix);
6693cdc4fcdSdyoung
6703cdc4fcdSdyoung if (ifp != NULL) { /* NB: for cipher test modules */
6713cdc4fcdSdyoung struct ieee80211_replay_event iev;
6723cdc4fcdSdyoung
6733cdc4fcdSdyoung IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
6743cdc4fcdSdyoung IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
6753cdc4fcdSdyoung iev.iev_cipher = k->wk_cipher->ic_cipher;
67687515e34Sskrll if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE)
67787515e34Sskrll iev.iev_keyix = k->wk_rxkeyix;
67887515e34Sskrll else
6793cdc4fcdSdyoung iev.iev_keyix = k->wk_keyix;
6803cdc4fcdSdyoung iev.iev_keyrsc = k->wk_keyrsc;
6813cdc4fcdSdyoung iev.iev_rsc = rsc;
6823cdc4fcdSdyoung rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
6833cdc4fcdSdyoung }
6843cdc4fcdSdyoung }
6853cdc4fcdSdyoung
6863cdc4fcdSdyoung void
ieee80211_notify_michael_failure(struct ieee80211com * ic,const struct ieee80211_frame * wh,u_int keyix)6873cdc4fcdSdyoung ieee80211_notify_michael_failure(struct ieee80211com *ic,
6883cdc4fcdSdyoung const struct ieee80211_frame *wh, u_int keyix)
6893cdc4fcdSdyoung {
6903cdc4fcdSdyoung struct ifnet *ifp = ic->ic_ifp;
6913cdc4fcdSdyoung
6923cdc4fcdSdyoung IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
6933cdc4fcdSdyoung "[%s] michael MIC verification failed <keyix %u>\n",
6943cdc4fcdSdyoung ether_sprintf(wh->i_addr2), keyix);
6953cdc4fcdSdyoung ic->ic_stats.is_rx_tkipmic++;
6963cdc4fcdSdyoung
6973cdc4fcdSdyoung if (ifp != NULL) { /* NB: for cipher test modules */
6983cdc4fcdSdyoung struct ieee80211_michael_event iev;
6993cdc4fcdSdyoung
7003cdc4fcdSdyoung IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
7013cdc4fcdSdyoung IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
7023cdc4fcdSdyoung iev.iev_cipher = IEEE80211_CIPHER_TKIP;
7033cdc4fcdSdyoung iev.iev_keyix = keyix;
7043cdc4fcdSdyoung rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev));
7053cdc4fcdSdyoung }
7063cdc4fcdSdyoung }
7073cdc4fcdSdyoung
7083cdc4fcdSdyoung void
ieee80211_load_module(const char * modname)7093cdc4fcdSdyoung ieee80211_load_module(const char *modname)
7103cdc4fcdSdyoung {
7113cdc4fcdSdyoung #ifdef notyet
7123cdc4fcdSdyoung struct thread *td = curthread;
7133cdc4fcdSdyoung
7143cdc4fcdSdyoung if (suser(td) == 0 && securelevel_gt(td->td_ucred, 0) == 0) {
7153cdc4fcdSdyoung mtx_lock(&Giant);
7163cdc4fcdSdyoung (void) linker_load_module(modname, NULL, NULL, NULL, NULL);
7173cdc4fcdSdyoung mtx_unlock(&Giant);
7183cdc4fcdSdyoung }
7193cdc4fcdSdyoung #else
7203cdc4fcdSdyoung printf("%s: load the %s module by hand for now.\n", __func__, modname);
7213cdc4fcdSdyoung #endif
7223cdc4fcdSdyoung }
7233f23c9efSmaxv
7243f23c9efSmaxv /* -------------------------------------------------------------------------- */
7253f23c9efSmaxv
7263f23c9efSmaxv /*
7273f23c9efSmaxv * Append the specified data to the indicated mbuf chain,
7283f23c9efSmaxv * Extend the mbuf chain if the new data does not fit in
7293f23c9efSmaxv * existing space.
7303f23c9efSmaxv *
7313f23c9efSmaxv * Return 1 if able to complete the job; otherwise 0.
7323f23c9efSmaxv */
7333f23c9efSmaxv int
m_append(struct mbuf * m0,int len,const void * cpv)7343f23c9efSmaxv m_append(struct mbuf *m0, int len, const void *cpv)
7353f23c9efSmaxv {
7363f23c9efSmaxv struct mbuf *m, *n;
7373f23c9efSmaxv int remainder, space;
7383f23c9efSmaxv const char *cp = cpv;
7393f23c9efSmaxv
7403f23c9efSmaxv KASSERT(len != M_COPYALL);
7413f23c9efSmaxv for (m = m0; m->m_next != NULL; m = m->m_next)
7423f23c9efSmaxv continue;
7433f23c9efSmaxv remainder = len;
7443f23c9efSmaxv space = M_TRAILINGSPACE(m);
7453f23c9efSmaxv if (space > 0) {
7463f23c9efSmaxv /*
7473f23c9efSmaxv * Copy into available space.
7483f23c9efSmaxv */
7493f23c9efSmaxv if (space > remainder)
7503f23c9efSmaxv space = remainder;
7513f23c9efSmaxv memmove(mtod(m, char *) + m->m_len, cp, space);
7523f23c9efSmaxv m->m_len += space;
7533f23c9efSmaxv cp = cp + space, remainder -= space;
7543f23c9efSmaxv }
7553f23c9efSmaxv while (remainder > 0) {
7563f23c9efSmaxv /*
7573f23c9efSmaxv * Allocate a new mbuf; could check space
7583f23c9efSmaxv * and allocate a cluster instead.
7593f23c9efSmaxv */
7603f23c9efSmaxv n = m_get(M_DONTWAIT, m->m_type);
7613f23c9efSmaxv if (n == NULL)
7623f23c9efSmaxv break;
763d1579b2dSriastradh n->m_len = uimin(MLEN, remainder);
7643f23c9efSmaxv memmove(mtod(n, void *), cp, n->m_len);
7653f23c9efSmaxv cp += n->m_len, remainder -= n->m_len;
7663f23c9efSmaxv m->m_next = n;
7673f23c9efSmaxv m = n;
7683f23c9efSmaxv }
7693f23c9efSmaxv if (m0->m_flags & M_PKTHDR)
7703f23c9efSmaxv m0->m_pkthdr.len += len - remainder;
7713f23c9efSmaxv return (remainder == 0);
7723f23c9efSmaxv }
773