xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/dhd_wet.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /**
2*4882a593Smuzhiyun  * @file
3*4882a593Smuzhiyun  * @brief
4*4882a593Smuzhiyun  * Wireless EThernet (WET) Bridge.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * WET STA and WET client are inter-exchangable in this file and refer to
7*4882a593Smuzhiyun  * addressable entities whose traffic are sent and received through this
8*4882a593Smuzhiyun  * bridge, including the hosting device.
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * Supported protocol families: IP v4.
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * Tx: replace frames' source MAC address with wireless interface's;
13*4882a593Smuzhiyun  * update the IP-MAC address mapping table entry.
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  * Rx: replace frames' the destination MAC address with what found in
16*4882a593Smuzhiyun  * the IP-MAC address mapping table.
17*4882a593Smuzhiyun  *
18*4882a593Smuzhiyun  * All data structures defined in this file are optimized for IP v4. To
19*4882a593Smuzhiyun  * support other protocol families, write protocol specific handlers.
20*4882a593Smuzhiyun  * Doing so may require data structures changes to expand various address
21*4882a593Smuzhiyun  * storages to fit the protocol specific needs, for example, IPX needs 10
22*4882a593Smuzhiyun  * octets for its network address. Also one may need to define the data
23*4882a593Smuzhiyun  * structures in a more generic way so that they work with all supported
24*4882a593Smuzhiyun  * protocol families, for example, the wet_sta strcture may be defined
25*4882a593Smuzhiyun  * as follow:
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  *	struct wet_sta {
28*4882a593Smuzhiyun  *		uint8 nal;		network address length
29*4882a593Smuzhiyun  *		uint8 na[NETA_MAX_LEN];	network address
30*4882a593Smuzhiyun  *		uint8 mac[ETHER_ADDR_LEN];
31*4882a593Smuzhiyun  *		...
32*4882a593Smuzhiyun  *	};
33*4882a593Smuzhiyun  *
34*4882a593Smuzhiyun  * Copyright (C) 2020, Broadcom.
35*4882a593Smuzhiyun  *
36*4882a593Smuzhiyun  *      Unless you and Broadcom execute a separate written software license
37*4882a593Smuzhiyun  * agreement governing use of this software, this software is licensed to you
38*4882a593Smuzhiyun  * under the terms of the GNU General Public License version 2 (the "GPL"),
39*4882a593Smuzhiyun  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
40*4882a593Smuzhiyun  * following added to such license:
41*4882a593Smuzhiyun  *
42*4882a593Smuzhiyun  *      As a special exception, the copyright holders of this software give you
43*4882a593Smuzhiyun  * permission to link this software with independent modules, and to copy and
44*4882a593Smuzhiyun  * distribute the resulting executable under terms of your choice, provided that
45*4882a593Smuzhiyun  * you also meet, for each linked independent module, the terms and conditions of
46*4882a593Smuzhiyun  * the license of that module.  An independent module is a module which is not
47*4882a593Smuzhiyun  * derived from this software.  The special exception does not apply to any
48*4882a593Smuzhiyun  * modifications of the software.
49*4882a593Smuzhiyun  *
50*4882a593Smuzhiyun  *
51*4882a593Smuzhiyun  * <<Broadcom-WL-IPTag/Open:>>
52*4882a593Smuzhiyun  *
53*4882a593Smuzhiyun  * $Id$
54*4882a593Smuzhiyun  */
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun /**
57*4882a593Smuzhiyun  * @file
58*4882a593Smuzhiyun  * @brief
59*4882a593Smuzhiyun  * XXX Twiki: [WirelessEthernet]
60*4882a593Smuzhiyun  */
61*4882a593Smuzhiyun #include <typedefs.h>
62*4882a593Smuzhiyun #include <bcmdefs.h>
63*4882a593Smuzhiyun #include <osl.h>
64*4882a593Smuzhiyun #include <bcmutils.h>
65*4882a593Smuzhiyun #include <siutils.h>
66*4882a593Smuzhiyun #include <wlioctl.h>
67*4882a593Smuzhiyun #include <802.11.h>
68*4882a593Smuzhiyun #include <ethernet.h>
69*4882a593Smuzhiyun #include <vlan.h>
70*4882a593Smuzhiyun #include <802.3.h>
71*4882a593Smuzhiyun #include <bcmip.h>
72*4882a593Smuzhiyun #include <bcmarp.h>
73*4882a593Smuzhiyun #include <bcmudp.h>
74*4882a593Smuzhiyun #include <bcmdhcp.h>
75*4882a593Smuzhiyun #include <bcmendian.h>
76*4882a593Smuzhiyun #include <dhd_dbg.h>
77*4882a593Smuzhiyun #include <d11.h>
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun #include <dhd_wet.h>
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun /* IP/MAC address mapping entry */
82*4882a593Smuzhiyun typedef struct wet_sta wet_sta_t;
83*4882a593Smuzhiyun struct wet_sta {
84*4882a593Smuzhiyun 	/* client */
85*4882a593Smuzhiyun 	uint8 ip[IPV4_ADDR_LEN];	/* client IP addr */
86*4882a593Smuzhiyun 	struct ether_addr mac;	/* client MAC addr */
87*4882a593Smuzhiyun 	uint8 flags[DHCP_FLAGS_LEN];	/* orig. dhcp flags */
88*4882a593Smuzhiyun 	/* internal */
89*4882a593Smuzhiyun 	wet_sta_t *next;	/* free STA link */
90*4882a593Smuzhiyun 	wet_sta_t *next_ip;	/* hash link by IP */
91*4882a593Smuzhiyun 	wet_sta_t *next_mac;	/* hash link by MAC */
92*4882a593Smuzhiyun };
93*4882a593Smuzhiyun #define WET_NUMSTAS		(1 << 8)	/* max. # clients, must be multiple of 2 */
94*4882a593Smuzhiyun #define WET_STA_HASH_SIZE	(WET_NUMSTAS/2)	/* must be <= WET_NUMSTAS */
95*4882a593Smuzhiyun #define WET_STA_HASH_IP(ip)	((ip)[3]&(WET_STA_HASH_SIZE-1))	/* hash by IP */
96*4882a593Smuzhiyun #define WET_STA_HASH_MAC(ea)	(((ea)[3]^(ea)[4]^(ea)[5])&(WET_STA_HASH_SIZE-1)) /* hash by MAC */
97*4882a593Smuzhiyun #define WET_STA_HASH_UNK	-1 /* Unknown hash */
98*4882a593Smuzhiyun #define IP_ISMULTI(ip)           (((ip) & 0xf0000000) == 0xe0000000) /* Check for multicast by IP */
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun /* WET private info structure */
101*4882a593Smuzhiyun struct dhd_wet_info {
102*4882a593Smuzhiyun 	/* pointer to dhdpublic info struct */
103*4882a593Smuzhiyun 	dhd_pub_t *pub;
104*4882a593Smuzhiyun 	/* Host addresses */
105*4882a593Smuzhiyun 	uint8 ip[IPV4_ADDR_LEN];
106*4882a593Smuzhiyun 	struct ether_addr mac;
107*4882a593Smuzhiyun 	/* STA storage, one entry per eth. client */
108*4882a593Smuzhiyun 	wet_sta_t sta[WET_NUMSTAS];
109*4882a593Smuzhiyun 	/* Free sta list */
110*4882a593Smuzhiyun 	wet_sta_t *stafree;
111*4882a593Smuzhiyun 	/* Used sta hash by IP */
112*4882a593Smuzhiyun 	wet_sta_t *stahash_ip[WET_STA_HASH_SIZE];
113*4882a593Smuzhiyun 	/* Used sta hash by MAC */
114*4882a593Smuzhiyun 	wet_sta_t *stahash_mac[WET_STA_HASH_SIZE];
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun /* forward declarations */
118*4882a593Smuzhiyun static int wet_eth_proc(dhd_wet_info_t *weth, void *sdu,
119*4882a593Smuzhiyun 	uint8 *frame, int length, int send);
120*4882a593Smuzhiyun static int wet_vtag_proc(dhd_wet_info_t *weth, void *sdu,
121*4882a593Smuzhiyun 	uint8 * eh, uint8 *vtag, int length, int send);
122*4882a593Smuzhiyun static int wet_ip_proc(dhd_wet_info_t *weth, void *sdu,
123*4882a593Smuzhiyun 	uint8 * eh, uint8 *iph, int length, int send);
124*4882a593Smuzhiyun static int wet_arp_proc(dhd_wet_info_t *weth, void *sdu,
125*4882a593Smuzhiyun 	uint8 *eh, uint8 *arph, int length, int send);
126*4882a593Smuzhiyun static int wet_udp_proc(dhd_wet_info_t *weth,
127*4882a593Smuzhiyun 	uint8 *eh, uint8 *iph, uint8 *udph, int length, int send);
128*4882a593Smuzhiyun static int wet_dhcpc_proc(dhd_wet_info_t *weth,
129*4882a593Smuzhiyun 	uint8 *eh, uint8 *iph, uint8 *udph, uint8 *dhcp, int length, int send);
130*4882a593Smuzhiyun static int wet_dhcps_proc(dhd_wet_info_t *weth,
131*4882a593Smuzhiyun 	uint8 *eh, uint8 *iph, uint8 *udph, uint8 *dhcp, int length, int send);
132*4882a593Smuzhiyun static int wet_sta_alloc(dhd_wet_info_t *weth, wet_sta_t **saddr);
133*4882a593Smuzhiyun static int wet_sta_update_all(dhd_wet_info_t *weth,
134*4882a593Smuzhiyun 	uint8 *iaddr, struct ether_addr *eaddr, wet_sta_t **saddr);
135*4882a593Smuzhiyun static int wet_sta_update_mac(dhd_wet_info_t *weth,
136*4882a593Smuzhiyun 	struct ether_addr *eaddr, wet_sta_t **saddr);
137*4882a593Smuzhiyun static int wet_sta_remove_mac_entry(dhd_wet_info_t *weth, struct ether_addr *eaddr);
138*4882a593Smuzhiyun static int wet_sta_find_ip(dhd_wet_info_t *weth,
139*4882a593Smuzhiyun 	uint8 *iaddr, wet_sta_t **saddr);
140*4882a593Smuzhiyun static int wet_sta_find_mac(dhd_wet_info_t *weth,
141*4882a593Smuzhiyun 	struct ether_addr *eaddr, wet_sta_t **saddr);
142*4882a593Smuzhiyun static void csum_fixup_16(uint8 *chksum,
143*4882a593Smuzhiyun 	uint8 *optr, int olen, uint8 *nptr, int nlen);
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun /*
146*4882a593Smuzhiyun  * Protocol handler. 'ph' points to protocol specific header,
147*4882a593Smuzhiyun  * for example, it points to IP header if it is IP packet.
148*4882a593Smuzhiyun  */
149*4882a593Smuzhiyun typedef int (*prot_proc_t)(dhd_wet_info_t *weth, void *sdu, uint8 *eh,
150*4882a593Smuzhiyun 	uint8 *ph, int length, int send);
151*4882a593Smuzhiyun /* Protocol handlers hash table - hash by ether type */
152*4882a593Smuzhiyun typedef struct prot_hdlr prot_hdlr_t;
153*4882a593Smuzhiyun struct prot_hdlr {
154*4882a593Smuzhiyun 	uint16 type;		/* ether type */
155*4882a593Smuzhiyun 	prot_proc_t prot_proc;
156*4882a593Smuzhiyun 	prot_hdlr_t *next;	/* next proto handler that has the same hash */
157*4882a593Smuzhiyun };
158*4882a593Smuzhiyun #define WET_PROT_HASH_SIZE	(1 << 3)
159*4882a593Smuzhiyun #define WET_PROT_HASH(t)	((t)[1]&(WET_PROT_HASH_SIZE-1))
160*4882a593Smuzhiyun static prot_hdlr_t ept_tbl[] = {
161*4882a593Smuzhiyun 	/* 0 */ {HTON16(ETHER_TYPE_8021Q), wet_vtag_proc, NULL}, /* 0x8100 */
162*4882a593Smuzhiyun };
163*4882a593Smuzhiyun static prot_hdlr_t prot_hash[WET_PROT_HASH_SIZE] = {
164*4882a593Smuzhiyun 	/* 0 */ {HTON16(ETHER_TYPE_IP), wet_ip_proc, &ept_tbl[0]}, /* 0x0800 */
165*4882a593Smuzhiyun 	/* 1 */ {0, NULL, NULL},	/* unused   */
166*4882a593Smuzhiyun 	/* 2 */ {0, NULL, NULL},	/* unused   */
167*4882a593Smuzhiyun 	/* 3 */ {0, NULL, NULL},	/* unused   */
168*4882a593Smuzhiyun 	/* 4 */ {0, NULL, NULL},	/* unused   */
169*4882a593Smuzhiyun 	/* 5 */ {0, NULL, NULL},	/* unused   */
170*4882a593Smuzhiyun 	/* 6 */ {HTON16(ETHER_TYPE_ARP), wet_arp_proc, NULL},	/* 0x0806 */
171*4882a593Smuzhiyun 	/* 7 */ {0, NULL, NULL},	/* unused   */
172*4882a593Smuzhiyun };
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun /*
175*4882a593Smuzhiyun  * IPv4 handler. 'ph' points to protocol specific header,
176*4882a593Smuzhiyun  * for example, it points to UDP header if it is UDP packet.
177*4882a593Smuzhiyun  */
178*4882a593Smuzhiyun typedef int (*ipv4_proc_t)(dhd_wet_info_t *weth, uint8 *eh,
179*4882a593Smuzhiyun 	uint8 *iph, uint8 *ph, int length, int send);
180*4882a593Smuzhiyun /* IPv4 handlers hash table - hash by protocol type */
181*4882a593Smuzhiyun typedef struct ipv4_hdlr ipv4_hdlr_t;
182*4882a593Smuzhiyun struct ipv4_hdlr {
183*4882a593Smuzhiyun 	uint8 type;	/* protocol type */
184*4882a593Smuzhiyun 	ipv4_proc_t ipv4_proc;
185*4882a593Smuzhiyun 	ipv4_hdlr_t *next;	/* next proto handler that has the same hash */
186*4882a593Smuzhiyun };
187*4882a593Smuzhiyun #define WET_IPV4_HASH_SIZE	(1 << 1)
188*4882a593Smuzhiyun #define WET_IPV4_HASH(p)	((p)&(WET_IPV4_HASH_SIZE-1))
189*4882a593Smuzhiyun static ipv4_hdlr_t ipv4_hash[WET_IPV4_HASH_SIZE] = {
190*4882a593Smuzhiyun 	/* 0 */ {0, NULL, NULL},	/* unused   */
191*4882a593Smuzhiyun 	/* 1 */ {IP_PROT_UDP, wet_udp_proc, NULL},	/* 0x11 */
192*4882a593Smuzhiyun };
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun /*
195*4882a593Smuzhiyun  * UDP handler. 'ph' points to protocol specific header,
196*4882a593Smuzhiyun  * for example, it points to DHCP header if it is DHCP packet.
197*4882a593Smuzhiyun  */
198*4882a593Smuzhiyun typedef int (*udp_proc_t)(dhd_wet_info_t *weth, uint8 *eh,
199*4882a593Smuzhiyun 	uint8 *iph, uint8 *udph, uint8 *ph, int length, int send);
200*4882a593Smuzhiyun /* UDP handlers hash table - hash by port number */
201*4882a593Smuzhiyun typedef struct udp_hdlr udp_hdlr_t;
202*4882a593Smuzhiyun struct udp_hdlr {
203*4882a593Smuzhiyun 	uint16 port;	/* udp dest. port */
204*4882a593Smuzhiyun 	udp_proc_t udp_proc;
205*4882a593Smuzhiyun 	udp_hdlr_t *next;	/* next proto handler that has the same hash */
206*4882a593Smuzhiyun };
207*4882a593Smuzhiyun #define WET_UDP_HASH_SIZE	(1 << 3)
208*4882a593Smuzhiyun #define WET_UDP_HASH(p)	((p)[1]&(WET_UDP_HASH_SIZE-1))
209*4882a593Smuzhiyun static udp_hdlr_t udp_hash[WET_UDP_HASH_SIZE] = {
210*4882a593Smuzhiyun 	/* 0 */ {0, NULL, NULL},	/* unused   */
211*4882a593Smuzhiyun 	/* 1 */ {0, NULL, NULL},	/* unused   */
212*4882a593Smuzhiyun 	/* 2 */ {0, NULL, NULL},	/* unused   */
213*4882a593Smuzhiyun 	/* 3 */ {HTON16(DHCP_PORT_SERVER), wet_dhcpc_proc, NULL}, /* 0x43 */
214*4882a593Smuzhiyun 	/* 4 */ {HTON16(DHCP_PORT_CLIENT), wet_dhcps_proc, NULL}, /* 0x44 */
215*4882a593Smuzhiyun 	/* 5 */ {0, NULL, NULL},	/* unused   */
216*4882a593Smuzhiyun 	/* 6 */ {0, NULL, NULL},	/* unused   */
217*4882a593Smuzhiyun 	/* 7 */ {0, NULL, NULL},	/* unused   */
218*4882a593Smuzhiyun };
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun #define WETHWADDR(weth)	((weth)->pub->mac.octet)
221*4882a593Smuzhiyun #define WETOSH(weth)	((weth)->pub->osh)
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun /* special values */
224*4882a593Smuzhiyun /* 802.3 llc/snap header */
225*4882a593Smuzhiyun static uint8 llc_snap_hdr[SNAP_HDR_LEN] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
226*4882a593Smuzhiyun static uint8 ipv4_bcast[IPV4_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff}; /* IP v4 broadcast address */
227*4882a593Smuzhiyun static uint8 ipv4_null[IPV4_ADDR_LEN] = {0x00, 0x00, 0x00, 0x00}; /* IP v4 NULL address */
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun dhd_wet_info_t *
dhd_get_wet_info(dhd_pub_t * pub)230*4882a593Smuzhiyun dhd_get_wet_info(dhd_pub_t *pub)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	dhd_wet_info_t *p;
233*4882a593Smuzhiyun 	int i;
234*4882a593Smuzhiyun 	p = (dhd_wet_info_t *)MALLOCZ(pub->osh, sizeof(dhd_wet_info_t));
235*4882a593Smuzhiyun 	if (p == NULL) {
236*4882a593Smuzhiyun 		return 0;
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 	for (i = 0; i < WET_NUMSTAS - 1; i ++)
239*4882a593Smuzhiyun 		p->sta[i].next = &p->sta[i + 1];
240*4882a593Smuzhiyun 	p->stafree = &p->sta[0];
241*4882a593Smuzhiyun 	p->pub = pub;
242*4882a593Smuzhiyun 	return p;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun void
dhd_free_wet_info(dhd_pub_t * pub,void * wet)246*4882a593Smuzhiyun dhd_free_wet_info(dhd_pub_t *pub, void *wet)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun 	if (wet) {
249*4882a593Smuzhiyun 		MFREE(pub->osh, wet, sizeof(dhd_wet_info_t));
250*4882a593Smuzhiyun 	}
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun 
dhd_set_wet_host_ipv4(dhd_pub_t * pub,void * parms,uint32 len)253*4882a593Smuzhiyun void dhd_set_wet_host_ipv4(dhd_pub_t *pub, void *parms, uint32 len)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	dhd_wet_info_t *p;
256*4882a593Smuzhiyun 	p = (dhd_wet_info_t *)pub->wet_info;
257*4882a593Smuzhiyun 	bcopy(parms, p->ip, len);
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun 
dhd_set_wet_host_mac(dhd_pub_t * pub,void * parms,uint32 len)260*4882a593Smuzhiyun void dhd_set_wet_host_mac(dhd_pub_t *pub, void *parms, uint32 len)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun 	dhd_wet_info_t *p;
263*4882a593Smuzhiyun 	p = (dhd_wet_info_t *)pub->wet_info;
264*4882a593Smuzhiyun 	bcopy(parms, &p->mac, len);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun /* process Ethernet frame */
267*4882a593Smuzhiyun /*
268*4882a593Smuzhiyun * Return:
269*4882a593Smuzhiyun *	= 0 if frame is done ok
270*4882a593Smuzhiyun *	< 0 if unable to handle the frame
271*4882a593Smuzhiyun *	> 0 if no further process
272*4882a593Smuzhiyun */
273*4882a593Smuzhiyun static int
BCMFASTPATH(wet_eth_proc)274*4882a593Smuzhiyun BCMFASTPATH(wet_eth_proc)(dhd_wet_info_t *weth, void *sdu, uint8 *frame, int length, int send)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun 	uint8 *pt = frame + ETHER_TYPE_OFFSET;
277*4882a593Smuzhiyun 	uint16 type;
278*4882a593Smuzhiyun 	uint8 *ph;
279*4882a593Smuzhiyun 	prot_hdlr_t *phdlr;
280*4882a593Smuzhiyun 	/* intercept Ethernet II frame (type > 1500) */
281*4882a593Smuzhiyun 	if (length >= ETHER_HDR_LEN && (pt[0] > (ETHER_MAX_DATA >> 8) ||
282*4882a593Smuzhiyun 	    (pt[0] == (ETHER_MAX_DATA >> 8) && pt[1] > (ETHER_MAX_DATA & 0xff))))
283*4882a593Smuzhiyun 		;
284*4882a593Smuzhiyun 	/* intercept 802.3 LLC/SNAP frame (type <= 1500) */
285*4882a593Smuzhiyun 	else if (length >= ETHER_HDR_LEN + SNAP_HDR_LEN + ETHER_TYPE_LEN) {
286*4882a593Smuzhiyun 		uint8 *llc = frame + ETHER_HDR_LEN;
287*4882a593Smuzhiyun 		if (bcmp(llc_snap_hdr, llc, SNAP_HDR_LEN))
288*4882a593Smuzhiyun 			return 0;
289*4882a593Smuzhiyun 		pt = llc + SNAP_HDR_LEN;
290*4882a593Smuzhiyun 	}
291*4882a593Smuzhiyun 	/* frame too short bail out */
292*4882a593Smuzhiyun 	else {
293*4882a593Smuzhiyun 		DHD_ERROR(("wet_eth_proc: %s short eth frame, ignored\n",
294*4882a593Smuzhiyun 			send ? "send" : "recv"));
295*4882a593Smuzhiyun 		return -1;
296*4882a593Smuzhiyun 	}
297*4882a593Smuzhiyun 	ph = pt + ETHER_TYPE_LEN;
298*4882a593Smuzhiyun 	length -= ph - frame;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	/* Call protocol specific handler to process frame. */
301*4882a593Smuzhiyun 	type = *(uint16 *)pt;
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	for (phdlr = &prot_hash[WET_PROT_HASH(pt)];
304*4882a593Smuzhiyun 	     phdlr != NULL; phdlr = phdlr->next) {
305*4882a593Smuzhiyun 		if (phdlr->type != type || !phdlr->prot_proc)
306*4882a593Smuzhiyun 			continue;
307*4882a593Smuzhiyun 		return (phdlr->prot_proc)(weth, sdu, frame, ph, length, send);
308*4882a593Smuzhiyun 	}
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (!bcmp(WETHWADDR(weth), frame + ETHER_SRC_OFFSET, ETHER_ADDR_LEN)) {
311*4882a593Smuzhiyun 		return 0;
312*4882a593Smuzhiyun 	}
313*4882a593Smuzhiyun 	else {
314*4882a593Smuzhiyun 		DHD_INFO(("%s: %s unknown type (0x%X), ignored %s\n",
315*4882a593Smuzhiyun 			__FUNCTION__, send ? "send" : "recv", type,
316*4882a593Smuzhiyun 			(type == 0xDD86) ? "IPv6":""));
317*4882a593Smuzhiyun 		/* ignore unsupported protocol from different mac addr than us */
318*4882a593Smuzhiyun 		return BCME_UNSUPPORTED;
319*4882a593Smuzhiyun 	}
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun /* process 8021p/Q tagged frame */
323*4882a593Smuzhiyun /*
324*4882a593Smuzhiyun * Return:
325*4882a593Smuzhiyun *	= 0 if frame is done ok
326*4882a593Smuzhiyun *	< 0 if unable to handle the frame
327*4882a593Smuzhiyun *	> 0 if no further process
328*4882a593Smuzhiyun */
329*4882a593Smuzhiyun static int
BCMFASTPATH(wet_vtag_proc)330*4882a593Smuzhiyun BCMFASTPATH(wet_vtag_proc)(dhd_wet_info_t *weth, void *sdu,
331*4882a593Smuzhiyun 	uint8 * eh, uint8 *vtag, int length, int send)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun 	uint16 type;
334*4882a593Smuzhiyun 	uint8 *pt;
335*4882a593Smuzhiyun 	prot_hdlr_t *phdlr;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	/* check minimum length */
338*4882a593Smuzhiyun 	if (length < ETHERVLAN_HDR_LEN) {
339*4882a593Smuzhiyun 		DHD_ERROR(("wet_vtag_proc: %s short VLAN frame, ignored\n",
340*4882a593Smuzhiyun 			send ? "send" : "recv"));
341*4882a593Smuzhiyun 		return -1;
342*4882a593Smuzhiyun 	}
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	/*
345*4882a593Smuzhiyun 	 * FIXME: check recursiveness to prevent stack from overflow
346*4882a593Smuzhiyun 	 * in case someone sent frames 8100xxxxxxxx8100xxxxxxxx...
347*4882a593Smuzhiyun 	 */
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	/* Call protocol specific handler to process frame. */
350*4882a593Smuzhiyun 	type = *(uint16 *)(pt = vtag + VLAN_TAG_LEN);
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	for (phdlr = &prot_hash[WET_PROT_HASH(pt)];
353*4882a593Smuzhiyun 	     phdlr != NULL; phdlr = phdlr->next) {
354*4882a593Smuzhiyun 		if (phdlr->type != type || !phdlr->prot_proc)
355*4882a593Smuzhiyun 			continue;
356*4882a593Smuzhiyun 		return (phdlr->prot_proc)(weth, sdu, eh,
357*4882a593Smuzhiyun 			pt + ETHER_TYPE_LEN, length, send);
358*4882a593Smuzhiyun 	}
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	return 0;
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun /* process IP frame */
364*4882a593Smuzhiyun /*
365*4882a593Smuzhiyun * Return:
366*4882a593Smuzhiyun *	= 0 if frame is done ok
367*4882a593Smuzhiyun *	< 0 if unable to handle the frame
368*4882a593Smuzhiyun *       > 0 if no further process
369*4882a593Smuzhiyun */
370*4882a593Smuzhiyun static int
BCMFASTPATH(wet_ip_proc)371*4882a593Smuzhiyun BCMFASTPATH(wet_ip_proc)(dhd_wet_info_t *weth, void *sdu,
372*4882a593Smuzhiyun 		uint8 *eh, uint8 *iph, int length, int send)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	uint8 type;
375*4882a593Smuzhiyun 	int ihl;
376*4882a593Smuzhiyun 	wet_sta_t *sta;
377*4882a593Smuzhiyun 	ipv4_hdlr_t *iphdlr;
378*4882a593Smuzhiyun 	uint8 *iaddr;
379*4882a593Smuzhiyun 	struct ether_addr *ea = NULL;
380*4882a593Smuzhiyun 	int ret, ea_off = 0;
381*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
382*4882a593Smuzhiyun 	BCM_REFERENCE(eabuf);
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	/* IPv4 only */
385*4882a593Smuzhiyun 	if (length < 1 || (IP_VER(iph) != IP_VER_4)) {
386*4882a593Smuzhiyun 		DHD_INFO(("wet_ip_proc: %s non IPv4 frame, ignored\n",
387*4882a593Smuzhiyun 			send ? "send" : "recv"));
388*4882a593Smuzhiyun 		return -1;
389*4882a593Smuzhiyun 	}
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	ihl = IPV4_HLEN(iph);
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	/* minimum length */
394*4882a593Smuzhiyun 	if (length < ihl) {
395*4882a593Smuzhiyun 		DHD_ERROR(("wet_ip_proc: %s short IPv4 frame, ignored\n",
396*4882a593Smuzhiyun 		send ? "send" : "recv"));
397*4882a593Smuzhiyun 		return -1;
398*4882a593Smuzhiyun 	}
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	/* protocol specific handling */
401*4882a593Smuzhiyun 	type = IPV4_PROT(iph);
402*4882a593Smuzhiyun 	for (iphdlr = &ipv4_hash[WET_IPV4_HASH(type)];
403*4882a593Smuzhiyun 			iphdlr; iphdlr = iphdlr->next) {
404*4882a593Smuzhiyun 		if (iphdlr->type != type || !iphdlr->ipv4_proc)
405*4882a593Smuzhiyun 			continue;
406*4882a593Smuzhiyun 		if ((ret = (iphdlr->ipv4_proc)(weth, eh,
407*4882a593Smuzhiyun 			iph, iph + ihl, length - ihl, send)))
408*4882a593Smuzhiyun 			return ret;
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	/* generic IP packet handling
412*4882a593Smuzhiyun 	 * Replace source MAC in Ethernet header with wireless's and
413*4882a593Smuzhiyun 	 * keep track of IP MAC mapping when sending frame.
414*4882a593Smuzhiyun 	 */
415*4882a593Smuzhiyun 	if (send) {
416*4882a593Smuzhiyun 		uint32 iaddr_dest, iaddr_src;
417*4882a593Smuzhiyun 		bool wet_table_upd = TRUE;
418*4882a593Smuzhiyun 		iaddr = iph + IPV4_SRC_IP_OFFSET;
419*4882a593Smuzhiyun 		iaddr_dest = ntoh32(*((uint32 *)(iph + IPV4_DEST_IP_OFFSET)));
420*4882a593Smuzhiyun 		iaddr_src = ntoh32(*(uint32 *)(iaddr));
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 		/* Do not process and update knowledge base on receipt of a local IP
423*4882a593Smuzhiyun 		 * multicast frame
424*4882a593Smuzhiyun 		 */
425*4882a593Smuzhiyun 		if (IP_ISMULTI(iaddr_dest) && !iaddr_src) {
426*4882a593Smuzhiyun 			DHD_INFO(("recv multicast frame from %s.Don't update hash table\n",
427*4882a593Smuzhiyun 				bcm_ether_ntoa((struct ether_addr*)
428*4882a593Smuzhiyun 				(eh + ETHER_SRC_OFFSET), eabuf)));
429*4882a593Smuzhiyun 			wet_table_upd = FALSE;
430*4882a593Smuzhiyun 		}
431*4882a593Smuzhiyun 		if (wet_table_upd && wet_sta_update_all(weth, iaddr,
432*4882a593Smuzhiyun 				(struct ether_addr*)(eh + ETHER_SRC_OFFSET), &sta) < 0) {
433*4882a593Smuzhiyun 			DHD_INFO(("wet_ip_proc: unable to update STA %u.%u.%u.%u %s\n",
434*4882a593Smuzhiyun 				iaddr[0], iaddr[1], iaddr[2], iaddr[3],
435*4882a593Smuzhiyun 				bcm_ether_ntoa(
436*4882a593Smuzhiyun 				(struct ether_addr*)(eh + ETHER_SRC_OFFSET), eabuf)));
437*4882a593Smuzhiyun 			return -1;
438*4882a593Smuzhiyun 		}
439*4882a593Smuzhiyun 		ea = (struct ether_addr *)WETHWADDR(weth);
440*4882a593Smuzhiyun 		ea_off = ETHER_SRC_OFFSET;
441*4882a593Smuzhiyun 		eacopy(ea, eh + ea_off);
442*4882a593Smuzhiyun 	}
443*4882a593Smuzhiyun 	/*
444*4882a593Smuzhiyun 	 * Replace dest MAC in Ethernet header using the found one
445*4882a593Smuzhiyun 	 * when receiving frame.
446*4882a593Smuzhiyun 	 */
447*4882a593Smuzhiyun 	/* no action for received bcast/mcast ethernet frame */
448*4882a593Smuzhiyun 	else if (!ETHER_ISMULTI(eh)) {
449*4882a593Smuzhiyun 		iaddr = iph + IPV4_DEST_IP_OFFSET;
450*4882a593Smuzhiyun 		if (wet_sta_find_ip(weth, iaddr, &sta) < 0) {
451*4882a593Smuzhiyun 			DHD_ERROR(("wet_ip_proc: unable to find STA %u.%u.%u.%u\n",
452*4882a593Smuzhiyun 				iaddr[0], iaddr[1], iaddr[2], iaddr[3]));
453*4882a593Smuzhiyun 			return -1;
454*4882a593Smuzhiyun 		}
455*4882a593Smuzhiyun 		ea = &sta->mac;
456*4882a593Smuzhiyun 		ea_off = ETHER_DEST_OFFSET;
457*4882a593Smuzhiyun 		eacopy(ea, eh + ea_off);
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	return 0;
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun /* process ARP frame - ARP proxy */
464*4882a593Smuzhiyun /*
465*4882a593Smuzhiyun  * Return:
466*4882a593Smuzhiyun  *	= 0 if frame is done ok
467*4882a593Smuzhiyun  *	< 0 if unable to handle the frame
468*4882a593Smuzhiyun  *       > 0 if no further process
469*4882a593Smuzhiyun  */
470*4882a593Smuzhiyun static int
BCMFASTPATH(wet_arp_proc)471*4882a593Smuzhiyun BCMFASTPATH(wet_arp_proc)(dhd_wet_info_t *weth, void *sdu,
472*4882a593Smuzhiyun 		uint8 *eh, uint8 *arph, int length, int send)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun 	wet_sta_t *sta;
475*4882a593Smuzhiyun 	uint8 *iaddr;
476*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
477*4882a593Smuzhiyun 	BCM_REFERENCE(eabuf);
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	/*
480*4882a593Smuzhiyun 	 * FIXME: validate ARP header:
481*4882a593Smuzhiyun 	 *  h/w Ethernet 2, proto IP x800, h/w addr size 6, proto addr size 4.
482*4882a593Smuzhiyun 	 */
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	/*
485*4882a593Smuzhiyun 	 * Replace source MAC in Ethernet header as well as source MAC in
486*4882a593Smuzhiyun 	 * ARP protocol header when processing frame sent.
487*4882a593Smuzhiyun 	 */
488*4882a593Smuzhiyun 	if (send) {
489*4882a593Smuzhiyun 		iaddr = arph + ARP_SRC_IP_OFFSET;
490*4882a593Smuzhiyun 		if (wet_sta_update_all(weth, iaddr,
491*4882a593Smuzhiyun 				(struct ether_addr*)(eh + ETHER_SRC_OFFSET), &sta) < 0) {
492*4882a593Smuzhiyun 			DHD_INFO(("wet_arp_proc: unable to update STA %u.%u.%u.%u %s\n",
493*4882a593Smuzhiyun 					iaddr[0], iaddr[1], iaddr[2], iaddr[3],
494*4882a593Smuzhiyun 					bcm_ether_ntoa(
495*4882a593Smuzhiyun 					(struct ether_addr*)(eh + ETHER_SRC_OFFSET), eabuf)));
496*4882a593Smuzhiyun 			return -1;
497*4882a593Smuzhiyun 		}
498*4882a593Smuzhiyun 		bcopy(WETHWADDR(weth), eh + ETHER_SRC_OFFSET, ETHER_ADDR_LEN);
499*4882a593Smuzhiyun 		bcopy(WETHWADDR(weth), arph+ARP_SRC_ETH_OFFSET, ETHER_ADDR_LEN);
500*4882a593Smuzhiyun 	}
501*4882a593Smuzhiyun 	/*
502*4882a593Smuzhiyun 	 * Replace dest MAC in Ethernet header as well as dest MAC in
503*4882a593Smuzhiyun 	 * ARP protocol header when processing frame recv'd. Process ARP
504*4882a593Smuzhiyun 	 * replies and Unicast ARP requests.
505*4882a593Smuzhiyun 	 */
506*4882a593Smuzhiyun 	else if ((*(uint16 *)(arph + ARP_OPC_OFFSET) == HTON16(ARP_OPC_REPLY)) ||
507*4882a593Smuzhiyun 		((*(uint16 *)(arph + ARP_OPC_OFFSET) == HTON16(ARP_OPC_REQUEST)) &&
508*4882a593Smuzhiyun 		(!ETHER_ISMULTI(eh)))) {
509*4882a593Smuzhiyun 		iaddr = arph + ARP_TGT_IP_OFFSET;
510*4882a593Smuzhiyun 		if (wet_sta_find_ip(weth, iaddr, &sta) < 0) {
511*4882a593Smuzhiyun 			DHD_INFO(("wet_arp_proc: unable to find STA %u.%u.%u.%u\n",
512*4882a593Smuzhiyun 				iaddr[0], iaddr[1], iaddr[2], iaddr[3]));
513*4882a593Smuzhiyun 			return -1;
514*4882a593Smuzhiyun 		}
515*4882a593Smuzhiyun 		bcopy(&sta->mac, arph + ARP_TGT_ETH_OFFSET, ETHER_ADDR_LEN);
516*4882a593Smuzhiyun 		bcopy(&sta->mac, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	return 0;
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun /* process UDP frame */
523*4882a593Smuzhiyun /*
524*4882a593Smuzhiyun  * Return:
525*4882a593Smuzhiyun  *	= 0 if frame is done ok
526*4882a593Smuzhiyun  *	< 0 if unable to handle the frame
527*4882a593Smuzhiyun  *       > 0 if no further process
528*4882a593Smuzhiyun  */
529*4882a593Smuzhiyun static int
BCMFASTPATH(wet_udp_proc)530*4882a593Smuzhiyun BCMFASTPATH(wet_udp_proc)(dhd_wet_info_t *weth,
531*4882a593Smuzhiyun 		uint8 *eh, uint8 *iph, uint8 *udph, int length, int send)
532*4882a593Smuzhiyun {
533*4882a593Smuzhiyun 	udp_hdlr_t *udphdlr;
534*4882a593Smuzhiyun 	uint16 port;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	/* check frame length, at least UDP_HDR_LEN */
537*4882a593Smuzhiyun 	if ((length -= UDP_HDR_LEN) < 0) {
538*4882a593Smuzhiyun 		DHD_ERROR(("wet_udp_proc: %s short UDP frame, ignored\n",
539*4882a593Smuzhiyun 			send ? "send" : "recv"));
540*4882a593Smuzhiyun 		return -1;
541*4882a593Smuzhiyun 	}
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	/*
544*4882a593Smuzhiyun 	 * Unfortunately we must spend some time here to deal with
545*4882a593Smuzhiyun 	 * some higher layer protocol special processings.
546*4882a593Smuzhiyun 	 * See individual handlers for protocol specific details.
547*4882a593Smuzhiyun 	 */
548*4882a593Smuzhiyun 	port = *(uint16 *)(udph + UDP_DEST_PORT_OFFSET);
549*4882a593Smuzhiyun 	for (udphdlr = &udp_hash[WET_UDP_HASH((uint8 *)&port)];
550*4882a593Smuzhiyun 			udphdlr; udphdlr = udphdlr->next) {
551*4882a593Smuzhiyun 		if (udphdlr->port != port || !udphdlr->udp_proc)
552*4882a593Smuzhiyun 			continue;
553*4882a593Smuzhiyun 		return (udphdlr->udp_proc)(weth, eh, iph, udph,
554*4882a593Smuzhiyun 				udph + UDP_HDR_LEN, length, send);
555*4882a593Smuzhiyun 	}
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	return 0;
558*4882a593Smuzhiyun }
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun /*
561*4882a593Smuzhiyun  * DHCP is a 'complex' protocol for WET, mainly because it
562*4882a593Smuzhiyun  * uses its protocol body to convey IP/MAC info. It is impossible
563*4882a593Smuzhiyun  * to forward frames correctly back and forth without looking
564*4882a593Smuzhiyun  * into the DHCP's body and interpreting it. See RFC2131 sect.
565*4882a593Smuzhiyun  * 4.1 'Constructing and sending DHCP messages' for details
566*4882a593Smuzhiyun  * of using/parsing various fields in the body.
567*4882a593Smuzhiyun  *
568*4882a593Smuzhiyun  * DHCP pass through:
569*4882a593Smuzhiyun  *
570*4882a593Smuzhiyun  *       Must alter DHCP flag to broadcast so that the server
571*4882a593Smuzhiyun  *       can reply with the broadcast address before we can
572*4882a593Smuzhiyun  *	 provide DHCP relay functionality. Otherwise the DHCP
573*4882a593Smuzhiyun  *       server will send DHCP replies using the DHCP client's
574*4882a593Smuzhiyun  *       MAC address. Such replies will not be delivered simply
575*4882a593Smuzhiyun  *       because:
576*4882a593Smuzhiyun  *
577*4882a593Smuzhiyun  *         1. The AP's bridge will not forward the replies back to
578*4882a593Smuzhiyun  *         this device through the wireless link because it does not
579*4882a593Smuzhiyun  *         know such node exists on this link. The bridge's forwarding
580*4882a593Smuzhiyun  *         table on the AP will have this device's MAC address only.
581*4882a593Smuzhiyun  *         It does not know anything else behind this device.
582*4882a593Smuzhiyun  *
583*4882a593Smuzhiyun  *         2. The AP's wireless driver won't allow such frames out
584*4882a593Smuzhiyun  *         either even if they made their way out the AP's bridge
585*4882a593Smuzhiyun  *         through the bridge's DLF broadcasting because there is
586*4882a593Smuzhiyun  *         no such STA associated with the AP.
587*4882a593Smuzhiyun  *
588*4882a593Smuzhiyun  *         3. This device's MAC won't allow such frames pass
589*4882a593Smuzhiyun  *         through in non-promiscuous mode even when they made
590*4882a593Smuzhiyun  *         their way out of the AP's wireless interface somehow.
591*4882a593Smuzhiyun  *
592*4882a593Smuzhiyun  * DHCP relay:
593*4882a593Smuzhiyun  *
594*4882a593Smuzhiyun  *       Once the WET is configured with the host MAC address it can
595*4882a593Smuzhiyun  *       relay the host request as if it were sent from WET itself.
596*4882a593Smuzhiyun  *
597*4882a593Smuzhiyun  *       Once the WET is configured with the host IP address it can
598*4882a593Smuzhiyun  *       pretend to be the host and act as a relay agent.
599*4882a593Smuzhiyun  *
600*4882a593Smuzhiyun  * process DHCP client frame (client to server, or server to relay agent)
601*4882a593Smuzhiyun  * Return:
602*4882a593Smuzhiyun  *	= 0 if frame is done ok
603*4882a593Smuzhiyun  *	< 0 if unable to handle the frame
604*4882a593Smuzhiyun  *      > 0 if no further process
605*4882a593Smuzhiyun  */
606*4882a593Smuzhiyun static int
BCMFASTPATH(wet_dhcpc_proc)607*4882a593Smuzhiyun BCMFASTPATH(wet_dhcpc_proc)(dhd_wet_info_t *weth,
608*4882a593Smuzhiyun 		uint8 *eh, uint8 *iph, uint8 *udph, uint8 *dhcp, int length, int send)
609*4882a593Smuzhiyun {
610*4882a593Smuzhiyun 	wet_sta_t *sta;
611*4882a593Smuzhiyun 	uint16 flags;
612*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
613*4882a593Smuzhiyun 	uint16 port;
614*4882a593Smuzhiyun 	uint8 *ipv4;
615*4882a593Smuzhiyun 	const struct ether_addr *ether;
616*4882a593Smuzhiyun 	BCM_REFERENCE(eabuf);
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 	/*
619*4882a593Smuzhiyun 	 * FIXME: validate DHCP body:
620*4882a593Smuzhiyun 	 * htype Ethernet 1, hlen Ethernet 6, frame length at least 242.
621*4882a593Smuzhiyun 	 */
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 	/* only interested in requests when sending to server */
624*4882a593Smuzhiyun 	if (send && *(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REQUEST)
625*4882a593Smuzhiyun 		return 0;
626*4882a593Smuzhiyun 	/* only interested in replies when receiving from server as a relay agent */
627*4882a593Smuzhiyun 	if (!send && *(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REPLY)
628*4882a593Smuzhiyun 		return 0;
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	/* send request */
631*4882a593Smuzhiyun 	if (send) {
632*4882a593Smuzhiyun 		/* find existing or alloc new IP/MAC mapping entry */
633*4882a593Smuzhiyun 		if (wet_sta_update_mac(weth,
634*4882a593Smuzhiyun 				(struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), &sta) < 0) {
635*4882a593Smuzhiyun 			DHD_INFO(("wet_dhcpc_proc: unable to update STA %s\n",
636*4882a593Smuzhiyun 				bcm_ether_ntoa(
637*4882a593Smuzhiyun 				(struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), eabuf)));
638*4882a593Smuzhiyun 			return -1;
639*4882a593Smuzhiyun 		}
640*4882a593Smuzhiyun 		bcopy(dhcp + DHCP_FLAGS_OFFSET, &flags, DHCP_FLAGS_LEN);
641*4882a593Smuzhiyun 		/* We can always relay the host's request when we know its MAC addr. */
642*4882a593Smuzhiyun 		if (!ETHER_ISNULLADDR(weth->mac.octet) &&
643*4882a593Smuzhiyun 				!bcmp(dhcp + DHCP_CHADDR_OFFSET, &weth->mac, ETHER_ADDR_LEN)) {
644*4882a593Smuzhiyun 			/* replace chaddr with host's MAC */
645*4882a593Smuzhiyun 			csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
646*4882a593Smuzhiyun 					dhcp + DHCP_CHADDR_OFFSET, ETHER_ADDR_LEN,
647*4882a593Smuzhiyun 					WETHWADDR(weth), ETHER_ADDR_LEN);
648*4882a593Smuzhiyun 			bcopy(WETHWADDR(weth), dhcp + DHCP_CHADDR_OFFSET, ETHER_ADDR_LEN);
649*4882a593Smuzhiyun 			/* force reply to be unicast */
650*4882a593Smuzhiyun 			flags &= ~HTON16(DHCP_FLAG_BCAST);
651*4882a593Smuzhiyun 		}
652*4882a593Smuzhiyun 		/* We can relay other clients' requests when we know the host's IP addr. */
653*4882a593Smuzhiyun 		else if (!IPV4_ADDR_NULL(weth->ip)) {
654*4882a593Smuzhiyun 			/* we can only handle the first hop otherwise drop it */
655*4882a593Smuzhiyun 			if (!IPV4_ADDR_NULL(dhcp + DHCP_GIADDR_OFFSET)) {
656*4882a593Smuzhiyun 				DHD_INFO(("wet_dhcpc_proc: not first hop, ignored\n"));
657*4882a593Smuzhiyun 				return -1;
658*4882a593Smuzhiyun 			}
659*4882a593Smuzhiyun 			/* replace giaddr with host's IP */
660*4882a593Smuzhiyun 			csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
661*4882a593Smuzhiyun 					dhcp + DHCP_GIADDR_OFFSET, IPV4_ADDR_LEN,
662*4882a593Smuzhiyun 					weth->ip, IPV4_ADDR_LEN);
663*4882a593Smuzhiyun 			bcopy(weth->ip, dhcp + DHCP_GIADDR_OFFSET, IPV4_ADDR_LEN);
664*4882a593Smuzhiyun 			/* force reply to be unicast */
665*4882a593Smuzhiyun 			flags &= ~HTON16(DHCP_FLAG_BCAST);
666*4882a593Smuzhiyun 		}
667*4882a593Smuzhiyun 		/*
668*4882a593Smuzhiyun 		 * Request comes in when we don't know the host's MAC and/or IP
669*4882a593Smuzhiyun 		 * addresses hence we can't relay the request. We must notify the
670*4882a593Smuzhiyun 		 * server of our addressing limitation by turning on the broadcast
671*4882a593Smuzhiyun 		 * bit at this point as what the function comments point out.
672*4882a593Smuzhiyun 		 */
673*4882a593Smuzhiyun 		else
674*4882a593Smuzhiyun 			flags |= HTON16(DHCP_FLAG_BCAST);
675*4882a593Smuzhiyun 		/* update flags */
676*4882a593Smuzhiyun 		bcopy(dhcp + DHCP_FLAGS_OFFSET, sta->flags, DHCP_FLAGS_LEN);
677*4882a593Smuzhiyun 		if (flags != *(uint16 *)sta->flags) {
678*4882a593Smuzhiyun 			csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
679*4882a593Smuzhiyun 					dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN,
680*4882a593Smuzhiyun 					(uint8 *)&flags, DHCP_FLAGS_LEN);
681*4882a593Smuzhiyun 			bcopy((uint8 *)&flags, dhcp + DHCP_FLAGS_OFFSET,
682*4882a593Smuzhiyun 					DHCP_FLAGS_LEN);
683*4882a593Smuzhiyun 		}
684*4882a593Smuzhiyun 		/* replace the Ethernet source MAC with ours */
685*4882a593Smuzhiyun 		bcopy(WETHWADDR(weth), eh + ETHER_SRC_OFFSET, ETHER_ADDR_LEN);
686*4882a593Smuzhiyun 	}
687*4882a593Smuzhiyun 	/* relay recv'd reply to its destiny */
688*4882a593Smuzhiyun 	else if (!IPV4_ADDR_NULL(weth->ip) &&
689*4882a593Smuzhiyun 			!bcmp(dhcp + DHCP_GIADDR_OFFSET, weth->ip, IPV4_ADDR_LEN)) {
690*4882a593Smuzhiyun 		/* find IP/MAC mapping entry */
691*4882a593Smuzhiyun 		if (wet_sta_find_mac(weth,
692*4882a593Smuzhiyun 		(struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), &sta) < 0) {
693*4882a593Smuzhiyun 			DHD_INFO(("wet_dhcpc_proc: unable to find STA %s\n",
694*4882a593Smuzhiyun 				bcm_ether_ntoa(
695*4882a593Smuzhiyun 				(struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), eabuf)));
696*4882a593Smuzhiyun 			return -1;
697*4882a593Smuzhiyun 		}
698*4882a593Smuzhiyun 		/*
699*4882a593Smuzhiyun 		 * XXX the following code works for the first hop only
700*4882a593Smuzhiyun 		 */
701*4882a593Smuzhiyun 		/* restore the DHCP giaddr with its original */
702*4882a593Smuzhiyun 		csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
703*4882a593Smuzhiyun 				dhcp + DHCP_GIADDR_OFFSET, IPV4_ADDR_LEN,
704*4882a593Smuzhiyun 				ipv4_null, IPV4_ADDR_LEN);
705*4882a593Smuzhiyun 		bcopy(ipv4_null, dhcp + DHCP_GIADDR_OFFSET, IPV4_ADDR_LEN);
706*4882a593Smuzhiyun 		/* restore the original client's dhcp flags */
707*4882a593Smuzhiyun 		if (bcmp(dhcp + DHCP_FLAGS_OFFSET, sta->flags, DHCP_FLAGS_LEN)) {
708*4882a593Smuzhiyun 			csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
709*4882a593Smuzhiyun 					dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN,
710*4882a593Smuzhiyun 					sta->flags, DHCP_FLAGS_LEN);
711*4882a593Smuzhiyun 			bcopy(sta->flags, dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN);
712*4882a593Smuzhiyun 		}
713*4882a593Smuzhiyun 		/* replace the dest UDP port with DHCP client port */
714*4882a593Smuzhiyun 		port = HTON16(DHCP_PORT_CLIENT);
715*4882a593Smuzhiyun 		csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
716*4882a593Smuzhiyun 				udph + UDP_DEST_PORT_OFFSET, UDP_PORT_LEN,
717*4882a593Smuzhiyun 				(uint8 *)&port, UDP_PORT_LEN);
718*4882a593Smuzhiyun 		bcopy((uint8 *)&port, udph + UDP_DEST_PORT_OFFSET, UDP_PORT_LEN);
719*4882a593Smuzhiyun 		/* replace the dest MAC & IP addr with the client's */
720*4882a593Smuzhiyun 		if (*(uint16 *)sta->flags & HTON16(DHCP_FLAG_BCAST)) {
721*4882a593Smuzhiyun 			ipv4 = ipv4_bcast;
722*4882a593Smuzhiyun 			ether = &ether_bcast;
723*4882a593Smuzhiyun 		}
724*4882a593Smuzhiyun 		else {
725*4882a593Smuzhiyun 			ipv4 = dhcp + DHCP_YIADDR_OFFSET;
726*4882a593Smuzhiyun 			ether = &sta->mac;
727*4882a593Smuzhiyun 		}
728*4882a593Smuzhiyun 		csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
729*4882a593Smuzhiyun 				iph + IPV4_DEST_IP_OFFSET, IPV4_ADDR_LEN,
730*4882a593Smuzhiyun 				ipv4, IPV4_ADDR_LEN);
731*4882a593Smuzhiyun 		csum_fixup_16(iph + IPV4_CHKSUM_OFFSET,
732*4882a593Smuzhiyun 				iph + IPV4_DEST_IP_OFFSET, IPV4_ADDR_LEN,
733*4882a593Smuzhiyun 				ipv4, IPV4_ADDR_LEN);
734*4882a593Smuzhiyun 		bcopy(ipv4, iph + IPV4_DEST_IP_OFFSET, IPV4_ADDR_LEN);
735*4882a593Smuzhiyun 		bcopy(ether, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
736*4882a593Smuzhiyun 	}
737*4882a593Smuzhiyun 	/* it should not recv non-relay reply at all, but just in case */
738*4882a593Smuzhiyun 	else {
739*4882a593Smuzhiyun 		DHD_INFO(("wet_dhcpc_proc: ignore recv'd frame from %s\n",
740*4882a593Smuzhiyun 		bcm_ether_ntoa((struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), eabuf)));
741*4882a593Smuzhiyun 		return -1;
742*4882a593Smuzhiyun 	}
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 	/* no further processing! */
745*4882a593Smuzhiyun 	return 1;
746*4882a593Smuzhiyun }
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun /* process DHCP server frame (server to client) */
749*4882a593Smuzhiyun /*
750*4882a593Smuzhiyun  * Return:
751*4882a593Smuzhiyun  *	= 0 if frame is done ok
752*4882a593Smuzhiyun  *	< 0 if unable to handle the frame
753*4882a593Smuzhiyun  *      > 0 if no further process
754*4882a593Smuzhiyun  */
755*4882a593Smuzhiyun static int
BCMFASTPATH(wet_dhcps_proc)756*4882a593Smuzhiyun BCMFASTPATH(wet_dhcps_proc)(dhd_wet_info_t *weth,
757*4882a593Smuzhiyun 	uint8 *eh, uint8 *iph, uint8 *udph, uint8 *dhcp, int length, int send)
758*4882a593Smuzhiyun {
759*4882a593Smuzhiyun 	wet_sta_t *sta;
760*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
761*4882a593Smuzhiyun 	BCM_REFERENCE(eabuf);
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 	/*
764*4882a593Smuzhiyun 	 * FIXME: validate DHCP body:
765*4882a593Smuzhiyun 	 *  htype Ethernet 1, hlen Ethernet 6, frame length at least 242.
766*4882a593Smuzhiyun 	 */
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun 	/* only interested in replies when receiving from server */
769*4882a593Smuzhiyun 	if (send || *(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REPLY)
770*4882a593Smuzhiyun 		return 0;
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun 	/* find IP/MAC mapping entry */
773*4882a593Smuzhiyun 	if (wet_sta_find_mac(weth, (struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), &sta) < 0) {
774*4882a593Smuzhiyun 		DHD_INFO(("wet_dhcps_proc: unable to find STA %s\n",
775*4882a593Smuzhiyun 		bcm_ether_ntoa((struct ether_addr*)(dhcp + DHCP_CHADDR_OFFSET), eabuf)));
776*4882a593Smuzhiyun 		return -1;
777*4882a593Smuzhiyun 	}
778*4882a593Smuzhiyun 	/* relay the reply to the host when we know the host's MAC addr */
779*4882a593Smuzhiyun 	if (!ETHER_ISNULLADDR(weth->mac.octet) &&
780*4882a593Smuzhiyun 			!bcmp(dhcp + DHCP_CHADDR_OFFSET, WETHWADDR(weth), ETHER_ADDR_LEN)) {
781*4882a593Smuzhiyun 		csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
782*4882a593Smuzhiyun 				dhcp + DHCP_CHADDR_OFFSET, ETHER_ADDR_LEN,
783*4882a593Smuzhiyun 				weth->mac.octet, ETHER_ADDR_LEN);
784*4882a593Smuzhiyun 		bcopy(&weth->mac, dhcp + DHCP_CHADDR_OFFSET, ETHER_ADDR_LEN);
785*4882a593Smuzhiyun 	}
786*4882a593Smuzhiyun 	/* restore the original client's dhcp flags if necessary */
787*4882a593Smuzhiyun 	if (bcmp(dhcp + DHCP_FLAGS_OFFSET, sta->flags, DHCP_FLAGS_LEN)) {
788*4882a593Smuzhiyun 		csum_fixup_16(udph + UDP_CHKSUM_OFFSET,
789*4882a593Smuzhiyun 				dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN,
790*4882a593Smuzhiyun 				sta->flags, DHCP_FLAGS_LEN);
791*4882a593Smuzhiyun 		bcopy(sta->flags, dhcp + DHCP_FLAGS_OFFSET, DHCP_FLAGS_LEN);
792*4882a593Smuzhiyun 	}
793*4882a593Smuzhiyun 	/* replace the dest MAC with that of client's */
794*4882a593Smuzhiyun 	if (*(uint16 *)sta->flags & HTON16(DHCP_FLAG_BCAST))
795*4882a593Smuzhiyun 		bcopy((const uint8 *)&ether_bcast, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
796*4882a593Smuzhiyun 	else
797*4882a593Smuzhiyun 		bcopy(&sta->mac, eh + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	/* no further processing! */
800*4882a593Smuzhiyun 	return 1;
801*4882a593Smuzhiyun }
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun /* alloc IP/MAC mapping entry
804*4882a593Smuzhiyun  * Returns 0 if succeeded; < 0 otherwise.
805*4882a593Smuzhiyun  */
806*4882a593Smuzhiyun static int
wet_sta_alloc(dhd_wet_info_t * weth,wet_sta_t ** saddr)807*4882a593Smuzhiyun wet_sta_alloc(dhd_wet_info_t *weth, wet_sta_t **saddr)
808*4882a593Smuzhiyun {
809*4882a593Smuzhiyun 	wet_sta_t *sta;
810*4882a593Smuzhiyun 
811*4882a593Smuzhiyun 	/* allocate a new one */
812*4882a593Smuzhiyun 	if (!weth->stafree) {
813*4882a593Smuzhiyun 		DHD_INFO(("wet_sta_alloc: no room for another STA\n"));
814*4882a593Smuzhiyun 		return -1;
815*4882a593Smuzhiyun 	}
816*4882a593Smuzhiyun 	sta = weth->stafree;
817*4882a593Smuzhiyun 	weth->stafree = sta->next;
818*4882a593Smuzhiyun 
819*4882a593Smuzhiyun 	/* init them just in case */
820*4882a593Smuzhiyun 	sta->next = NULL;
821*4882a593Smuzhiyun 	sta->next_ip = NULL;
822*4882a593Smuzhiyun 	sta->next_mac = NULL;
823*4882a593Smuzhiyun 
824*4882a593Smuzhiyun 	*saddr = sta;
825*4882a593Smuzhiyun 	return 0;
826*4882a593Smuzhiyun }
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun /* update IP/MAC mapping entry and hash
829*4882a593Smuzhiyun  * Returns 0 if succeeded; < 0 otherwise.
830*4882a593Smuzhiyun  */
831*4882a593Smuzhiyun static int
BCMFASTPATH(wet_sta_update_all)832*4882a593Smuzhiyun BCMFASTPATH(wet_sta_update_all)(dhd_wet_info_t *weth, uint8 *iaddr, struct ether_addr *eaddr,
833*4882a593Smuzhiyun 		wet_sta_t **saddr)
834*4882a593Smuzhiyun {
835*4882a593Smuzhiyun 	wet_sta_t *sta;
836*4882a593Smuzhiyun 	int i;
837*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
838*4882a593Smuzhiyun 	BCM_REFERENCE(eabuf);
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun 	/* find the existing one and remove it from the old IP hash link */
841*4882a593Smuzhiyun 	if (!wet_sta_find_mac(weth, eaddr, &sta)) {
842*4882a593Smuzhiyun 		i = WET_STA_HASH_IP(sta->ip);
843*4882a593Smuzhiyun 		if (bcmp(sta->ip, iaddr, IPV4_ADDR_LEN)) {
844*4882a593Smuzhiyun 			wet_sta_t *sta2, **next;
845*4882a593Smuzhiyun 			for (next = &weth->stahash_ip[i], sta2 = *next;
846*4882a593Smuzhiyun 			sta2; sta2 = sta2->next_ip) {
847*4882a593Smuzhiyun 				if (sta2 == sta)
848*4882a593Smuzhiyun 					break;
849*4882a593Smuzhiyun 				next = &sta2->next_ip;
850*4882a593Smuzhiyun 			}
851*4882a593Smuzhiyun 			if (sta2) {
852*4882a593Smuzhiyun 				*next = sta2->next_ip;
853*4882a593Smuzhiyun 				sta2->next_ip = NULL;
854*4882a593Smuzhiyun 			}
855*4882a593Smuzhiyun 			i = WET_STA_HASH_UNK;
856*4882a593Smuzhiyun 		}
857*4882a593Smuzhiyun 	}
858*4882a593Smuzhiyun 	/* allocate a new one and hash it by MAC */
859*4882a593Smuzhiyun 	else if (!wet_sta_alloc(weth, &sta)) {
860*4882a593Smuzhiyun 		i = WET_STA_HASH_MAC(eaddr->octet);
861*4882a593Smuzhiyun 		bcopy(eaddr, &sta->mac, ETHER_ADDR_LEN);
862*4882a593Smuzhiyun 		sta->next_mac = weth->stahash_mac[i];
863*4882a593Smuzhiyun 		weth->stahash_mac[i] = sta;
864*4882a593Smuzhiyun 		i = WET_STA_HASH_UNK;
865*4882a593Smuzhiyun 	}
866*4882a593Smuzhiyun 	/* bail out if we can't find nor create any */
867*4882a593Smuzhiyun 	else {
868*4882a593Smuzhiyun 		DHD_INFO(("wet_sta_update_all: unable to alloc STA %u.%u.%u.%u %s\n",
869*4882a593Smuzhiyun 		iaddr[0], iaddr[1], iaddr[2], iaddr[3],
870*4882a593Smuzhiyun 		bcm_ether_ntoa(eaddr, eabuf)));
871*4882a593Smuzhiyun 		return -1;
872*4882a593Smuzhiyun 	}
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	/* update IP and hash by new IP */
875*4882a593Smuzhiyun 	if (i == WET_STA_HASH_UNK) {
876*4882a593Smuzhiyun 		i = WET_STA_HASH_IP(iaddr);
877*4882a593Smuzhiyun 		bcopy(iaddr, sta->ip, IPV4_ADDR_LEN);
878*4882a593Smuzhiyun 		sta->next_ip = weth->stahash_ip[i];
879*4882a593Smuzhiyun 		weth->stahash_ip[i] = sta;
880*4882a593Smuzhiyun 
881*4882a593Smuzhiyun 		/* start here and look for other entries with same IP address */
882*4882a593Smuzhiyun 		{
883*4882a593Smuzhiyun 			wet_sta_t *sta2, *prev;
884*4882a593Smuzhiyun 			prev = sta;
885*4882a593Smuzhiyun 			for (sta2 = sta->next_ip;	sta2; sta2 = sta2->next_ip) {
886*4882a593Smuzhiyun 				/* does this entry have the same IP address? */
887*4882a593Smuzhiyun 				if (!bcmp(sta->ip, sta2->ip, IPV4_ADDR_LEN)) {
888*4882a593Smuzhiyun 					/* sta2 currently points to the entry we need to remove */
889*4882a593Smuzhiyun 					/* fix next pointers */
890*4882a593Smuzhiyun 					prev->next_ip = sta2->next_ip;
891*4882a593Smuzhiyun 					sta2->next_ip = NULL;
892*4882a593Smuzhiyun 					/* now we need to find this guy in the MAC list and
893*4882a593Smuzhiyun 					   remove it from that list too.
894*4882a593Smuzhiyun 					   */
895*4882a593Smuzhiyun 					wet_sta_remove_mac_entry(weth, &sta2->mac);
896*4882a593Smuzhiyun 					/* entry should be completely out of the table now,
897*4882a593Smuzhiyun 					   add it to the free list
898*4882a593Smuzhiyun 					   */
899*4882a593Smuzhiyun 					memset(sta2, 0, sizeof(wet_sta_t));
900*4882a593Smuzhiyun 					sta2->next = weth->stafree;
901*4882a593Smuzhiyun 					weth->stafree = sta2;
902*4882a593Smuzhiyun 
903*4882a593Smuzhiyun 					sta2 = prev;
904*4882a593Smuzhiyun 				}
905*4882a593Smuzhiyun 				prev = sta2;
906*4882a593Smuzhiyun 			}
907*4882a593Smuzhiyun 		}
908*4882a593Smuzhiyun 	}
909*4882a593Smuzhiyun 
910*4882a593Smuzhiyun 	*saddr = sta;
911*4882a593Smuzhiyun 	return 0;
912*4882a593Smuzhiyun }
913*4882a593Smuzhiyun 
914*4882a593Smuzhiyun /* update IP/MAC mapping entry and hash */
915*4882a593Smuzhiyun static int
BCMFASTPATH(wet_sta_update_mac)916*4882a593Smuzhiyun BCMFASTPATH(wet_sta_update_mac)(dhd_wet_info_t *weth, struct ether_addr *eaddr, wet_sta_t **saddr)
917*4882a593Smuzhiyun {
918*4882a593Smuzhiyun 	wet_sta_t *sta;
919*4882a593Smuzhiyun 	int i;
920*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
921*4882a593Smuzhiyun 	BCM_REFERENCE(eabuf);
922*4882a593Smuzhiyun 
923*4882a593Smuzhiyun 	/* find the existing one */
924*4882a593Smuzhiyun 	if (!wet_sta_find_mac(weth, eaddr, &sta))
925*4882a593Smuzhiyun 		;
926*4882a593Smuzhiyun 	/* allocate a new one and hash it */
927*4882a593Smuzhiyun 	else if (!wet_sta_alloc(weth, &sta)) {
928*4882a593Smuzhiyun 		i = WET_STA_HASH_MAC(eaddr->octet);
929*4882a593Smuzhiyun 		bcopy(eaddr, &sta->mac, ETHER_ADDR_LEN);
930*4882a593Smuzhiyun 		sta->next_mac = weth->stahash_mac[i];
931*4882a593Smuzhiyun 		weth->stahash_mac[i] = sta;
932*4882a593Smuzhiyun 	}
933*4882a593Smuzhiyun 	/* bail out if we can't find nor create any */
934*4882a593Smuzhiyun 	else {
935*4882a593Smuzhiyun 		DHD_INFO(("wet_sta_update_mac: unable to alloc STA %s\n",
936*4882a593Smuzhiyun 		bcm_ether_ntoa(eaddr, eabuf)));
937*4882a593Smuzhiyun 		return -1;
938*4882a593Smuzhiyun 	}
939*4882a593Smuzhiyun 
940*4882a593Smuzhiyun 	*saddr = sta;
941*4882a593Smuzhiyun 	return 0;
942*4882a593Smuzhiyun }
943*4882a593Smuzhiyun 
944*4882a593Smuzhiyun /*  Remove MAC entry from hash list
945*4882a593Smuzhiyun  *  NOTE:  This only removes the entry matching "eaddr" from the MAC
946*4882a593Smuzhiyun  *  list.  The caller needs to remove from the IP list and
947*4882a593Smuzhiyun  *  put back onto the free list to completely remove the entry
948*4882a593Smuzhiyun  *  from the WET table.
949*4882a593Smuzhiyun  */
950*4882a593Smuzhiyun static int
BCMFASTPATH(wet_sta_remove_mac_entry)951*4882a593Smuzhiyun BCMFASTPATH(wet_sta_remove_mac_entry)(dhd_wet_info_t *weth, struct ether_addr *eaddr)
952*4882a593Smuzhiyun {
953*4882a593Smuzhiyun 	wet_sta_t *sta, *prev;
954*4882a593Smuzhiyun 	int i = WET_STA_HASH_MAC(eaddr->octet);
955*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
956*4882a593Smuzhiyun 	int found = 0;
957*4882a593Smuzhiyun 	BCM_REFERENCE(eabuf);
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun 	/* find the existing one */
960*4882a593Smuzhiyun 	for (sta = prev = weth->stahash_mac[i]; sta; sta = sta->next_mac) {
961*4882a593Smuzhiyun 		if (!bcmp(&sta->mac, eaddr, ETHER_ADDR_LEN)) {
962*4882a593Smuzhiyun 			found = 1;
963*4882a593Smuzhiyun 			break;
964*4882a593Smuzhiyun 		}
965*4882a593Smuzhiyun 		prev = sta;
966*4882a593Smuzhiyun 	}
967*4882a593Smuzhiyun 
968*4882a593Smuzhiyun 	/* bail out if we can't find */
969*4882a593Smuzhiyun 	if (!found) {
970*4882a593Smuzhiyun 		DHD_INFO(("wet_sta_remove_mac_entry: unable to find STA %s entry\n",
971*4882a593Smuzhiyun 		bcm_ether_ntoa(eaddr, eabuf)));
972*4882a593Smuzhiyun 		return -1;
973*4882a593Smuzhiyun 	}
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun 	/* fix the list */
976*4882a593Smuzhiyun 	if (prev == sta)
977*4882a593Smuzhiyun 		weth->stahash_mac[i] = sta->next_mac; /* removing first entry in this bucket */
978*4882a593Smuzhiyun 	else
979*4882a593Smuzhiyun 		prev->next_mac = sta->next_mac;
980*4882a593Smuzhiyun 
981*4882a593Smuzhiyun 	return 0;
982*4882a593Smuzhiyun }
983*4882a593Smuzhiyun 
984*4882a593Smuzhiyun /* find IP/MAC mapping entry by IP address
985*4882a593Smuzhiyun  * Returns 0 if succeeded; < 0 otherwise.
986*4882a593Smuzhiyun  */
987*4882a593Smuzhiyun static int
BCMFASTPATH(wet_sta_find_ip)988*4882a593Smuzhiyun BCMFASTPATH(wet_sta_find_ip)(dhd_wet_info_t *weth, uint8 *iaddr, wet_sta_t **saddr)
989*4882a593Smuzhiyun {
990*4882a593Smuzhiyun 	int i = WET_STA_HASH_IP(iaddr);
991*4882a593Smuzhiyun 	wet_sta_t *sta;
992*4882a593Smuzhiyun 
993*4882a593Smuzhiyun 	/* find the existing one by IP */
994*4882a593Smuzhiyun 	for (sta = weth->stahash_ip[i]; sta; sta = sta->next_ip) {
995*4882a593Smuzhiyun 		if (bcmp(sta->ip, iaddr, IPV4_ADDR_LEN))
996*4882a593Smuzhiyun 			continue;
997*4882a593Smuzhiyun 		*saddr = sta;
998*4882a593Smuzhiyun 		return 0;
999*4882a593Smuzhiyun 	}
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun 	/* sta has not been learned */
1002*4882a593Smuzhiyun 	DHD_INFO(("wet_sta_find_ip: unable to find STA %u.%u.%u.%u\n",
1003*4882a593Smuzhiyun 		iaddr[0], iaddr[1], iaddr[2], iaddr[3]));
1004*4882a593Smuzhiyun 	return -1;
1005*4882a593Smuzhiyun }
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun /* find IP/MAC mapping entry by MAC address
1008*4882a593Smuzhiyun  * Returns 0 if succeeded; < 0 otherwise.
1009*4882a593Smuzhiyun  */
1010*4882a593Smuzhiyun static int
BCMFASTPATH(wet_sta_find_mac)1011*4882a593Smuzhiyun BCMFASTPATH(wet_sta_find_mac)(dhd_wet_info_t *weth, struct ether_addr *eaddr, wet_sta_t **saddr)
1012*4882a593Smuzhiyun {
1013*4882a593Smuzhiyun 	int i = WET_STA_HASH_MAC(eaddr->octet);
1014*4882a593Smuzhiyun 	wet_sta_t *sta;
1015*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
1016*4882a593Smuzhiyun 	BCM_REFERENCE(eabuf);
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun 	/* find the existing one by MAC */
1019*4882a593Smuzhiyun 	for (sta = weth->stahash_mac[i]; sta; sta = sta->next_mac) {
1020*4882a593Smuzhiyun 		if (bcmp(&sta->mac, eaddr, ETHER_ADDR_LEN))
1021*4882a593Smuzhiyun 			continue;
1022*4882a593Smuzhiyun 		*saddr = sta;
1023*4882a593Smuzhiyun 		return 0;
1024*4882a593Smuzhiyun 	}
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 	/* sta has not been learnt */
1027*4882a593Smuzhiyun 	DHD_INFO(("wet_sta_find_mac: unable to find STA %s\n",
1028*4882a593Smuzhiyun 		bcm_ether_ntoa(eaddr, eabuf)));
1029*4882a593Smuzhiyun 	return -1;
1030*4882a593Smuzhiyun }
1031*4882a593Smuzhiyun 
1032*4882a593Smuzhiyun /* Adjust 16 bit checksum - taken from RFC 3022.
1033*4882a593Smuzhiyun  *
1034*4882a593Smuzhiyun  *   The algorithm below is applicable only for even offsets (i.e., optr
1035*4882a593Smuzhiyun  *   below must be at an even offset from start of header) and even lengths
1036*4882a593Smuzhiyun  *   (i.e., olen and nlen below must be even).
1037*4882a593Smuzhiyun  */
1038*4882a593Smuzhiyun static void
BCMFASTPATH(csum_fixup_16)1039*4882a593Smuzhiyun BCMFASTPATH(csum_fixup_16)(uint8 *chksum, uint8 *optr, int olen, uint8 *nptr, int nlen)
1040*4882a593Smuzhiyun {
1041*4882a593Smuzhiyun 	long x, old, new;
1042*4882a593Smuzhiyun 
1043*4882a593Smuzhiyun 	ASSERT(!((uintptr_t)optr&1) && !(olen&1));
1044*4882a593Smuzhiyun 	ASSERT(!((uintptr_t)nptr&1) && !(nlen&1));
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun 	x = (chksum[0]<< 8)+chksum[1];
1047*4882a593Smuzhiyun 	if (!x)
1048*4882a593Smuzhiyun 		return;
1049*4882a593Smuzhiyun 	x = ~x & 0xFFFF;
1050*4882a593Smuzhiyun 	while (olen)
1051*4882a593Smuzhiyun 	{
1052*4882a593Smuzhiyun 		old = (optr[0]<< 8)+optr[1]; optr += 2;
1053*4882a593Smuzhiyun 		x -= old & 0xffff;
1054*4882a593Smuzhiyun 		if (x <= 0) { x--; x &= 0xffff; }
1055*4882a593Smuzhiyun 		olen -= 2;
1056*4882a593Smuzhiyun 	}
1057*4882a593Smuzhiyun 	while (nlen)
1058*4882a593Smuzhiyun 	{
1059*4882a593Smuzhiyun 		new = (nptr[0]<< 8)+nptr[1]; nptr += 2;
1060*4882a593Smuzhiyun 		x += new & 0xffff;
1061*4882a593Smuzhiyun 		if (x & 0x10000) { x++; x &= 0xffff; }
1062*4882a593Smuzhiyun 		nlen -= 2;
1063*4882a593Smuzhiyun 	}
1064*4882a593Smuzhiyun 	x = ~x & 0xFFFF;
1065*4882a593Smuzhiyun 	chksum[0] = (uint8)(x >> 8); chksum[1] = (uint8)x;
1066*4882a593Smuzhiyun }
1067*4882a593Smuzhiyun 
1068*4882a593Smuzhiyun /* Process frames in transmit direction by replacing source MAC with
1069*4882a593Smuzhiyun  * wireless's and keep track of IP MAC address mapping table.
1070*4882a593Smuzhiyun  * Return:
1071*4882a593Smuzhiyun  *	= 0 if frame is done ok;
1072*4882a593Smuzhiyun  *	< 0 if unable to handle the frame;
1073*4882a593Smuzhiyun  *
1074*4882a593Smuzhiyun  * To avoid other interfaces to see our changes specially
1075*4882a593Smuzhiyun  * changes to broadcast frame which definitely will be seen by
1076*4882a593Smuzhiyun  * other bridged interfaces we must copy the frame to our own
1077*4882a593Smuzhiyun  * buffer, modify it, and then sent it.
1078*4882a593Smuzhiyun  * Return the new sdu in 'new'.
1079*4882a593Smuzhiyun  */
1080*4882a593Smuzhiyun int
BCMFASTPATH(dhd_wet_send_proc)1081*4882a593Smuzhiyun BCMFASTPATH(dhd_wet_send_proc)(void *wet, void *sdu, void **new)
1082*4882a593Smuzhiyun {
1083*4882a593Smuzhiyun 	dhd_wet_info_t *weth = (dhd_wet_info_t *)wet;
1084*4882a593Smuzhiyun 	uint8 *frame = PKTDATA(WETOSH(weth), sdu);
1085*4882a593Smuzhiyun 	int length = PKTLEN(WETOSH(weth), sdu);
1086*4882a593Smuzhiyun 	void *pkt = sdu;
1087*4882a593Smuzhiyun 
1088*4882a593Smuzhiyun 	/*
1089*4882a593Smuzhiyun 	 * FIXME: need to tell if buffer is shared and only
1090*4882a593Smuzhiyun 	 * do copy on shared buffer.
1091*4882a593Smuzhiyun 	 */
1092*4882a593Smuzhiyun 	/*
1093*4882a593Smuzhiyun 	 * copy broadcast/multicast frame to our own packet
1094*4882a593Smuzhiyun 	 * otherwise we will screw up others because we alter
1095*4882a593Smuzhiyun 	 * the frame content.
1096*4882a593Smuzhiyun 	 */
1097*4882a593Smuzhiyun 	if (length < ETHER_HDR_LEN) {
1098*4882a593Smuzhiyun 		DHD_ERROR(("dhd_wet_send_proc: unable to process short frame\n"));
1099*4882a593Smuzhiyun 		return -1;
1100*4882a593Smuzhiyun 	}
1101*4882a593Smuzhiyun 	if (ETHER_ISMULTI(frame)) {
1102*4882a593Smuzhiyun 		length = pkttotlen(WETOSH(weth), sdu);
1103*4882a593Smuzhiyun 		if (!(pkt = PKTGET(WETOSH(weth), length, TRUE))) {
1104*4882a593Smuzhiyun 			DHD_ERROR(("dhd_wet_send_proc: unable to alloc, dropped\n"));
1105*4882a593Smuzhiyun 			return -1;
1106*4882a593Smuzhiyun 		}
1107*4882a593Smuzhiyun 		frame = PKTDATA(WETOSH(weth), pkt);
1108*4882a593Smuzhiyun 		pktcopy(WETOSH(weth), sdu, 0, length, frame);
1109*4882a593Smuzhiyun 		/* Transfer priority */
1110*4882a593Smuzhiyun 		PKTSETPRIO(pkt, PKTPRIO(sdu));
1111*4882a593Smuzhiyun 		PKTFREE(WETOSH(weth), sdu, TRUE);
1112*4882a593Smuzhiyun 		PKTSETLEN(WETOSH(weth), pkt, length);
1113*4882a593Smuzhiyun 	}
1114*4882a593Smuzhiyun 	*new = pkt;
1115*4882a593Smuzhiyun 
1116*4882a593Smuzhiyun 	/* process frame */
1117*4882a593Smuzhiyun 	return wet_eth_proc(weth, sdu, frame, length, 1) < 0 ? -1 : 0;
1118*4882a593Smuzhiyun }
1119*4882a593Smuzhiyun 
1120*4882a593Smuzhiyun /*
1121*4882a593Smuzhiyun  * Process frames in receive direction by replacing destination MAC with
1122*4882a593Smuzhiyun  * the one found in IP MAC address mapping table.
1123*4882a593Smuzhiyun  * Return:
1124*4882a593Smuzhiyun  *	= 0 if frame is done ok;
1125*4882a593Smuzhiyun  *	< 0 if unable to handle the frame;
1126*4882a593Smuzhiyun  */
1127*4882a593Smuzhiyun int
BCMFASTPATH(dhd_wet_recv_proc)1128*4882a593Smuzhiyun BCMFASTPATH(dhd_wet_recv_proc)(void *wet, void *sdu)
1129*4882a593Smuzhiyun {
1130*4882a593Smuzhiyun 	dhd_wet_info_t *weth = (dhd_wet_info_t *)wet;
1131*4882a593Smuzhiyun 	/* process frame */
1132*4882a593Smuzhiyun 	return wet_eth_proc(weth, sdu, PKTDATA(WETOSH(weth), sdu),
1133*4882a593Smuzhiyun 			PKTLEN(WETOSH(weth), sdu), 0) < 0 ? -1 : 0;
1134*4882a593Smuzhiyun }
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun /* Delete WET Database */
1137*4882a593Smuzhiyun void
dhd_wet_sta_delete_list(dhd_pub_t * dhd_pub)1138*4882a593Smuzhiyun dhd_wet_sta_delete_list(dhd_pub_t *dhd_pub)
1139*4882a593Smuzhiyun {
1140*4882a593Smuzhiyun 	wet_sta_t *sta;
1141*4882a593Smuzhiyun 	int i, j;
1142*4882a593Smuzhiyun 	dhd_wet_info_t *weth = dhd_pub->wet_info;
1143*4882a593Smuzhiyun 
1144*4882a593Smuzhiyun 	for (i = 0; i < WET_STA_HASH_SIZE; i ++) {
1145*4882a593Smuzhiyun 		for (sta = weth->stahash_mac[i]; sta; sta = sta->next_mac) {
1146*4882a593Smuzhiyun 			wet_sta_t *sta2, **next;
1147*4882a593Smuzhiyun 			j = WET_STA_HASH_IP(sta->ip);
1148*4882a593Smuzhiyun 			for (next = &weth->stahash_ip[j], sta2 = *next;
1149*4882a593Smuzhiyun 					sta2; sta2 = sta2->next_ip) {
1150*4882a593Smuzhiyun 				if (sta2 == sta)
1151*4882a593Smuzhiyun 					break;
1152*4882a593Smuzhiyun 				next = &sta2->next_ip;
1153*4882a593Smuzhiyun 			}
1154*4882a593Smuzhiyun 			if (sta2) {
1155*4882a593Smuzhiyun 				*next = sta2->next_ip;
1156*4882a593Smuzhiyun 				sta2->next_ip = NULL;
1157*4882a593Smuzhiyun 			}
1158*4882a593Smuzhiyun 			j = WET_STA_HASH_UNK;
1159*4882a593Smuzhiyun 
1160*4882a593Smuzhiyun 			wet_sta_remove_mac_entry(weth, &sta->mac);
1161*4882a593Smuzhiyun 			memset(sta, 0, sizeof(wet_sta_t));
1162*4882a593Smuzhiyun 		}
1163*4882a593Smuzhiyun 	}
1164*4882a593Smuzhiyun }
1165*4882a593Smuzhiyun void
dhd_wet_dump(dhd_pub_t * dhdp,struct bcmstrbuf * b)1166*4882a593Smuzhiyun dhd_wet_dump(dhd_pub_t *dhdp, struct bcmstrbuf *b)
1167*4882a593Smuzhiyun {
1168*4882a593Smuzhiyun 	char eabuf[ETHER_ADDR_STR_LEN];
1169*4882a593Smuzhiyun 	wet_sta_t *sta;
1170*4882a593Smuzhiyun 	int i;
1171*4882a593Smuzhiyun 	dhd_wet_info_t *weth = dhdp->wet_info;
1172*4882a593Smuzhiyun 
1173*4882a593Smuzhiyun 	bcm_bprintf(b, "Host MAC: %s\n", bcm_ether_ntoa(&weth->mac, eabuf));
1174*4882a593Smuzhiyun 	bcm_bprintf(b, "Host IP: %u.%u.%u.%u\n",
1175*4882a593Smuzhiyun 			weth->ip[0], weth->ip[1], weth->ip[2], weth->ip[3]);
1176*4882a593Smuzhiyun 	bcm_bprintf(b, "Entry\tEnetAddr\t\tInetAddr\n");
1177*4882a593Smuzhiyun 	for (i = 0; i < WET_NUMSTAS; i ++) {
1178*4882a593Smuzhiyun 		/* FIXME: it leaves the last sta entry unfiltered, who cares! */
1179*4882a593Smuzhiyun 		if (weth->sta[i].next)
1180*4882a593Smuzhiyun 			continue;
1181*4882a593Smuzhiyun 		/* format the entry dump */
1182*4882a593Smuzhiyun 		sta = &weth->sta[i];
1183*4882a593Smuzhiyun 		bcm_bprintf(b, "%u\t%s\t%u.%u.%u.%u\n",
1184*4882a593Smuzhiyun 				i, bcm_ether_ntoa(&sta->mac, eabuf),
1185*4882a593Smuzhiyun 				sta->ip[0], sta->ip[1], sta->ip[2], sta->ip[3]);
1186*4882a593Smuzhiyun 	}
1187*4882a593Smuzhiyun }
1188