From c104cff26ea3910047fb9c08ca58dc20539e8522 Mon Sep 17 00:00:00 2001 From: Rui Paulo Date: Mon, 20 Jul 2009 19:12:08 +0000 Subject: [PATCH] More mesh bits, namely: * bridge support (sam) * handling of errors (sam) * deletion of inactive routing entries * more debug msgs (sam) * fixed some inconsistencies with the spec. * decap is now specific to mesh (sam) * print mesh seq. no. on ifconfig list mesh * small perf. improvements Reviewed by: sam Approved by: re (kib) --- sbin/ifconfig/ifieee80211.c | 10 +- sys/net80211/ieee80211_hwmp.c | 111 ++++++---- sys/net80211/ieee80211_input.c | 16 +- sys/net80211/ieee80211_ioctl.h | 18 +- sys/net80211/ieee80211_mesh.c | 356 +++++++++++++++++++++++++++----- sys/net80211/ieee80211_mesh.h | 40 +++- sys/net80211/ieee80211_output.c | 49 +++-- 7 files changed, 459 insertions(+), 141 deletions(-) diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c index 2b196a615abb..be929fcf37db 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -3965,21 +3965,23 @@ list_mesh(int s) if (ioctl(s, SIOCG80211, &ireq) < 0) err(1, "unable to get the Mesh routing table"); - printf("%-17.17s %-17.17s %4s %4s %4s\n" + printf("%-17.17s %-17.17s %4s %4s %4s %6s\n" , "DEST" , "NEXT HOP" , "HOPS" , "METRIC" - , "LIFETIME"); + , "LIFETIME" + , "MSEQ"); for (i = 0; i < ireq.i_len / sizeof(*routes); i++) { printf("%s ", ether_ntoa((const struct ether_addr *)routes[i].imr_dest)); - printf("%s %4u %4d %6d\n", + printf("%s %4u %4u %6u %6u\n", ether_ntoa((const struct ether_addr *) routes[i].imr_nexthop), routes[i].imr_nhops, routes[i].imr_metric, - routes[i].imr_lifetime); + routes[i].imr_lifetime, + routes[i].imr_lastmseq); } } diff --git a/sys/net80211/ieee80211_hwmp.c b/sys/net80211/ieee80211_hwmp.c index 5a369590ea7b..41b119f7a801 100644 --- a/sys/net80211/ieee80211_hwmp.c +++ b/sys/net80211/ieee80211_hwmp.c @@ -121,7 +121,6 @@ static int ieee80211_hwmp_replyforward = 1; static const int ieee80211_hwmp_maxprepretries = 3; static const struct timeval ieee80211_hwmp_maxhopstime = { 0, 500000 }; static const struct timeval ieee80211_hwmp_preqminint = { 0, 100000 }; -static const struct timeval ieee80211_hwmp_prepminint = { 0, 100000 }; static const struct timeval ieee80211_hwmp_perrminint = { 0, 100000 }; static const struct timeval ieee80211_hwmp_roottimeout = { 5, 0 }; static const struct timeval ieee80211_hwmp_pathtimeout = { 5, 0 }; @@ -152,8 +151,6 @@ static const struct timeval ieee80211_hwmp_confirmint = { 2, 0 }; /* NB: the Target Address set in a Proactive PREQ is the broadcast address. */ static const uint8_t broadcastaddr[IEEE80211_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; -static const uint8_t invalidaddr[IEEE80211_ADDR_LEN] = - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; typedef uint32_t ieee80211_hwmp_seq; #define IEEE80211_HWMP_SEQ_LEQ(a, b) ((int32_t)((a)-(b)) <= 0) @@ -171,7 +168,6 @@ struct ieee80211_hwmp_state { ieee80211_hwmp_seq hs_seq; /* next seq to be used */ ieee80211_hwmp_seq hs_preqid; /* next PREQ ID to be used */ struct timeval hs_lastpreq; /* last time we sent a PREQ */ - struct timeval hs_lastprep; /* last time we sent a PREP */ struct timeval hs_lastperr; /* last time we sent a PERR */ int hs_rootmode; /* proactive HWMP */ struct callout hs_roottimer; @@ -201,6 +197,8 @@ static const struct ieee80211_mesh_proto_path mesh_proto_hwmp = { .mpp_vdetach = hwmp_vdetach, .mpp_newstate = hwmp_newstate, .mpp_privlen = sizeof(struct ieee80211_hwmp_route), + /* ieee80211_hwmp_pathtimeout */ + .mpp_inact = { 5, 0 }, }; @@ -250,8 +248,7 @@ hwmp_vdetach(struct ieee80211vap *vap) { struct ieee80211_hwmp_state *hs = vap->iv_hwmp; - if (callout_active(&hs->hs_roottimer)) - callout_drain(&hs->hs_roottimer); + callout_drain(&hs->hs_roottimer); free(vap->iv_hwmp, M_80211_VAP); vap->iv_hwmp = NULL; } @@ -269,6 +266,8 @@ hwmp_newstate(struct ieee80211vap *vap, enum ieee80211_state ostate, int arg) /* Flush the table on RUN -> !RUN, e.g. interface down & up */ if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) callout_drain(&hs->hs_roottimer); + if (nstate == IEEE80211_S_RUN) + hwmp_rootmode_setup(vap); return 0; } @@ -644,7 +643,7 @@ hwmp_rootmode_cb(void *arg) struct ieee80211_meshpreq_ie preq; IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, - "%s", "sending broadcast PREQ"); + "%s", "send broadcast PREQ"); /* XXX check portal role */ preq.preq_flags = IEEE80211_MESHPREQ_FLAGS_AM; @@ -683,7 +682,7 @@ hwmp_rootmode_rann_cb(void *arg) struct ieee80211_meshrann_ie rann; IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, vap->iv_bss, - "%s", "sending broadcast RANN"); + "%s", "send broadcast RANN"); /* XXX check portal role */ rann.rann_flags = 0; @@ -740,7 +739,7 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, */ if (IEEE80211_ADDR_EQ(vap->iv_myaddr, PREQ_TADDR(0))) { IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, - "replying to %s", ether_sprintf(preq->preq_origaddr)); + "reply to %s", ether_sprintf(preq->preq_origaddr)); /* * Build and send a PREP frame. */ @@ -760,7 +759,7 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, rt = ieee80211_mesh_rt_find(vap, preq->preq_origaddr); if (rt == NULL) hwmp_discover(vap, preq->preq_origaddr, NULL); - else if (IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr)) + else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) hwmp_discover(vap, rt->rt_dest, NULL); return; } @@ -774,30 +773,36 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, (IEEE80211_MESHPREQ_TFLAGS_TO|IEEE80211_MESHPREQ_TFLAGS_RF)))) { uint8_t rootmac[IEEE80211_ADDR_LEN]; - IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, - "root mesh station @ %s", - ether_sprintf(preq->preq_origaddr)); IEEE80211_ADDR_COPY(rootmac, preq->preq_origaddr); rt = ieee80211_mesh_rt_find(vap, rootmac); - if (rt == NULL) + if (rt == NULL) { rt = ieee80211_mesh_rt_add(vap, rootmac); + if (rt == NULL) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "unable to add root mesh path to %s", + ether_sprintf(rootmac)); + vap->iv_stats.is_mesh_rtaddfailed++; + return; + } + } + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "root mesh station @ %s", ether_sprintf(rootmac)); + /* * Reply with a PREP if we don't have a path to the root * or if the root sent us a proactive PREQ. */ - if (IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr) || + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || (preq->preq_flags & IEEE80211_MESHPREQ_FLAGS_PP)) { prep.prep_flags = 0; prep.prep_hopcount = 0; prep.prep_ttl = ms->ms_ttl; - IEEE80211_ADDR_COPY(prep.prep_origaddr, - vap->iv_myaddr); + IEEE80211_ADDR_COPY(prep.prep_origaddr, vap->iv_myaddr); prep.prep_origseq = preq->preq_origseq; prep.prep_targetseq = ++hs->hs_seq; prep.prep_lifetime = preq->preq_lifetime; prep.prep_metric = IEEE80211_MESHLMETRIC_INITIALVAL; - IEEE80211_ADDR_COPY(prep.prep_targetaddr, - rootmac); + IEEE80211_ADDR_COPY(prep.prep_targetaddr, rootmac); prep.prep_targetseq = PREQ_TSEQ(0); hwmp_send_prep(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &prep); @@ -820,7 +825,7 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, * We have a valid route to this node. */ if (rt != NULL && - !IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr)) { + (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID)) { hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); @@ -829,7 +834,7 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, if (preq->preq_ttl > 1 && preq->preq_hopcount < hs->hs_maxhops) { IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, - "forwarding PREQ from %s", + "forward PREQ from %s", ether_sprintf(preq->preq_origaddr)); /* * Propagate the original PREQ. @@ -880,8 +885,16 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, */ } else if (preq->preq_ttl > 1 && preq->preq_hopcount < hs->hs_maxhops) { - if (rt == NULL) + if (rt == NULL) { rt = ieee80211_mesh_rt_add(vap, PREQ_TADDR(0)); + if (rt == NULL) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, + ni, "unable to add PREQ path to %s", + ether_sprintf(PREQ_TADDR(0))); + vap->iv_stats.is_mesh_rtaddfailed++; + return; + } + } hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); rt->rt_metric = preq->preq_metric; @@ -890,7 +903,7 @@ hwmp_recv_preq(struct ieee80211vap *vap, struct ieee80211_node *ni, hr->hr_preqid = preq->preq_id; IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, - "forwarding PREQ from %s", + "forward PREQ from %s", ether_sprintf(preq->preq_origaddr)); ppreq.preq_hopcount += 1; ppreq.preq_ttl -= 1; @@ -968,7 +981,7 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, struct ieee80211_meshprep_ie pprep; /* propagated PREP */ IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, - "propagating PREP from %s", + "propagate PREP from %s", ether_sprintf(prep->prep_origaddr)); memcpy(&pprep, prep, sizeof(pprep)); @@ -986,10 +999,18 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, */ if (hs->hs_rootmode != IEEE80211_HWMP_ROOTMODE_DISABLED) { rt = ieee80211_mesh_rt_add(vap, prep->prep_origaddr); + if (rt == NULL) { + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, + ni, "unable to add PREP path to %s", + ether_sprintf(prep->prep_origaddr)); + vap->iv_stats.is_mesh_rtaddfailed++; + return; + } IEEE80211_ADDR_COPY(rt->rt_nexthop, wh->i_addr2); rt->rt_nhops = prep->prep_hopcount; rt->rt_lifetime = prep->prep_lifetime; rt->rt_metric = prep->prep_metric; + rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; return; } return; @@ -1002,7 +1023,7 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, * If we do, check if this path reply contains a * better route. */ - if (IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr)) + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) useprep = 1; else if (prep->prep_hopcount < rt->rt_nhops || prep->prep_metric < rt->rt_metric) @@ -1012,6 +1033,7 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, rt->rt_nhops = prep->prep_hopcount; rt->rt_lifetime = prep->prep_lifetime; rt->rt_metric = prep->prep_metric; + rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID; } } else { IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, @@ -1044,6 +1066,8 @@ hwmp_recv_prep(struct ieee80211vap *vap, struct ieee80211_node *ni, for (; m != NULL; m = next) { next = m->m_nextpkt; m->m_nextpkt = NULL; + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, + "flush queued frame %p len %d", m, m->m_pkthdr.len); ifp->if_transmit(ifp, m); } } @@ -1054,14 +1078,7 @@ hwmp_send_prep(struct ieee80211_node *ni, const uint8_t da[IEEE80211_ADDR_LEN], struct ieee80211_meshprep_ie *prep) { - struct ieee80211_hwmp_state *hs = ni->ni_vap->iv_hwmp; - - /* - * Enforce PREP interval. - */ - if (ratecheck(&hs->hs_lastprep, &ieee80211_hwmp_prepminint) == 0) - return EALREADY; - getmicrouptime(&hs->hs_lastprep); + /* NB: there's no PREP minimum interval. */ /* * mesh prep action frame format @@ -1092,7 +1109,7 @@ hwmp_peerdown(struct ieee80211_node *ni) return; hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, - "%s", "deleting route entry"); + "%s", "delete route entry"); perr.perr_mode = 0; perr.perr_ndests = 1; IEEE80211_ADDR_COPY(PERR_DADDR(0), rt->rt_dest); @@ -1144,7 +1161,7 @@ hwmp_recv_perr(struct ieee80211vap *vap, struct ieee80211_node *ni, */ if (forward) { IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, ni, - "propagating PERR from %s", ether_sprintf(wh->i_addr2)); + "propagate PERR from %s", ether_sprintf(wh->i_addr2)); memcpy(&pperr, perr, sizeof(*perr)); hwmp_send_perr(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &pperr); } @@ -1264,13 +1281,16 @@ hwmp_discover(struct ieee80211vap *vap, if (rt == NULL) { rt = ieee80211_mesh_rt_add(vap, dest); if (rt == NULL) { - /* XXX stat+msg */ + IEEE80211_NOTE(vap, IEEE80211_MSG_HWMP, + ni, "unable to add discovery path to %s", + ether_sprintf(dest)); + vap->iv_stats.is_mesh_rtaddfailed++; goto done; } } hr = IEEE80211_MESH_ROUTE_PRIV(rt, struct ieee80211_hwmp_route); - if (IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr)) { + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { if (hr->hr_preqid == 0) { hr->hr_seq = ++hs->hs_seq; hr->hr_preqid = ++hs->hs_preqid; @@ -1281,7 +1301,9 @@ hwmp_discover(struct ieee80211vap *vap, /* XXX check preq retries */ sendpreq = 1; IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, - "%s", "initiating path discovery"); + "start path discovery (src %s)", + m == NULL ? "" : ether_sprintf( + mtod(m, struct ether_header *)->ether_shost)); /* * Try to discover the path for this node. */ @@ -1306,16 +1328,16 @@ hwmp_discover(struct ieee80211vap *vap, hwmp_send_preq(vap->iv_bss, vap->iv_myaddr, broadcastaddr, &preq); } - if (!IEEE80211_ADDR_EQ(rt->rt_nexthop, invalidaddr)) + if (rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) ni = ieee80211_find_txnode(vap, rt->rt_nexthop); } else { ni = ieee80211_find_txnode(vap, dest); + /* NB: if null then we leak mbuf */ + KASSERT(ni != NULL, ("leak mcast frame")); return ni; } done: if (ni == NULL && m != NULL) { - IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, - dest, NULL, "%s", "no valid path to this node"); if (sendpreq) { struct ieee80211com *ic = vap->iv_ic; /* @@ -1323,13 +1345,18 @@ hwmp_discover(struct ieee80211vap *vap, * completes. If discovery never completes the * frame will be flushed by way of the aging timer. */ + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_HWMP, dest, + "%s", "queue frame until path found"); m->m_pkthdr.rcvif = (void *)(uintptr_t) ieee80211_mac_hash(ic, dest); /* XXX age chosen randomly */ ieee80211_ageq_append(&ic->ic_stageq, m, IEEE80211_INACT_WAIT); - } else + } else { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_HWMP, + dest, NULL, "%s", "no valid path to this node"); m_freem(m); + } } return ni; } diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index b2b6368df0a6..3c87a3ae34e0 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -233,22 +233,17 @@ ieee80211_deliver_data(struct ieee80211vap *vap, struct mbuf * ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen) { -#ifdef IEEE80211_SUPPORT_MESH - union { - struct ieee80211_qosframe_addr4 wh4; - uint8_t b[sizeof(struct ieee80211_qosframe_addr4) + - sizeof(struct ieee80211_meshcntl_ae11)]; - } whu; -#define wh whu.wh4 -#else struct ieee80211_qosframe_addr4 wh; -#endif struct ether_header *eh; struct llc *llc; + KASSERT(hdrlen <= sizeof(wh), + ("hdrlen %d > max %zd", hdrlen, sizeof(wh))); + if (m->m_len < hdrlen + sizeof(*llc) && (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) { - /* XXX stat, msg */ + vap->iv_stats.is_rx_tooshort++; + /* XXX msg */ return NULL; } memcpy(&wh, mtod(m, caddr_t), hdrlen); @@ -295,7 +290,6 @@ ieee80211_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen) eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); } return m; -#undef wh } /* diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 8105990c695f..c3c454f69aaf 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -235,9 +235,13 @@ struct ieee80211_stats { uint32_t is_hwmp_wrongseq; /* wrong hwmp seq no. */ uint32_t is_hwmp_rootreqs; /* root PREQs sent */ uint32_t is_hwmp_rootrann; /* root RANNs sent */ - uint32_t is_rx_badalign; /* dropped 'cuz misaligned */ - uint32_t is_spare[15]; + uint32_t is_mesh_badae; /* dropped 'cuz invalid AE */ + uint32_t is_mesh_rtaddfailed; /* route add failed */ + uint32_t is_mesh_notproxy; /* dropped 'cuz not proxying */ + uint32_t is_rx_badalign; /* dropped 'cuz misaligned */ + + uint32_t is_spare[12]; }; /* @@ -328,6 +332,16 @@ enum { IEEE80211_MESH_RTCMD_DELETE = 3, /* delete an entry from the table */ }; +struct ieee80211req_mesh_route { + uint8_t imr_dest[IEEE80211_ADDR_LEN]; + uint8_t imr_nexthop[IEEE80211_ADDR_LEN]; + uint16_t imr_nhops; + uint16_t imr_pad; + uint32_t imr_metric; + uint32_t imr_lifetime; + uint32_t imr_lastmseq; +}; + /* * HWMP root modes */ diff --git a/sys/net80211/ieee80211_mesh.c b/sys/net80211/ieee80211_mesh.c index 677c1ea1c263..912cad45fc25 100644 --- a/sys/net80211/ieee80211_mesh.c +++ b/sys/net80211/ieee80211_mesh.c @@ -62,10 +62,12 @@ __FBSDID("$FreeBSD$"); #include #include +static void mesh_rt_flush_invalid(struct ieee80211vap *); static int mesh_select_proto_path(struct ieee80211vap *, const char *); static int mesh_select_proto_metric(struct ieee80211vap *, const char *); static void mesh_vattach(struct ieee80211vap *); static int mesh_newstate(struct ieee80211vap *, enum ieee80211_state, int); +static void mesh_rt_cleanup_cb(void *); static void mesh_linkchange(struct ieee80211_node *, enum ieee80211_mesh_mlstate); static void mesh_checkid(void *, struct ieee80211_node *); @@ -138,6 +140,7 @@ static struct ieee80211_mesh_proto_path mesh_proto_paths[4]; static struct ieee80211_mesh_proto_metric mesh_proto_metrics[4]; #define MESH_RT_LOCK(ms) mtx_lock(&(ms)->ms_rt_lock) +#define MESH_RT_LOCK_ASSERT(ms) mtx_assert(&(ms)->ms_rt_lock, MA_OWNED) #define MESH_RT_UNLOCK(ms) mtx_unlock(&(ms)->ms_rt_lock) MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh", "802.11s routing table"); @@ -145,6 +148,44 @@ MALLOC_DEFINE(M_80211_MESH_RT, "80211mesh", "802.11s routing table"); /* * Helper functions to manipulate the Mesh routing table. */ + +static struct ieee80211_mesh_route * +mesh_rt_find_locked(struct ieee80211_mesh_state *ms, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_route *rt; + + MESH_RT_LOCK_ASSERT(ms); + + TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { + if (IEEE80211_ADDR_EQ(dest, rt->rt_dest)) + return rt; + } + return NULL; +} + +static struct ieee80211_mesh_route * +mesh_rt_add_locked(struct ieee80211_mesh_state *ms, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_route *rt; + + KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest), + ("%s: adding broadcast to the routing table", __func__)); + + MESH_RT_LOCK_ASSERT(ms); + + rt = malloc(ALIGN(sizeof(struct ieee80211_mesh_route)) + + ms->ms_ppath->mpp_privlen, M_80211_MESH_RT, M_NOWAIT | M_ZERO); + if (rt != NULL) { + IEEE80211_ADDR_COPY(rt->rt_dest, dest); + rt->rt_priv = (void *)ALIGN(&rt[1]); + getmicrouptime(&rt->rt_crtime); + TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next); + } + return rt; +} + struct ieee80211_mesh_route * ieee80211_mesh_rt_find(struct ieee80211vap *vap, const uint8_t dest[IEEE80211_ADDR_LEN]) @@ -153,14 +194,9 @@ ieee80211_mesh_rt_find(struct ieee80211vap *vap, struct ieee80211_mesh_route *rt; MESH_RT_LOCK(ms); - TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { - if (IEEE80211_ADDR_EQ(dest, rt->rt_dest)) { - MESH_RT_UNLOCK(ms); - return rt; - } - } + rt = mesh_rt_find_locked(ms, dest); MESH_RT_UNLOCK(ms); - return NULL; + return rt; } struct ieee80211_mesh_route * @@ -174,20 +210,65 @@ ieee80211_mesh_rt_add(struct ieee80211vap *vap, ("%s: duplicate entry in the routing table", __func__)); KASSERT(!IEEE80211_ADDR_EQ(vap->iv_myaddr, dest), ("%s: adding self to the routing table", __func__)); - KASSERT(!IEEE80211_ADDR_EQ(broadcastaddr, dest), - ("%s: adding broadcast to the routing table", __func__)); - rt = malloc(sizeof(struct ieee80211_mesh_route), M_80211_MESH_RT, - M_NOWAIT | M_ZERO); - IEEE80211_ADDR_COPY(rt->rt_dest, dest); - rt->rt_priv = malloc(ms->ms_ppath->mpp_privlen, M_80211_MESH_RT, - M_NOWAIT | M_ZERO); MESH_RT_LOCK(ms); - TAILQ_INSERT_TAIL(&ms->ms_routes, rt, rt_next); + rt = mesh_rt_add_locked(ms, dest); MESH_RT_UNLOCK(ms); return rt; } +/* + * Add a proxy route (as needed) for the specified destination. + */ +void +ieee80211_mesh_proxy_check(struct ieee80211vap *vap, + const uint8_t dest[IEEE80211_ADDR_LEN]) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt; + + MESH_RT_LOCK(ms); + rt = mesh_rt_find_locked(ms, dest); + if (rt == NULL) { + rt = mesh_rt_add_locked(ms, dest); + if (rt == NULL) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, + "%s", "unable to add proxy entry"); + vap->iv_stats.is_mesh_rtaddfailed++; + } else { + IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr); + rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID + | IEEE80211_MESHRT_FLAGS_PROXY; + } + /* XXX assert PROXY? */ + } else if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0) { + struct ieee80211com *ic = vap->iv_ic; + /* + * Fix existing entry created by received frames from + * stations that have some memory of dest. We also + * flush any frames held on the staging queue; delivering + * them is too much trouble right now. + */ + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, + "%s", "fix proxy entry"); + IEEE80211_ADDR_COPY(rt->rt_nexthop, vap->iv_myaddr); + rt->rt_flags |= IEEE80211_MESHRT_FLAGS_VALID + | IEEE80211_MESHRT_FLAGS_PROXY; + /* XXX belongs in hwmp */ + ieee80211_ageq_drain_node(&ic->ic_stageq, + (void *)(uintptr_t) ieee80211_mac_hash(ic, dest)); + /* XXX stat? */ + } + MESH_RT_UNLOCK(ms); +} + +static __inline void +mesh_rt_del(struct ieee80211_mesh_state *ms, struct ieee80211_mesh_route *rt) +{ + TAILQ_REMOVE(&ms->ms_routes, rt, rt_next); + free(rt, M_80211_MESH_RT); +} + void ieee80211_mesh_rt_del(struct ieee80211vap *vap, const uint8_t dest[IEEE80211_ADDR_LEN]) @@ -198,9 +279,7 @@ ieee80211_mesh_rt_del(struct ieee80211vap *vap, MESH_RT_LOCK(ms); TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) { if (IEEE80211_ADDR_EQ(rt->rt_dest, dest)) { - TAILQ_REMOVE(&ms->ms_routes, rt, rt_next); - free(rt->rt_priv, M_80211_MESH_RT); - free(rt, M_80211_MESH_RT); + mesh_rt_del(ms, rt); MESH_RT_UNLOCK(ms); return; } @@ -217,10 +296,32 @@ ieee80211_mesh_rt_flush(struct ieee80211vap *vap) if (ms == NULL) return; MESH_RT_LOCK(ms); + TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) + mesh_rt_del(ms, rt); + MESH_RT_UNLOCK(ms); +} + +/* + * Flush expired routing entries, i.e. those in invalid state for + * some time. + */ +static void +mesh_rt_flush_invalid(struct ieee80211vap *vap) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + struct ieee80211_mesh_route *rt, *next; + struct timeval tv, delta; + + if (ms == NULL) + return; + getmicrouptime(&tv); + MESH_RT_LOCK(ms); TAILQ_FOREACH_SAFE(rt, &ms->ms_routes, rt_next, next) { - TAILQ_REMOVE(&ms->ms_routes, rt, rt_next); - free(rt->rt_priv, M_80211_MESH_RT); - free(rt, M_80211_MESH_RT); + delta = tv; + timevalsub(&delta, &rt->rt_crtime); + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 && + timevalcmp(&delta, &ms->ms_ppath->mpp_inact, >=)) + mesh_rt_del(ms, rt); } MESH_RT_UNLOCK(ms); } @@ -381,7 +482,7 @@ mesh_vdetach_peers(void *arg, struct ieee80211_node *ni) IEEE80211_ACTION_MESHPEERING_CLOSE, args); } - callout_stop(&ni->ni_mltimer); + callout_drain(&ni->ni_mltimer); /* XXX belongs in hwmp */ ieee80211_ageq_drain_node(&ic->ic_stageq, (void *)(uintptr_t) ieee80211_mac_hash(ic, ni->ni_macaddr)); @@ -392,6 +493,7 @@ mesh_vdetach(struct ieee80211vap *vap) { struct ieee80211_mesh_state *ms = vap->iv_mesh; + callout_drain(&ms->ms_cleantimer); ieee80211_iterate_nodes(&vap->iv_ic->ic_sta, mesh_vdetach_peers, NULL); ieee80211_mesh_rt_flush(vap); @@ -413,7 +515,7 @@ mesh_vattach(struct ieee80211vap *vap) M_NOWAIT | M_ZERO); if (ms == NULL) { printf("%s: couldn't alloc MBSS state\n", __func__); - return; + return; } vap->iv_mesh = ms; ms->ms_seq = 0; @@ -421,6 +523,7 @@ mesh_vattach(struct ieee80211vap *vap) ms->ms_ttl = IEEE80211_MESH_DEFAULT_TTL; TAILQ_INIT(&ms->ms_routes); mtx_init(&ms->ms_rt_lock, "MBSS", "802.11s routing table", MTX_DEF); + callout_init(&ms->ms_cleantimer, CALLOUT_MPSAFE); mesh_select_proto_metric(vap, "AIRTIME"); KASSERT(ms->ms_pmetric, ("ms_pmetric == NULL")); mesh_select_proto_path(vap, "HWMP"); @@ -449,9 +552,8 @@ mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) if (ostate != IEEE80211_S_SCAN) ieee80211_cancel_scan(vap); /* background scan */ ni = vap->iv_bss; /* NB: no reference held */ - /* Flush the routing table */ - if (nstate != IEEE80211_S_INIT && ostate == IEEE80211_S_INIT) - ieee80211_mesh_rt_flush(vap); + if (nstate != IEEE80211_S_RUN && ostate == IEEE80211_S_RUN) + callout_drain(&ms->ms_cleantimer); switch (nstate) { case IEEE80211_S_INIT: switch (ostate) { @@ -471,6 +573,7 @@ mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) if (ostate != IEEE80211_S_INIT) { /* NB: optimize INIT -> INIT case */ ieee80211_reset_bss(vap); + ieee80211_mesh_rt_flush(vap); } break; case IEEE80211_S_SCAN: @@ -573,17 +676,33 @@ mesh_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) break; } ieee80211_node_authorize(vap->iv_bss); + callout_reset(&ms->ms_cleantimer, + msecs_to_ticks(ms->ms_ppath->mpp_inact.tv_sec * 1000 + + ms->ms_ppath->mpp_inact.tv_usec / 1000), + mesh_rt_cleanup_cb, vap); break; default: break; } - /* NB: ostate not nstate */ ms->ms_ppath->mpp_newstate(vap, ostate, arg); - return 0; } +static void +mesh_rt_cleanup_cb(void *arg) +{ + struct ieee80211vap *vap = arg; + struct ieee80211_mesh_state *ms = vap->iv_mesh; + + mesh_rt_flush_invalid(vap); + callout_reset(&ms->ms_cleantimer, + msecs_to_ticks(ms->ms_ppath->mpp_inact.tv_sec * 1000 + + ms->ms_ppath->mpp_inact.tv_usec / 1000), + mesh_rt_cleanup_cb, vap); +} + + /* * Helper function to note the Mesh Peer Link FSM change. */ @@ -617,8 +736,16 @@ mesh_linkchange(struct ieee80211_node *ni, enum ieee80211_mesh_mlstate state) ms->ms_neighbors--; } ni->ni_mlstate = state; - if (state == IEEE80211_NODE_MESH_HOLDING) + switch (state) { + case IEEE80211_NODE_MESH_HOLDING: ms->ms_ppath->mpp_peerdown(ni); + break; + case IEEE80211_NODE_MESH_ESTABLISHED: + ieee80211_mesh_discover(vap, ni->ni_macaddr, NULL); + break; + default: + break; + } } /* @@ -660,8 +787,11 @@ mesh_checkpseq(struct ieee80211vap *vap, rt = ieee80211_mesh_rt_find(vap, source); if (rt == NULL) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, source, + "add mcast route, mesh seqno %d", seq); rt = ieee80211_mesh_rt_add(vap, source); - rt->rt_lastmseq = seq; + if (rt != NULL) + rt->rt_lastmseq = seq; return 0; } if (IEEE80211_MESH_SEQ_GEQ(rt->rt_lastmseq, seq)) { @@ -684,6 +814,13 @@ mesh_find_txnode(struct ieee80211vap *vap, rt = ieee80211_mesh_rt_find(vap, dest); if (rt == NULL) return NULL; + if ((rt->rt_flags & IEEE80211_MESHRT_FLAGS_VALID) == 0 || + (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)) { + IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_MESH, dest, + "%s: !valid or proxy, flags 0x%x", __func__, rt->rt_flags); + /* XXX stat */ + return NULL; + } return ieee80211_find_txnode(vap, rt->rt_nexthop); } @@ -757,11 +894,6 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m, } IEEE80211_ADDR_COPY(whcopy->i_addr1, ni->ni_macaddr); } - IEEE80211_NOTE(vap, IEEE80211_MSG_MESH, ni, - "fwd %s frame from %s ttl %d", - IEEE80211_IS_MULTICAST(wh->i_addr1) ? "mcast" : "ucast", - ether_sprintf(wh->i_addr3), mccopy->mc_ttl); - KASSERT(mccopy->mc_ttl > 0, ("%s called with wrong ttl", __func__)); mccopy->mc_ttl--; @@ -779,6 +911,117 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m, } } +static struct mbuf * +mesh_decap(struct ieee80211vap *vap, struct mbuf *m, int hdrlen, int meshdrlen) +{ +#define WHDIR(wh) ((wh)->i_fc[1] & IEEE80211_FC1_DIR_MASK) + uint8_t b[sizeof(struct ieee80211_qosframe_addr4) + + sizeof(struct ieee80211_meshcntl_ae11)]; + const struct ieee80211_qosframe_addr4 *wh; + const struct ieee80211_meshcntl_ae10 *mc; + struct ether_header *eh; + struct llc *llc; + int ae; + + if (m->m_len < hdrlen + sizeof(*llc) && + (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY, + "discard data frame: %s", "m_pullup failed"); + vap->iv_stats.is_rx_tooshort++; + return NULL; + } + memcpy(b, mtod(m, caddr_t), hdrlen); + wh = (const struct ieee80211_qosframe_addr4 *)&b[0]; + mc = (const struct ieee80211_meshcntl_ae10 *)&b[hdrlen - meshdrlen]; + KASSERT(WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS || + WHDIR(wh) == IEEE80211_FC1_DIR_DSTODS, + ("bogus dir, fc 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1])); + + llc = (struct llc *)(mtod(m, caddr_t) + hdrlen); + if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && + llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && + llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0 && + /* NB: preserve AppleTalk frames that have a native SNAP hdr */ + !(llc->llc_snap.ether_type == htons(ETHERTYPE_AARP) || + llc->llc_snap.ether_type == htons(ETHERTYPE_IPX))) { + m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh)); + llc = NULL; + } else { + m_adj(m, hdrlen - sizeof(*eh)); + } + eh = mtod(m, struct ether_header *); + ae = mc->mc_flags & 3; + if (WHDIR(wh) == IEEE80211_FC1_DIR_FROMDS) { + IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr1); + if (ae == 0) { + IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr3); + } else if (ae == 1) { + IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr4); + } else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + (const struct ieee80211_frame *)wh, NULL, + "bad AE %d", ae); + vap->iv_stats.is_mesh_badae++; + m_freem(m); + return NULL; + } + } else { + if (ae == 0) { + IEEE80211_ADDR_COPY(eh->ether_dhost, wh->i_addr3); + IEEE80211_ADDR_COPY(eh->ether_shost, wh->i_addr4); + } else if (ae == 2) { + IEEE80211_ADDR_COPY(eh->ether_dhost, mc->mc_addr4); + IEEE80211_ADDR_COPY(eh->ether_shost, mc->mc_addr5); + } else { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + (const struct ieee80211_frame *)wh, NULL, + "bad AE %d", ae); + vap->iv_stats.is_mesh_badae++; + m_freem(m); + return NULL; + } + } +#ifdef ALIGNED_POINTER + if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) { + m = ieee80211_realign(vap, m, sizeof(*eh)); + if (m == NULL) + return NULL; + } +#endif /* ALIGNED_POINTER */ + if (llc != NULL) { + eh = mtod(m, struct ether_header *); + eh->ether_type = htons(m->m_pkthdr.len - sizeof(*eh)); + } + return m; +#undef WDIR +} + +/* + * Return non-zero if the unicast mesh data frame should be processed + * locally. Frames that are not proxy'd have our address, otherwise + * we need to consult the routing table to look for a proxy entry. + */ +static __inline int +mesh_isucastforme(struct ieee80211vap *vap, const struct ieee80211_frame *wh, + const struct ieee80211_meshcntl *mc) +{ + int ae = mc->mc_flags & 3; + + KASSERT((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS, + ("bad dir 0x%x:0x%x", wh->i_fc[0], wh->i_fc[1])); + KASSERT(ae == 0 || ae == 2, ("bad AE %d", ae)); + if (ae == 2) { /* ucast w/ proxy */ + const struct ieee80211_meshcntl_ae10 *mc10 = + (const struct ieee80211_meshcntl_ae10 *) mc; + struct ieee80211_mesh_route *rt = + ieee80211_mesh_rt_find(vap, mc10->mc_addr4); + /* check for proxy route to ourself */ + return (rt != NULL && + (rt->rt_flags & IEEE80211_MESHRT_FLAGS_PROXY)); + } else /* ucast w/o proxy */ + return IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr); +} + static int mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { @@ -789,7 +1032,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) struct ifnet *ifp = vap->iv_ifp; struct ieee80211_frame *wh; const struct ieee80211_meshcntl *mc; - int hdrspace, need_tap; + int hdrspace, meshdrlen, need_tap; uint8_t dir, type, subtype, qos; uint32_t seq; uint8_t *addr; @@ -904,13 +1147,14 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) } /* * Now calculate the full extent of the headers. Note - * ieee80211_decap will pull up anything we didn't get + * mesh_decap will pull up anything we didn't get * above when it strips the 802.11 headers. */ mc = (const struct ieee80211_meshcntl *) (mtod(m, const uint8_t *) + hdrspace); - hdrspace += sizeof(struct ieee80211_meshcntl) + + meshdrlen = sizeof(struct ieee80211_meshcntl) + (mc->mc_flags & 3) * IEEE80211_ADDR_LEN; + hdrspace += meshdrlen; seq = LE_READ_4(mc->mc_seq); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) addr = wh->i_addr3; @@ -935,7 +1179,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) * for the rules. XXX tap fwd'd packets not for us? */ if (dir == IEEE80211_FC1_DIR_FROMDS || - !IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_myaddr)) { + !mesh_isucastforme(vap, wh, mc)) { mesh_forward(vap, m, mc); if (dir == IEEE80211_FC1_DIR_DSTODS) goto out; @@ -970,7 +1214,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) /* * Finally, strip the 802.11 header. */ - m = ieee80211_decap(vap, m, hdrspace); + m = mesh_decap(vap, m, hdrspace, meshdrlen); if (m == NULL) { /* XXX mask bit to check for both */ /* don't count Null data frames as errors */ @@ -987,13 +1231,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) m = ieee80211_decap_amsdu(ni, m); if (m == NULL) return IEEE80211_FC0_TYPE_DATA; - } else { -#ifdef IEEE80211_SUPPORT_SUPERG - m = ieee80211_decap_fastframe(vap, ni, m); - if (m == NULL) - return IEEE80211_FC0_TYPE_DATA; -#endif - } + } ieee80211_deliver_data(vap, ni, m); return type; case IEEE80211_FC0_TYPE_MGT: @@ -1967,7 +2205,7 @@ mesh_peer_timeout_backoff(struct ieee80211_node *ni) static __inline void mesh_peer_timeout_stop(struct ieee80211_node *ni) { - callout_stop(&ni->ni_mltimer); + callout_drain(&ni->ni_mltimer); } /* @@ -2401,6 +2639,7 @@ mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) return ENOMEM; } ireq->i_len = len; + /* XXX M_WAIT? */ p = malloc(len, M_TEMP, M_NOWAIT | M_ZERO); if (p == NULL) return ENOMEM; @@ -2418,6 +2657,7 @@ mesh_ioctl_get80211(struct ieee80211vap *vap, struct ieee80211req *ireq) imr->imr_metric = rt->rt_metric; imr->imr_nhops = rt->rt_nhops; imr->imr_lifetime = rt->rt_lifetime; + imr->imr_lastmseq = rt->rt_lastmseq; off += sizeof(*imr); } MESH_RT_UNLOCK(ms); @@ -2475,17 +2715,19 @@ mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) if (ireq->i_val != 0 || ireq->i_len > IEEE80211_MESHID_LEN) return EINVAL; error = copyin(ireq->i_data, tmpmeshid, ireq->i_len); - if (error) + if (error != 0) break; memset(ms->ms_id, 0, IEEE80211_NWID_LEN); ms->ms_idlen = ireq->i_len; memcpy(ms->ms_id, tmpmeshid, ireq->i_len); + error = ENETRESET; break; case IEEE80211_IOC_MESH_AP: if (ireq->i_val) ms->ms_flags |= IEEE80211_MESHFLAGS_AP; else ms->ms_flags &= ~IEEE80211_MESHFLAGS_AP; + error = ENETRESET; break; case IEEE80211_IOC_MESH_FWRD: if (ireq->i_val) @@ -2509,7 +2751,7 @@ mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) return EINVAL; error = copyin(ireq->i_data, &tmpaddr, IEEE80211_ADDR_LEN); - if (!error) + if (error == 0) ieee80211_mesh_discover(vap, tmpaddr, NULL); break; case IEEE80211_MESH_RTCMD_DELETE: @@ -2521,13 +2763,19 @@ mesh_ioctl_set80211(struct ieee80211vap *vap, struct ieee80211req *ireq) break; case IEEE80211_IOC_MESH_PR_METRIC: error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto)); - if (!error) - return mesh_select_proto_metric(vap, tmpproto); + if (error == 0) { + error = mesh_select_proto_metric(vap, tmpproto); + if (error == 0) + error = ENETRESET; + } break; case IEEE80211_IOC_MESH_PR_PATH: error = copyin(ireq->i_data, tmpproto, sizeof(tmpproto)); - if (!error) - return mesh_select_proto_path(vap, tmpproto); + if (error == 0) { + error = mesh_select_proto_path(vap, tmpproto); + if (error == 0) + error = ENETRESET; + } break; default: return ENOSYS; diff --git a/sys/net80211/ieee80211_mesh.h b/sys/net80211/ieee80211_mesh.h index 595eece24906..37004e596797 100644 --- a/sys/net80211/ieee80211_mesh.h +++ b/sys/net80211/ieee80211_mesh.h @@ -359,8 +359,8 @@ struct ieee80211_meshcntl_ae10 { uint8_t mc_flags; /* Address Extension 10 */ uint8_t mc_ttl; /* TTL */ uint8_t mc_seq[4]; /* Sequence No. */ + uint8_t mc_addr4[IEEE80211_ADDR_LEN]; uint8_t mc_addr5[IEEE80211_ADDR_LEN]; - uint8_t mc_addr6[IEEE80211_ADDR_LEN]; } __packed; struct ieee80211_meshcntl_ae11 { @@ -372,22 +372,18 @@ struct ieee80211_meshcntl_ae11 { uint8_t mc_addr6[IEEE80211_ADDR_LEN]; } __packed; -struct ieee80211req_mesh_route { - uint8_t imr_dest[IEEE80211_ADDR_LEN]; - uint8_t imr_nexthop[IEEE80211_ADDR_LEN]; - uint32_t imr_metric; - uint16_t imr_nhops; - uint32_t imr_lifetime; -}; - #ifdef _KERNEL MALLOC_DECLARE(M_80211_MESH_RT); struct ieee80211_mesh_route { TAILQ_ENTRY(ieee80211_mesh_route) rt_next; + struct timeval rt_crtime; /* creation time */ uint8_t rt_dest[IEEE80211_ADDR_LEN]; uint8_t rt_nexthop[IEEE80211_ADDR_LEN]; uint32_t rt_metric; /* path metric */ uint16_t rt_nhops; /* number of hops */ + uint16_t rt_flags; +#define IEEE80211_MESHRT_FLAGS_VALID 0x01 /* patch discovery complete */ +#define IEEE80211_MESHRT_FLAGS_PROXY 0x02 /* proxy entry */ uint32_t rt_lifetime; uint32_t rt_lastmseq; /* last seq# seen dest */ void *rt_priv; /* private data */ @@ -411,8 +407,9 @@ struct ieee80211_mesh_proto_path { void (*mpp_vdetach)(struct ieee80211vap *); int (*mpp_newstate)(struct ieee80211vap *, enum ieee80211_state, int); - size_t mpp_privlen; /* size required in the routing table + const size_t mpp_privlen; /* size required in the routing table for private data */ + const struct timeval mpp_inact; /* inact. timeout for invalid routes */ }; /* @@ -454,6 +451,7 @@ struct ieee80211_mesh_state { #define IEEE80211_MESHFLAGS_FWD 0x04 /* forward packets */ uint8_t ms_flags; struct mtx ms_rt_lock; + struct callout ms_cleantimer; TAILQ_HEAD(, ieee80211_mesh_route) ms_routes; struct ieee80211_mesh_proto_metric *ms_pmetric; struct ieee80211_mesh_proto_path *ms_ppath; @@ -470,6 +468,8 @@ struct ieee80211_mesh_route * void ieee80211_mesh_rt_del(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); void ieee80211_mesh_rt_flush(struct ieee80211vap *); +void ieee80211_mesh_proxy_check(struct ieee80211vap *, + const uint8_t [IEEE80211_ADDR_LEN]); int ieee80211_mesh_register_proto_path(const struct ieee80211_mesh_proto_path *); @@ -493,8 +493,26 @@ void ieee80211_mesh_init_neighbor(struct ieee80211_node *, const struct ieee80211_frame *, const struct ieee80211_scanparams *); +/* + * Return non-zero if proxy operation is enabled. + */ +static __inline int +ieee80211_mesh_isproxyena(struct ieee80211vap *vap) +{ + struct ieee80211_mesh_state *ms = vap->iv_mesh; + return (ms->ms_flags & + (IEEE80211_MESHFLAGS_AP | IEEE80211_MESHFLAGS_PORTAL)) != 0; +} + +/* + * Process an outbound frame: if a path is known to the + * destination then return a reference to the next hop + * for immediate transmission. Otherwise initiate path + * discovery and, if possible queue the packet to be + * sent when path discovery completes. + */ static __inline struct ieee80211_node * -ieee80211_mesh_discover(struct ieee80211vap *vap, +ieee80211_mesh_discover(struct ieee80211vap *vap, const uint8_t dest[IEEE80211_ADDR_LEN], struct mbuf *m) { struct ieee80211_mesh_state *ms = vap->iv_mesh; diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 96b2d45e2589..061a63d01f9f 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -242,19 +242,34 @@ ieee80211_start(struct ifnet *ifp) } #ifdef IEEE80211_SUPPORT_MESH } else { + if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { + /* + * Proxy station only if configured. + */ + if (!ieee80211_mesh_isproxyena(vap)) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_OUTPUT | + IEEE80211_MSG_MESH, + eh->ether_dhost, NULL, + "%s", "proxy not enabled"); + vap->iv_stats.is_mesh_notproxy++; + ifp->if_oerrors++; + m_freem(m); + continue; + } + ieee80211_mesh_proxy_check(vap, eh->ether_shost); + } ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); if (ni == NULL) { /* - * NB: ieee80211_mesh_discover function - * holds/disposes frame - * (e.g. queueing on path discovery). + * NB: ieee80211_mesh_discover holds/disposes + * frame (e.g. queueing on path discovery). */ ifp->if_oerrors++; continue; } } #endif - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && (m->m_flags & M_PWR_SAV) == 0) { /* @@ -987,7 +1002,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, struct ieee80211com *ic = ni->ni_ic; #ifdef IEEE80211_SUPPORT_MESH struct ieee80211_mesh_state *ms = vap->iv_mesh; - struct ieee80211_meshcntl_ae11 *mc; + struct ieee80211_meshcntl_ae10 *mc; #endif struct ether_header eh; struct ieee80211_frame *wh; @@ -1066,21 +1081,21 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, * w/ 4-address format and address extension mode 10 */ is4addr = 0; /* NB: don't use, disable */ + if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) + hdrsize += IEEE80211_ADDR_LEN; /* unicast are 4-addr */ meshhdrsize = sizeof(struct ieee80211_meshcntl); /* XXX defines for AE modes */ - /* XXX not right, need to check if from non-mesh-sta */ if (IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { - if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) { - hdrsize += IEEE80211_ADDR_LEN; + if (!IEEE80211_IS_MULTICAST(eh.ether_dhost)) meshae = 0; - } else + else meshae = 4; /* NB: pseudo */ } else if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) { meshae = 1; - meshhdrsize += 2*IEEE80211_ADDR_LEN; + meshhdrsize += 1*IEEE80211_ADDR_LEN; } else { meshae = 2; - meshhdrsize += 3*IEEE80211_ADDR_LEN; + meshhdrsize += 2*IEEE80211_ADDR_LEN; } } else { #endif @@ -1179,7 +1194,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, #ifdef IEEE80211_SUPPORT_MESH case IEEE80211_M_MBSS: /* NB: offset by hdrspace to deal with DATAPAD */ - mc = (struct ieee80211_meshcntl_ae11 *) + mc = (struct ieee80211_meshcntl_ae10 *) (mtod(m, uint8_t *) + hdrspace); switch (meshae) { case 0: /* ucast, no proxy */ @@ -1203,8 +1218,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); - /* XXX not right, need MeshSA */ - IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); + IEEE80211_ADDR_COPY(wh->i_addr3, vap->iv_myaddr); mc->mc_flags = 1; IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_shost); qos = ((struct ieee80211_qosframe *) wh)->i_qos; @@ -1213,12 +1227,13 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); - /* XXX not right, need MeshDA+MeshSA */ + /* XXX not right, need MeshDA */ IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); - IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); + /* XXX assume are MeshSA */ + IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, vap->iv_myaddr); mc->mc_flags = 2; + IEEE80211_ADDR_COPY(mc->mc_addr4, eh.ether_dhost); IEEE80211_ADDR_COPY(mc->mc_addr5, eh.ether_shost); - IEEE80211_ADDR_COPY(mc->mc_addr6, eh.ether_shost); qos = ((struct ieee80211_qosframe_addr4 *) wh)->i_qos; break; default: