/**
|
* @file
|
* @brief
|
* Wireless EThernet (WET) Bridge.
|
*
|
* WET STA and WET client are inter-exchangable in this file and refer to
|
* addressable entities whose traffic are sent and received through this
|
* bridge, including the hosting device.
|
*
|
* Supported protocol families: IP v4.
|
*
|
* Tx: replace frames' source MAC address with wireless interface's;
|
* update the IP-MAC address mapping table entry.
|
*
|
* Rx: replace frames' the destination MAC address with what found in
|
* the IP-MAC address mapping table.
|
*
|
* All data structures defined in this file are optimized for IP v4. To
|
* support other protocol families, write protocol specific handlers.
|
* Doing so may require data structures changes to expand various address
|
* storages to fit the protocol specific needs, for example, IPX needs 10
|
* octets for its network address. Also one may need to define the data
|
* structures in a more generic way so that they work with all supported
|
* protocol families, for example, the wet_sta strcture may be defined
|
* as follow:
|
*
|
* struct wet_sta {
|
* uint8 nal; network address length
|
* uint8 na[NETA_MAX_LEN]; network address
|
* uint8 mac[ETHER_ADDR_LEN];
|
* ...
|
* };
|
*
|
* Copyright (C) 2020, Broadcom.
|
*
|
* Unless you and Broadcom execute a separate written software license
|
* agreement governing use of this software, this software is licensed to you
|
* under the terms of the GNU General Public License version 2 (the "GPL"),
|
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
|
* following added to such license:
|
*
|
* As a special exception, the copyright holders of this software give you
|
* permission to link this software with independent modules, and to copy and
|
* distribute the resulting executable under terms of your choice, provided that
|
* you also meet, for each linked independent module, the terms and conditions of
|
* the license of that module. An independent module is a module which is not
|
* derived from this software. The special exception does not apply to any
|
* modifications of the software.
|
*
|
*
|
* <<Broadcom-WL-IPTag/Open:>>
|
*
|
* $Id$
|
*/
|
|
/**
|
* @file
|
* @brief
|
* XXX Twiki: [WirelessEthernet]
|
*/
|
#include <typedefs.h>
|
#include <bcmdefs.h>
|
#include <osl.h>
|
#include <bcmutils.h>
|
#include <siutils.h>
|
#include <wlioctl.h>
|
#include <802.11.h>
|
#include <ethernet.h>
|
#include <vlan.h>
|
#include <802.3.h>
|
#include <bcmip.h>
|
#include <bcmarp.h>
|
#include <bcmudp.h>
|
#include <bcmdhcp.h>
|
#include <bcmendian.h>
|
#include <dhd_dbg.h>
|
#include <d11.h>
|
|
#include <dhd_wet.h>
|
|
/* IP/MAC address mapping entry */
|
typedef struct wet_sta wet_sta_t;
|
struct wet_sta {
|
/* client */
|
uint8 ip[IPV4_ADDR_LEN]; /* client IP addr */
|
struct ether_addr mac; /* client MAC addr */
|
uint8 flags[DHCP_FLAGS_LEN]; /* orig. dhcp flags */
|
/* internal */
|
wet_sta_t *next; /* free STA link */
|
wet_sta_t *next_ip; /* hash link by IP */
|
wet_sta_t *next_mac; /* hash link by MAC */
|
};
|
#define WET_NUMSTAS (1 << 8) /* max. # clients, must be multiple of 2 */
|
#define WET_STA_HASH_SIZE (WET_NUMSTAS/2) /* must be <= WET_NUMSTAS */
|
#define WET_STA_HASH_IP(ip) ((ip)[3]&(WET_STA_HASH_SIZE-1)) /* hash by IP */
|
#define WET_STA_HASH_MAC(ea) (((ea)[3]^(ea)[4]^(ea)[5])&(WET_STA_HASH_SIZE-1)) /* hash by MAC */
|
#define WET_STA_HASH_UNK -1 /* Unknown hash */
|
#define IP_ISMULTI(ip) (((ip) & 0xf0000000) == 0xe0000000) /* Check for multicast by IP */
|
|
/* WET private info structure */
|
struct dhd_wet_info {
|
/* pointer to dhdpublic info struct */
|
dhd_pub_t *pub;
|
/* Host addresses */
|
uint8 ip[IPV4_ADDR_LEN];
|
struct ether_addr mac;
|
/* STA storage, one entry per eth. client */
|
wet_sta_t sta[WET_NUMSTAS];
|
/* Free sta list */
|
wet_sta_t *stafree;
|
/* Used sta hash by IP */
|
wet_sta_t *stahash_ip[WET_STA_HASH_SIZE];
|
/* Used sta hash by MAC */
|
wet_sta_t *stahash_mac[WET_STA_HASH_SIZE];
|
};
|
|
/* forward declarations */
|
static int wet_eth_proc(dhd_wet_info_t *weth, void *sdu,
|
uint8 *frame, int length, int send);
|
static int wet_vtag_proc(dhd_wet_info_t *weth, void *sdu,
|
uint8 * eh, uint8 *vtag, int length, int send);
|
static int wet_ip_proc(dhd_wet_info_t *weth, void *sdu,
|
uint8 * eh, uint8 *iph, int length, int send);
|
static int wet_arp_proc(dhd_wet_info_t *weth, void *sdu,
|
uint8 *eh, uint8 *arph, int length, int send);
|
static int wet_udp_proc(dhd_wet_info_t *weth,
|
uint8 *eh, uint8 *iph, uint8 *udph, int length, int send);
|
static int wet_dhcpc_proc(dhd_wet_info_t *weth,
|
uint8 *eh, uint8 *iph, uint8 *udph, uint8 *dhcp, int length, int send);
|
static int wet_dhcps_proc(dhd_wet_info_t *weth,
|
uint8 *eh, uint8 *iph, uint8 *udph, uint8 *dhcp, int length, int send);
|
static int wet_sta_alloc(dhd_wet_info_t *weth, wet_sta_t **saddr);
|
static int wet_sta_update_all(dhd_wet_info_t *weth,
|
uint8 *iaddr, struct ether_addr *eaddr, wet_sta_t **saddr);
|
static int wet_sta_update_mac(dhd_wet_info_t *weth,
|
struct ether_addr *eaddr, wet_sta_t **saddr);
|
static int wet_sta_remove_mac_entry(dhd_wet_info_t *weth, struct ether_addr *eaddr);
|
static int wet_sta_find_ip(dhd_wet_info_t *weth,
|
uint8 *iaddr, wet_sta_t **saddr);
|
static int wet_sta_find_mac(dhd_wet_info_t *weth,
|
struct ether_addr *eaddr, wet_sta_t **saddr);
|
static void csum_fixup_16(uint8 *chksum,
|
uint8 *optr, int olen, uint8 *nptr, int nlen);
|
|
/*
|
* Protocol handler. 'ph' points to protocol specific header,
|
* for example, it points to IP header if it is IP packet.
|
*/
|
typedef int (*prot_proc_t)(dhd_wet_info_t *weth, void *sdu, uint8 *eh,
|
uint8 *ph, int length, int send);
|
/* Protocol handlers hash table - hash by ether type */
|
typedef struct prot_hdlr prot_hdlr_t;
|
struct prot_hdlr {
|
uint16 type; /* ether type */
|
prot_proc_t prot_proc;
|
prot_hdlr_t *next; /* next proto handler that has the same hash */
|
};
|
#define WET_PROT_HASH_SIZE (1 << 3)
|
#define WET_PROT_HASH(t) ((t)[1]&(WET_PROT_HASH_SIZE-1))
|
static prot_hdlr_t ept_tbl[] = {
|
/* 0 */ {HTON16(ETHER_TYPE_8021Q), wet_vtag_proc, NULL}, /* 0x8100 */
|
};
|
static prot_hdlr_t prot_hash[WET_PROT_HASH_SIZE] = {
|
/* 0 */ {HTON16(ETHER_TYPE_IP), wet_ip_proc, &ept_tbl[0]}, /* 0x0800 */
|
/* 1 */ {0, NULL, NULL}, /* unused */
|
/* 2 */ {0, NULL, NULL}, /* unused */
|
/* 3 */ {0, NULL, NULL}, /* unused */
|
/* 4 */ {0, NULL, NULL}, /* unused */
|
/* 5 */ {0, NULL, NULL}, /* unused */
|
/* 6 */ {HTON16(ETHER_TYPE_ARP), wet_arp_proc, NULL}, /* 0x0806 */
|
/* 7 */ {0, NULL, NULL}, /* unused */
|
};
|
|
/*
|
* IPv4 handler. 'ph' points to protocol specific header,
|
* for example, it points to UDP header if it is UDP packet.
|
*/
|
typedef int (*ipv4_proc_t)(dhd_wet_info_t *weth, uint8 *eh,
|
uint8 *iph, uint8 *ph, int length, int send);
|
/* IPv4 handlers hash table - hash by protocol type */
|
typedef struct ipv4_hdlr ipv4_hdlr_t;
|
struct ipv4_hdlr {
|
uint8 type; /* protocol type */
|
ipv4_proc_t ipv4_proc;
|
ipv4_hdlr_t *next; /* next proto handler that has the same hash */
|
};
|
#define WET_IPV4_HASH_SIZE (1 << 1)
|
#define WET_IPV4_HASH(p) ((p)&(WET_IPV4_HASH_SIZE-1))
|
static ipv4_hdlr_t ipv4_hash[WET_IPV4_HASH_SIZE] = {
|
/* 0 */ {0, NULL, NULL}, /* unused */
|
/* 1 */ {IP_PROT_UDP, wet_udp_proc, NULL}, /* 0x11 */
|
};
|
|
/*
|
* UDP handler. 'ph' points to protocol specific header,
|
* for example, it points to DHCP header if it is DHCP packet.
|
*/
|
typedef int (*udp_proc_t)(dhd_wet_info_t *weth, uint8 *eh,
|
uint8 *iph, uint8 *udph, uint8 *ph, int length, int send);
|
/* UDP handlers hash table - hash by port number */
|
typedef struct udp_hdlr udp_hdlr_t;
|
struct udp_hdlr {
|
uint16 port; /* udp dest. port */
|
udp_proc_t udp_proc;
|
udp_hdlr_t *next; /* next proto handler that has the same hash */
|
};
|
#define WET_UDP_HASH_SIZE (1 << 3)
|
#define WET_UDP_HASH(p) ((p)[1]&(WET_UDP_HASH_SIZE-1))
|
static udp_hdlr_t udp_hash[WET_UDP_HASH_SIZE] = {
|
/* 0 */ {0, NULL, NULL}, /* unused */
|
/* 1 */ {0, NULL, NULL}, /* unused */
|
/* 2 */ {0, NULL, NULL}, /* unused */
|
/* 3 */ {HTON16(DHCP_PORT_SERVER), wet_dhcpc_proc, NULL}, /* 0x43 */
|
/* 4 */ {HTON16(DHCP_PORT_CLIENT), wet_dhcps_proc, NULL}, /* 0x44 */
|
/* 5 */ {0, NULL, NULL}, /* unused */
|
/* 6 */ {0, NULL, NULL}, /* unused */
|
/* 7 */ {0, NULL, NULL}, /* unused */
|
};
|
|
#define WETHWADDR(weth) ((weth)->pub->mac.octet)
|
#define WETOSH(weth) ((weth)->pub->osh)
|
|
/* special values */
|
/* 802.3 llc/snap header */
|
static uint8 llc_snap_hdr[SNAP_HDR_LEN] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
|
static uint8 ipv4_bcast[IPV4_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff}; /* IP v4 broadcast address */
|
static uint8 ipv4_null[IPV4_ADDR_LEN] = {0x00, 0x00, 0x00, 0x00}; /* IP v4 NULL address */
|
|
dhd_wet_info_t *
|
dhd_get_wet_info(dhd_pub_t *pub)
|
{
|
dhd_wet_info_t *p;
|
int i;
|
p = (dhd_wet_info_t *)MALLOCZ(pub->osh, sizeof(dhd_wet_info_t));
|
if (p == NULL) {
|
return 0;
|
}
|
for (i = 0; i < WET_NUMSTAS - 1; i ++)
|
p->sta[i].next = &p->sta[i + 1];
|
p->stafree = &p->sta[0];
|
p->pub = pub;
|
return p;
|
}
|
|
void
|
dhd_free_wet_info(dhd_pub_t *pub, void *wet)
|
{
|
if (wet) {
|
MFREE(pub->osh, wet, sizeof(dhd_wet_info_t));
|
}
|
}
|
|
void dhd_set_wet_host_ipv4(dhd_pub_t *pub, void *parms, uint32 len)
|
{
|
dhd_wet_info_t *p;
|
p = (dhd_wet_info_t *)pub->wet_info;
|
bcopy(parms, p->ip, len);
|
}
|
|
void dhd_set_wet_host_mac(dhd_pub_t *pub, void *parms, uint32 len)
|
{
|
dhd_wet_info_t *p;
|
p = (dhd_wet_info_t *)pub->wet_info;
|
bcopy(parms, &p->mac, len);
|
}
|
/* process Ethernet frame */
|
/*
|
* Return:
|
* = 0 if frame is done ok
|
* < 0 if unable to handle the frame
|
* > 0 if no further process
|
*/
|
static int
|
BCMFASTPATH(wet_eth_proc)(dhd_wet_info_t *weth, void *sdu, uint8 *frame, int length, int send)
|
{
|
uint8 *pt = frame + ETHER_TYPE_OFFSET;
|
uint16 type;
|
uint8 *ph;
|
prot_hdlr_t *phdlr;
|
/* intercept Ethernet II frame (type > 1500) */
|
if (length >= ETHER_HDR_LEN && (pt[0] > (ETHER_MAX_DATA >> 8) ||
|
(pt[0] == (ETHER_MAX_DATA >> 8) && pt[1] > (ETHER_MAX_DATA & 0xff))))
|
;
|
/* intercept 802.3 LLC/SNAP frame (type <= 1500) */
|
else if (length >= ETHER_HDR_LEN + SNAP_HDR_LEN + ETHER_TYPE_LEN) {
|
uint8 *llc = frame + ETHER_HDR_LEN;
|
if (bcmp(llc_snap_hdr, llc, SNAP_HDR_LEN))
|
return 0;
|
pt = llc + SNAP_HDR_LEN;
|
}
|
/* frame too short bail out */
|
else {
|
DHD_ERROR(("wet_eth_proc: %s short eth frame, ignored\n",
|
send ? "send" : "recv"));
|
return -1;
|
}
|
ph = pt + ETHER_TYPE_LEN;
|
length -= ph - frame;
|
|
/* Call protocol specific handler to process frame. */
|
type = *(uint16 *)pt;
|
|
for (phdlr = &prot_hash[WET_PROT_HASH(pt)];
|
phdlr != NULL; phdlr = phdlr->next) {
|
if (phdlr->type != type || !phdlr->prot_proc)
|
continue;
|
return (phdlr->prot_proc)(weth, sdu, frame, ph, length, send);
|
}
|
|
if (!bcmp(WETHWADDR(weth), frame + ETHER_SRC_OFFSET, ETHER_ADDR_LEN)) {
|
return 0;
|
}
|
else {
|
DHD_INFO(("%s: %s unknown type (0x%X), ignored %s\n",
|
__FUNCTION__, send ? "send" : "recv", type,
|
(type == 0xDD86) ? "IPv6":""));
|
/* ignore unsupported protocol from different mac addr than us */
|
return BCME_UNSUPPORTED;
|
}
|
}
|
|
/* process 8021p/Q tagged frame */
|
/*
|
* Return:
|
* = 0 if frame is done ok
|
* < 0 if unable to handle the frame
|
* > 0 if no further process
|
*/
|
static int
|
BCMFASTPATH(wet_vtag_proc)(dhd_wet_info_t *weth, void *sdu,
|
uint8 * eh, uint8 *vtag, int length, int send)
|
{
|
uint16 type;
|
uint8 *pt;
|
prot_hdlr_t *phdlr;
|
|
/* check minimum length */
|
if (length < ETHERVLAN_HDR_LEN) {
|
DHD_ERROR(("wet_vtag_proc: %s short VLAN frame, ignored\n",
|
send ? "send" : "recv"));
|
return -1;
|
}
|
|
/*
|
* FIXME: check recursiveness to prevent stack from overflow
|
* in case someone sent frames 8100xxxxxxxx8100xxxxxxxx...
|
*/
|
|
/* Call protocol specific handler to process frame. */
|
type = *(uint16 *)(pt = vtag + VLAN_TAG_LEN);
|
|
for (phdlr = &prot_hash[WET_PROT_HASH(pt)];
|
phdlr != NULL; phdlr = phdlr->next) {
|
if (phdlr->type != type || !phdlr->prot_proc)
|
continue;
|
return (phdlr->prot_proc)(weth, sdu, eh,
|
pt + ETHER_TYPE_LEN, length, send);
|
}
|
|
return 0;
|
}
|
|
/* process IP frame */
|
/*
|
* Return:
|
* = 0 if frame is done ok
|
* < 0 if unable to handle the frame
|
* > 0 if no further process
|
*/
|
static int
|
BCMFASTPATH(wet_ip_proc)(dhd_wet_info_t *weth, void *sdu,
|
uint8 *eh, uint8 *iph, int length, int send)
|
{
|
uint8 type;
|
int ihl;
|
wet_sta_t *sta;
|
ipv4_hdlr_t *iphdlr;
|
uint8 *iaddr;
|
struct ether_addr *ea = NULL;
|
int ret, ea_off = 0;
|
char eabuf[ETHER_ADDR_STR_LEN];
|
BCM_REFERENCE(eabuf);
|
|
/* IPv4 only */
|
if (length < 1 || (IP_VER(iph) != IP_VER_4)) {
|
DHD_INFO(("wet_ip_proc: %s non IPv4 frame, ignored\n",
|
send ? "send" : "recv"));
|
return -1;
|
}
|
|
ihl = IPV4_HLEN(iph);
|
|
/* minimum length */
|
if (length < ihl) {
|
DHD_ERROR(("wet_ip_proc: %s short IPv4 frame, ignored\n",
|
send ? "send" : "recv"));
|
return -1;
|
}
|
|
/* protocol specific handling */
|
type = IPV4_PROT(iph);
|
for (iphdlr = &ipv4_hash[WET_IPV4_HASH(type)];
|
iphdlr; iphdlr = iphdlr->next) {
|
if (iphdlr->type != type || !iphdlr->ipv4_proc)
|
continue;
|
if ((ret = (iphdlr->ipv4_proc)(weth, eh,
|
iph, iph + ihl, length - ihl, send)))
|
return ret;
|
}
|
|
/* generic IP packet handling
|
* Replace source MAC in Ethernet header with wireless's and
|
* keep track of IP MAC mapping when sending frame.
|
*/
|
if (send) {
|
uint32 iaddr_dest, iaddr_src;
|
bool wet_table_upd = TRUE;
|
iaddr = iph + IPV4_SRC_IP_OFFSET;
|
iaddr_dest = ntoh32(*((uint32 *)(iph + IPV4_DEST_IP_OFFSET)));
|
iaddr_src = ntoh32(*(uint32 *)(iaddr));
|
|
/* Do not process and update knowledge base on receipt of a local IP
|
* multicast frame
|
*/
|
if (IP_ISMULTI(iaddr_dest) && !iaddr_src) {
|
DHD_INFO(("recv multicast frame from %s.Don't update hash table\n",
|
bcm_ether_ntoa((struct ether_addr*)
|
(eh + ETHER_SRC_OFFSET), eabuf)));
|
wet_table_upd = FALSE;
|
}
|
if (wet_table_upd && wet_sta_update_all(weth, iaddr,
|
(struct ether_addr*)(eh + ETHER_SRC_OFFSET), &sta) < 0) {
|
DHD_INFO(("wet_ip_proc: unable to update STA %u.%u.%u.%u %s\n",
|
iaddr[0], iaddr[1], iaddr[2], iaddr[3],
|
bcm_ether_ntoa(
|
(struct ether_addr*)(eh + ETHER_SRC_OFFSET), eabuf)));
|
return -1;
|
}
|
ea = (struct ether_addr *)WETHWADDR(weth);
|
ea_off = ETHER_SRC_OFFSET;
|
eacopy(ea, eh + ea_off);
|
}
|
/*
|
* Replace dest MAC in Ethernet header using the found one
|
* when receiving frame.
|
*/
|
/* no action for received bcast/mcast ethernet frame */
|
else if (!ETHER_ISMULTI(eh)) {
|
iaddr = iph + IPV4_DEST_IP_OFFSET;
|
if (wet_sta_find_ip(weth, iaddr, &sta) < 0) {
|
DHD_ERROR(("wet_ip_proc: unable to find STA %u.%u.%u.%u\n",
|
iaddr[0], iaddr[1], iaddr[2], iaddr[3]));
|
return -1;
|
}
|
ea = &sta->mac;
|
ea_off = ETHER_DEST_OFFSET;
|
eacopy(ea, eh + ea_off);
|
}
|
|
return 0;
|
}
|
|
/* process ARP frame - ARP proxy */
|
/*
|
* Return:
|
* = 0 if frame is done ok
|
* < 0 if unable to handle the frame
|
* > 0 if no further process
|
*/
|
static int
|
BCMFASTPATH(wet_arp_proc)(dhd_wet_info_t *weth, void *sdu,
|
uint8 *eh, uint8 *arph, int length, int send)
|
{
|
wet_sta_t *sta;
|
uint8 *iaddr;
|
char eabuf[ETHER_ADDR_STR_LEN];
|
BCM_REFERENCE(eabuf);
|
|
/*
|
* FIXME: validate ARP header:
|
* h/w Ethernet 2, proto IP x800, h/w addr size 6, proto addr size 4.
|
*/
|
|
/*
|
* Replace source MAC in Ethernet header as well as source MAC in
|
* ARP protocol header when processing frame sent.
|
*/
|
if (send) {
|
iaddr = arph + ARP_SRC_IP_OFFSET;
|
if (wet_sta_update_all(weth, iaddr,
|
(struct ether_addr*)(eh + ETHER_SRC_OFFSET), &sta) < 0) {
|
DHD_INFO(("wet_arp_proc: unable to update STA %u.%u.%u.%u %s\n",
|
iaddr[0], iaddr[1], iaddr[2], iaddr[3],
|
bcm_ether_ntoa(
|
(struct ether_addr*)(eh + ETHER_SRC_OFFSET), eabuf)));
|
return -1;
|
}
|
bcopy(WETHWADDR(weth), eh + ETHER_SRC_OFFSET, ETHER_ADDR_LEN);
|
bcopy(WETHWADDR(weth), arph+ARP_SRC_ETH_OFFSET, ETHER_ADDR_LEN);
|
}
|
/*
|
* Replace dest MAC in Ethernet header as well as dest MAC in
|
* ARP protocol header when processing frame recv'd. Process ARP
|
* replies and Unicast ARP requests.
|
*/
|
else if ((*(uint16 *)(arph + ARP_OPC_OFFSET) == HTON16(ARP_OPC_REPLY)) ||
|
((*(uint16 *)(arph + ARP_OPC_OFFSET) == HTON16(ARP_OPC_REQUEST)) &&
|
(!ETHER_ISMULTI(eh)))) {
|
iaddr = arph + ARP_TGT_IP_OFFSET;
|
if (wet_sta_find_ip(weth, iaddr, &sta) < 0) {
|
DHD_INFO(("wet_arp_proc: unable to find STA %u.%u.%u.%u\n",
|
iaddr[0], iaddr[1], iaddr[2], iaddr[3]));
|
return -1;
|
}
|
bcopy(&sta->mac, arph + ARP_TGT_ETH_OFFSET, ETHER_ADDR_LEN);
|
bcopy(&sta->mac, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
|
}
|
|
return 0;
|
}
|
|
/* process UDP frame */
|
/*
|
* Return:
|
* = 0 if frame is done ok
|
* < 0 if unable to handle the frame
|
* > 0 if no further process
|
*/
|
static int
|
BCMFASTPATH(wet_udp_proc)(dhd_wet_info_t *weth,
|
uint8 *eh, uint8 *iph, uint8 *udph, int length, int send)
|
{
|
udp_hdlr_t *udphdlr;
|
uint16 port;
|
|
/* check frame length, at least UDP_HDR_LEN */
|
if ((length -= UDP_HDR_LEN) < 0) {
|
DHD_ERROR(("wet_udp_proc: %s short UDP frame, ignored\n",
|
send ? "send" : "recv"));
|
return -1;
|
}
|
|
/*
|
* Unfortunately we must spend some time here to deal with
|
* some higher layer protocol special processings.
|
* See individual handlers for protocol specific details.
|
*/
|
port = *(uint16 *)(udph + UDP_DEST_PORT_OFFSET);
|
for (udphdlr = &udp_hash[WET_UDP_HASH((uint8 *)&port)];
|
udphdlr; udphdlr = udphdlr->next) {
|
if (udphdlr->port != port || !udphdlr->udp_proc)
|
continue;
|
return (udphdlr->udp_proc)(weth, eh, iph, udph,
|
udph + UDP_HDR_LEN, length, send);
|
}
|
|
return 0;
|
}
|
|
/*
|
* DHCP is a 'complex' protocol for WET, mainly because it
|
* uses its protocol body to convey IP/MAC info. It is impossible
|
* to forward frames correctly back and forth without looking
|
* into the DHCP's body and interpreting it. See RFC2131 sect.
|
* 4.1 'Constructing and sending DHCP messages' for details
|
* of using/parsing various fields in the body.
|
*
|
* DHCP pass through:
|
*
|
* Must alter DHCP flag to broadcast so that the server
|
* can reply with the broadcast address before we can
|
* provide DHCP relay functionality. Otherwise the DHCP
|
* server will send DHCP replies using the DHCP client's
|
* MAC address. Such replies will not be delivered simply
|
* because:
|
*
|
* 1. The AP's bridge will not forward the replies back to
|
* this device through the wireless link because it does not
|
* know such node exists on this link. The bridge's forwarding
|
* table on the AP will have this device's MAC address only.
|
* It does not know anything else behind this device.
|
*
|
* 2. The AP's wireless driver won't allow such frames out
|
* either even if they made their way out the AP's bridge
|
* through the bridge's DLF broadcasting because there is
|
* no such STA associated with the AP.
|
*
|
* 3. This device's MAC won't allow such frames pass
|
* through in non-promiscuous mode even when they made
|
* their way out of the AP's wireless interface somehow.
|
*
|
* DHCP relay:
|
*
|
* Once the WET is configured with the host MAC address it can
|
* relay the host request as if it were sent from WET itself.
|
*
|
* Once the WET is configured with the host IP address it can
|
* pretend to be the host and act as a relay agent.
|
*
|
* process DHCP client frame (client to server, or server to relay agent)
|
* Return:
|
* = 0 if frame is done ok
|
* < 0 if unable to handle the frame
|
* > 0 if no further process
|
*/
|
static int
|
BCMFASTPATH(wet_dhcpc_proc)(dhd_wet_info_t *weth,
|
uint8 *eh, uint8 *iph, uint8 *udph, uint8 *dhcp, int length, int send)
|
{
|
wet_sta_t *sta;
|
uint16 flags;
|
char eabuf[ETHER_ADDR_STR_LEN];
|
uint16 port;
|
uint8 *ipv4;
|
const struct ether_addr *ether;
|
BCM_REFERENCE(eabuf);
|
|
/*
|
* FIXME: validate DHCP body:
|
* htype Ethernet 1, hlen Ethernet 6, frame length at least 242.
|
*/
|
|
/* only interested in requests when sending to server */
|
if (send && *(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REQUEST)
|
return 0;
|
/* only interested in replies when receiving from server as a relay agent */
|
if (!send && *(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REPLY)
|
return 0;
|
|
/* send request */
|
if (send) {
|
/* find existing or alloc new IP/MAC mapping entry */
|
if (wet_sta_update_mac(weth,
|
(struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), &sta) < 0) {
|
DHD_INFO(("wet_dhcpc_proc: unable to update STA %s\n",
|
bcm_ether_ntoa(
|
(struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), eabuf)));
|
return -1;
|
}
|
bcopy(dhcp + DHCP_FLAGS_OFFSET, &flags, DHCP_FLAGS_LEN);
|
/* We can always relay the host's request when we know its MAC addr. */
|
if (!ETHER_ISNULLADDR(weth->mac.octet) &&
|
!bcmp(dhcp + DHCP_CHADDR_OFFSET, &weth->mac, ETHER_ADDR_LEN)) {
|
/* replace chaddr with host's MAC */
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
dhcp + DHCP_CHADDR_OFFSET, ETHER_ADDR_LEN,
|
WETHWADDR(weth), ETHER_ADDR_LEN);
|
bcopy(WETHWADDR(weth), dhcp + DHCP_CHADDR_OFFSET, ETHER_ADDR_LEN);
|
/* force reply to be unicast */
|
flags &= ~HTON16(DHCP_FLAG_BCAST);
|
}
|
/* We can relay other clients' requests when we know the host's IP addr. */
|
else if (!IPV4_ADDR_NULL(weth->ip)) {
|
/* we can only handle the first hop otherwise drop it */
|
if (!IPV4_ADDR_NULL(dhcp + DHCP_GIADDR_OFFSET)) {
|
DHD_INFO(("wet_dhcpc_proc: not first hop, ignored\n"));
|
return -1;
|
}
|
/* replace giaddr with host's IP */
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
dhcp + DHCP_GIADDR_OFFSET, IPV4_ADDR_LEN,
|
weth->ip, IPV4_ADDR_LEN);
|
bcopy(weth->ip, dhcp + DHCP_GIADDR_OFFSET, IPV4_ADDR_LEN);
|
/* force reply to be unicast */
|
flags &= ~HTON16(DHCP_FLAG_BCAST);
|
}
|
/*
|
* Request comes in when we don't know the host's MAC and/or IP
|
* addresses hence we can't relay the request. We must notify the
|
* server of our addressing limitation by turning on the broadcast
|
* bit at this point as what the function comments point out.
|
*/
|
else
|
flags |= HTON16(DHCP_FLAG_BCAST);
|
/* update flags */
|
bcopy(dhcp + DHCP_FLAGS_OFFSET, sta->flags, DHCP_FLAGS_LEN);
|
if (flags != *(uint16 *)sta->flags) {
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN,
|
(uint8 *)&flags, DHCP_FLAGS_LEN);
|
bcopy((uint8 *)&flags, dhcp + DHCP_FLAGS_OFFSET,
|
DHCP_FLAGS_LEN);
|
}
|
/* replace the Ethernet source MAC with ours */
|
bcopy(WETHWADDR(weth), eh + ETHER_SRC_OFFSET, ETHER_ADDR_LEN);
|
}
|
/* relay recv'd reply to its destiny */
|
else if (!IPV4_ADDR_NULL(weth->ip) &&
|
!bcmp(dhcp + DHCP_GIADDR_OFFSET, weth->ip, IPV4_ADDR_LEN)) {
|
/* find IP/MAC mapping entry */
|
if (wet_sta_find_mac(weth,
|
(struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), &sta) < 0) {
|
DHD_INFO(("wet_dhcpc_proc: unable to find STA %s\n",
|
bcm_ether_ntoa(
|
(struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), eabuf)));
|
return -1;
|
}
|
/*
|
* XXX the following code works for the first hop only
|
*/
|
/* restore the DHCP giaddr with its original */
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
dhcp + DHCP_GIADDR_OFFSET, IPV4_ADDR_LEN,
|
ipv4_null, IPV4_ADDR_LEN);
|
bcopy(ipv4_null, dhcp + DHCP_GIADDR_OFFSET, IPV4_ADDR_LEN);
|
/* restore the original client's dhcp flags */
|
if (bcmp(dhcp + DHCP_FLAGS_OFFSET, sta->flags, DHCP_FLAGS_LEN)) {
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN,
|
sta->flags, DHCP_FLAGS_LEN);
|
bcopy(sta->flags, dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN);
|
}
|
/* replace the dest UDP port with DHCP client port */
|
port = HTON16(DHCP_PORT_CLIENT);
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
udph + UDP_DEST_PORT_OFFSET, UDP_PORT_LEN,
|
(uint8 *)&port, UDP_PORT_LEN);
|
bcopy((uint8 *)&port, udph + UDP_DEST_PORT_OFFSET, UDP_PORT_LEN);
|
/* replace the dest MAC & IP addr with the client's */
|
if (*(uint16 *)sta->flags & HTON16(DHCP_FLAG_BCAST)) {
|
ipv4 = ipv4_bcast;
|
ether = ðer_bcast;
|
}
|
else {
|
ipv4 = dhcp + DHCP_YIADDR_OFFSET;
|
ether = &sta->mac;
|
}
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
iph + IPV4_DEST_IP_OFFSET, IPV4_ADDR_LEN,
|
ipv4, IPV4_ADDR_LEN);
|
csum_fixup_16(iph + IPV4_CHKSUM_OFFSET,
|
iph + IPV4_DEST_IP_OFFSET, IPV4_ADDR_LEN,
|
ipv4, IPV4_ADDR_LEN);
|
bcopy(ipv4, iph + IPV4_DEST_IP_OFFSET, IPV4_ADDR_LEN);
|
bcopy(ether, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
|
}
|
/* it should not recv non-relay reply at all, but just in case */
|
else {
|
DHD_INFO(("wet_dhcpc_proc: ignore recv'd frame from %s\n",
|
bcm_ether_ntoa((struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), eabuf)));
|
return -1;
|
}
|
|
/* no further processing! */
|
return 1;
|
}
|
|
/* process DHCP server frame (server to client) */
|
/*
|
* Return:
|
* = 0 if frame is done ok
|
* < 0 if unable to handle the frame
|
* > 0 if no further process
|
*/
|
static int
|
BCMFASTPATH(wet_dhcps_proc)(dhd_wet_info_t *weth,
|
uint8 *eh, uint8 *iph, uint8 *udph, uint8 *dhcp, int length, int send)
|
{
|
wet_sta_t *sta;
|
char eabuf[ETHER_ADDR_STR_LEN];
|
BCM_REFERENCE(eabuf);
|
|
/*
|
* FIXME: validate DHCP body:
|
* htype Ethernet 1, hlen Ethernet 6, frame length at least 242.
|
*/
|
|
/* only interested in replies when receiving from server */
|
if (send || *(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REPLY)
|
return 0;
|
|
/* find IP/MAC mapping entry */
|
if (wet_sta_find_mac(weth, (struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), &sta) < 0) {
|
DHD_INFO(("wet_dhcps_proc: unable to find STA %s\n",
|
bcm_ether_ntoa((struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), eabuf)));
|
return -1;
|
}
|
/* relay the reply to the host when we know the host's MAC addr */
|
if (!ETHER_ISNULLADDR(weth->mac.octet) &&
|
!bcmp(dhcp + DHCP_CHADDR_OFFSET, WETHWADDR(weth), ETHER_ADDR_LEN)) {
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
dhcp + DHCP_CHADDR_OFFSET, ETHER_ADDR_LEN,
|
weth->mac.octet, ETHER_ADDR_LEN);
|
bcopy(&weth->mac, dhcp + DHCP_CHADDR_OFFSET, ETHER_ADDR_LEN);
|
}
|
/* restore the original client's dhcp flags if necessary */
|
if (bcmp(dhcp + DHCP_FLAGS_OFFSET, sta->flags, DHCP_FLAGS_LEN)) {
|
csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
|
dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN,
|
sta->flags, DHCP_FLAGS_LEN);
|
bcopy(sta->flags, dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN);
|
}
|
/* replace the dest MAC with that of client's */
|
if (*(uint16 *)sta->flags & HTON16(DHCP_FLAG_BCAST))
|
bcopy((const uint8 *)ðer_bcast, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
|
else
|
bcopy(&sta->mac, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
|
|
/* no further processing! */
|
return 1;
|
}
|
|
/* alloc IP/MAC mapping entry
|
* Returns 0 if succeeded; < 0 otherwise.
|
*/
|
static int
|
wet_sta_alloc(dhd_wet_info_t *weth, wet_sta_t **saddr)
|
{
|
wet_sta_t *sta;
|
|
/* allocate a new one */
|
if (!weth->stafree) {
|
DHD_INFO(("wet_sta_alloc: no room for another STA\n"));
|
return -1;
|
}
|
sta = weth->stafree;
|
weth->stafree = sta->next;
|
|
/* init them just in case */
|
sta->next = NULL;
|
sta->next_ip = NULL;
|
sta->next_mac = NULL;
|
|
*saddr = sta;
|
return 0;
|
}
|
|
/* update IP/MAC mapping entry and hash
|
* Returns 0 if succeeded; < 0 otherwise.
|
*/
|
static int
|
BCMFASTPATH(wet_sta_update_all)(dhd_wet_info_t *weth, uint8 *iaddr, struct ether_addr *eaddr,
|
wet_sta_t **saddr)
|
{
|
wet_sta_t *sta;
|
int i;
|
char eabuf[ETHER_ADDR_STR_LEN];
|
BCM_REFERENCE(eabuf);
|
|
/* find the existing one and remove it from the old IP hash link */
|
if (!wet_sta_find_mac(weth, eaddr, &sta)) {
|
i = WET_STA_HASH_IP(sta->ip);
|
if (bcmp(sta->ip, iaddr, IPV4_ADDR_LEN)) {
|
wet_sta_t *sta2, **next;
|
for (next = &weth->stahash_ip[i], sta2 = *next;
|
sta2; sta2 = sta2->next_ip) {
|
if (sta2 == sta)
|
break;
|
next = &sta2->next_ip;
|
}
|
if (sta2) {
|
*next = sta2->next_ip;
|
sta2->next_ip = NULL;
|
}
|
i = WET_STA_HASH_UNK;
|
}
|
}
|
/* allocate a new one and hash it by MAC */
|
else if (!wet_sta_alloc(weth, &sta)) {
|
i = WET_STA_HASH_MAC(eaddr->octet);
|
bcopy(eaddr, &sta->mac, ETHER_ADDR_LEN);
|
sta->next_mac = weth->stahash_mac[i];
|
weth->stahash_mac[i] = sta;
|
i = WET_STA_HASH_UNK;
|
}
|
/* bail out if we can't find nor create any */
|
else {
|
DHD_INFO(("wet_sta_update_all: unable to alloc STA %u.%u.%u.%u %s\n",
|
iaddr[0], iaddr[1], iaddr[2], iaddr[3],
|
bcm_ether_ntoa(eaddr, eabuf)));
|
return -1;
|
}
|
|
/* update IP and hash by new IP */
|
if (i == WET_STA_HASH_UNK) {
|
i = WET_STA_HASH_IP(iaddr);
|
bcopy(iaddr, sta->ip, IPV4_ADDR_LEN);
|
sta->next_ip = weth->stahash_ip[i];
|
weth->stahash_ip[i] = sta;
|
|
/* start here and look for other entries with same IP address */
|
{
|
wet_sta_t *sta2, *prev;
|
prev = sta;
|
for (sta2 = sta->next_ip; sta2; sta2 = sta2->next_ip) {
|
/* does this entry have the same IP address? */
|
if (!bcmp(sta->ip, sta2->ip, IPV4_ADDR_LEN)) {
|
/* sta2 currently points to the entry we need to remove */
|
/* fix next pointers */
|
prev->next_ip = sta2->next_ip;
|
sta2->next_ip = NULL;
|
/* now we need to find this guy in the MAC list and
|
remove it from that list too.
|
*/
|
wet_sta_remove_mac_entry(weth, &sta2->mac);
|
/* entry should be completely out of the table now,
|
add it to the free list
|
*/
|
memset(sta2, 0, sizeof(wet_sta_t));
|
sta2->next = weth->stafree;
|
weth->stafree = sta2;
|
|
sta2 = prev;
|
}
|
prev = sta2;
|
}
|
}
|
}
|
|
*saddr = sta;
|
return 0;
|
}
|
|
/* update IP/MAC mapping entry and hash */
|
static int
|
BCMFASTPATH(wet_sta_update_mac)(dhd_wet_info_t *weth, struct ether_addr *eaddr, wet_sta_t **saddr)
|
{
|
wet_sta_t *sta;
|
int i;
|
char eabuf[ETHER_ADDR_STR_LEN];
|
BCM_REFERENCE(eabuf);
|
|
/* find the existing one */
|
if (!wet_sta_find_mac(weth, eaddr, &sta))
|
;
|
/* allocate a new one and hash it */
|
else if (!wet_sta_alloc(weth, &sta)) {
|
i = WET_STA_HASH_MAC(eaddr->octet);
|
bcopy(eaddr, &sta->mac, ETHER_ADDR_LEN);
|
sta->next_mac = weth->stahash_mac[i];
|
weth->stahash_mac[i] = sta;
|
}
|
/* bail out if we can't find nor create any */
|
else {
|
DHD_INFO(("wet_sta_update_mac: unable to alloc STA %s\n",
|
bcm_ether_ntoa(eaddr, eabuf)));
|
return -1;
|
}
|
|
*saddr = sta;
|
return 0;
|
}
|
|
/* Remove MAC entry from hash list
|
* NOTE: This only removes the entry matching "eaddr" from the MAC
|
* list. The caller needs to remove from the IP list and
|
* put back onto the free list to completely remove the entry
|
* from the WET table.
|
*/
|
static int
|
BCMFASTPATH(wet_sta_remove_mac_entry)(dhd_wet_info_t *weth, struct ether_addr *eaddr)
|
{
|
wet_sta_t *sta, *prev;
|
int i = WET_STA_HASH_MAC(eaddr->octet);
|
char eabuf[ETHER_ADDR_STR_LEN];
|
int found = 0;
|
BCM_REFERENCE(eabuf);
|
|
/* find the existing one */
|
for (sta = prev = weth->stahash_mac[i]; sta; sta = sta->next_mac) {
|
if (!bcmp(&sta->mac, eaddr, ETHER_ADDR_LEN)) {
|
found = 1;
|
break;
|
}
|
prev = sta;
|
}
|
|
/* bail out if we can't find */
|
if (!found) {
|
DHD_INFO(("wet_sta_remove_mac_entry: unable to find STA %s entry\n",
|
bcm_ether_ntoa(eaddr, eabuf)));
|
return -1;
|
}
|
|
/* fix the list */
|
if (prev == sta)
|
weth->stahash_mac[i] = sta->next_mac; /* removing first entry in this bucket */
|
else
|
prev->next_mac = sta->next_mac;
|
|
return 0;
|
}
|
|
/* find IP/MAC mapping entry by IP address
|
* Returns 0 if succeeded; < 0 otherwise.
|
*/
|
static int
|
BCMFASTPATH(wet_sta_find_ip)(dhd_wet_info_t *weth, uint8 *iaddr, wet_sta_t **saddr)
|
{
|
int i = WET_STA_HASH_IP(iaddr);
|
wet_sta_t *sta;
|
|
/* find the existing one by IP */
|
for (sta = weth->stahash_ip[i]; sta; sta = sta->next_ip) {
|
if (bcmp(sta->ip, iaddr, IPV4_ADDR_LEN))
|
continue;
|
*saddr = sta;
|
return 0;
|
}
|
|
/* sta has not been learned */
|
DHD_INFO(("wet_sta_find_ip: unable to find STA %u.%u.%u.%u\n",
|
iaddr[0], iaddr[1], iaddr[2], iaddr[3]));
|
return -1;
|
}
|
|
/* find IP/MAC mapping entry by MAC address
|
* Returns 0 if succeeded; < 0 otherwise.
|
*/
|
static int
|
BCMFASTPATH(wet_sta_find_mac)(dhd_wet_info_t *weth, struct ether_addr *eaddr, wet_sta_t **saddr)
|
{
|
int i = WET_STA_HASH_MAC(eaddr->octet);
|
wet_sta_t *sta;
|
char eabuf[ETHER_ADDR_STR_LEN];
|
BCM_REFERENCE(eabuf);
|
|
/* find the existing one by MAC */
|
for (sta = weth->stahash_mac[i]; sta; sta = sta->next_mac) {
|
if (bcmp(&sta->mac, eaddr, ETHER_ADDR_LEN))
|
continue;
|
*saddr = sta;
|
return 0;
|
}
|
|
/* sta has not been learnt */
|
DHD_INFO(("wet_sta_find_mac: unable to find STA %s\n",
|
bcm_ether_ntoa(eaddr, eabuf)));
|
return -1;
|
}
|
|
/* Adjust 16 bit checksum - taken from RFC 3022.
|
*
|
* The algorithm below is applicable only for even offsets (i.e., optr
|
* below must be at an even offset from start of header) and even lengths
|
* (i.e., olen and nlen below must be even).
|
*/
|
static void
|
BCMFASTPATH(csum_fixup_16)(uint8 *chksum, uint8 *optr, int olen, uint8 *nptr, int nlen)
|
{
|
long x, old, new;
|
|
ASSERT(!((uintptr_t)optr&1) && !(olen&1));
|
ASSERT(!((uintptr_t)nptr&1) && !(nlen&1));
|
|
x = (chksum[0]<< 8)+chksum[1];
|
if (!x)
|
return;
|
x = ~x & 0xFFFF;
|
while (olen)
|
{
|
old = (optr[0]<< 8)+optr[1]; optr += 2;
|
x -= old & 0xffff;
|
if (x <= 0) { x--; x &= 0xffff; }
|
olen -= 2;
|
}
|
while (nlen)
|
{
|
new = (nptr[0]<< 8)+nptr[1]; nptr += 2;
|
x += new & 0xffff;
|
if (x & 0x10000) { x++; x &= 0xffff; }
|
nlen -= 2;
|
}
|
x = ~x & 0xFFFF;
|
chksum[0] = (uint8)(x >> 8); chksum[1] = (uint8)x;
|
}
|
|
/* Process frames in transmit direction by replacing source MAC with
|
* wireless's and keep track of IP MAC address mapping table.
|
* Return:
|
* = 0 if frame is done ok;
|
* < 0 if unable to handle the frame;
|
*
|
* To avoid other interfaces to see our changes specially
|
* changes to broadcast frame which definitely will be seen by
|
* other bridged interfaces we must copy the frame to our own
|
* buffer, modify it, and then sent it.
|
* Return the new sdu in 'new'.
|
*/
|
int
|
BCMFASTPATH(dhd_wet_send_proc)(void *wet, void *sdu, void **new)
|
{
|
dhd_wet_info_t *weth = (dhd_wet_info_t *)wet;
|
uint8 *frame = PKTDATA(WETOSH(weth), sdu);
|
int length = PKTLEN(WETOSH(weth), sdu);
|
void *pkt = sdu;
|
|
/*
|
* FIXME: need to tell if buffer is shared and only
|
* do copy on shared buffer.
|
*/
|
/*
|
* copy broadcast/multicast frame to our own packet
|
* otherwise we will screw up others because we alter
|
* the frame content.
|
*/
|
if (length < ETHER_HDR_LEN) {
|
DHD_ERROR(("dhd_wet_send_proc: unable to process short frame\n"));
|
return -1;
|
}
|
if (ETHER_ISMULTI(frame)) {
|
length = pkttotlen(WETOSH(weth), sdu);
|
if (!(pkt = PKTGET(WETOSH(weth), length, TRUE))) {
|
DHD_ERROR(("dhd_wet_send_proc: unable to alloc, dropped\n"));
|
return -1;
|
}
|
frame = PKTDATA(WETOSH(weth), pkt);
|
pktcopy(WETOSH(weth), sdu, 0, length, frame);
|
/* Transfer priority */
|
PKTSETPRIO(pkt, PKTPRIO(sdu));
|
PKTFREE(WETOSH(weth), sdu, TRUE);
|
PKTSETLEN(WETOSH(weth), pkt, length);
|
}
|
*new = pkt;
|
|
/* process frame */
|
return wet_eth_proc(weth, sdu, frame, length, 1) < 0 ? -1 : 0;
|
}
|
|
/*
|
* Process frames in receive direction by replacing destination MAC with
|
* the one found in IP MAC address mapping table.
|
* Return:
|
* = 0 if frame is done ok;
|
* < 0 if unable to handle the frame;
|
*/
|
int
|
BCMFASTPATH(dhd_wet_recv_proc)(void *wet, void *sdu)
|
{
|
dhd_wet_info_t *weth = (dhd_wet_info_t *)wet;
|
/* process frame */
|
return wet_eth_proc(weth, sdu, PKTDATA(WETOSH(weth), sdu),
|
PKTLEN(WETOSH(weth), sdu), 0) < 0 ? -1 : 0;
|
}
|
|
/* Delete WET Database */
|
void
|
dhd_wet_sta_delete_list(dhd_pub_t *dhd_pub)
|
{
|
wet_sta_t *sta;
|
int i, j;
|
dhd_wet_info_t *weth = dhd_pub->wet_info;
|
|
for (i = 0; i < WET_STA_HASH_SIZE; i ++) {
|
for (sta = weth->stahash_mac[i]; sta; sta = sta->next_mac) {
|
wet_sta_t *sta2, **next;
|
j = WET_STA_HASH_IP(sta->ip);
|
for (next = &weth->stahash_ip[j], sta2 = *next;
|
sta2; sta2 = sta2->next_ip) {
|
if (sta2 == sta)
|
break;
|
next = &sta2->next_ip;
|
}
|
if (sta2) {
|
*next = sta2->next_ip;
|
sta2->next_ip = NULL;
|
}
|
j = WET_STA_HASH_UNK;
|
|
wet_sta_remove_mac_entry(weth, &sta->mac);
|
memset(sta, 0, sizeof(wet_sta_t));
|
}
|
}
|
}
|
void
|
dhd_wet_dump(dhd_pub_t *dhdp, struct bcmstrbuf *b)
|
{
|
char eabuf[ETHER_ADDR_STR_LEN];
|
wet_sta_t *sta;
|
int i;
|
dhd_wet_info_t *weth = dhdp->wet_info;
|
|
bcm_bprintf(b, "Host MAC: %s\n", bcm_ether_ntoa(&weth->mac, eabuf));
|
bcm_bprintf(b, "Host IP: %u.%u.%u.%u\n",
|
weth->ip[0], weth->ip[1], weth->ip[2], weth->ip[3]);
|
bcm_bprintf(b, "Entry\tEnetAddr\t\tInetAddr\n");
|
for (i = 0; i < WET_NUMSTAS; i ++) {
|
/* FIXME: it leaves the last sta entry unfiltered, who cares! */
|
if (weth->sta[i].next)
|
continue;
|
/* format the entry dump */
|
sta = &weth->sta[i];
|
bcm_bprintf(b, "%u\t%s\t%u.%u.%u.%u\n",
|
i, bcm_ether_ntoa(&sta->mac, eabuf),
|
sta->ip[0], sta->ip[1], sta->ip[2], sta->ip[3]);
|
}
|
}
|