• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * L2 Filter handling functions
3  *
4  * Copyright (C) 2020, Broadcom.
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *
21  * <<Broadcom-WL-IPTag/Dual:>>
22  *
23  */
24 #include <bcmutils.h>
25 #include <bcmendian.h>
26 #include <bcmdefs.h>
27 #include <bcmdevs.h>
28 #include <ethernet.h>
29 #include <bcmip.h>
30 #include <bcmipv6.h>
31 #include <bcmudp.h>
32 #include <bcmarp.h>
33 #include <bcmicmp.h>
34 #include <bcmproto.h>
35 #include <bcmdhcp.h>
36 #include <802.11.h>
37 #include <bcm_l2_filter.h>
38 
39 #ifdef BCMDBG_ERR
40 #define	L2_FILTER_ERROR(args)	printf args
41 #else
42 #define	L2_FILTER_ERROR(args)
43 #endif	/* BCMDBG_ERR */
44 
45 #ifdef BCMDBG_MSG
46 #define	L2_FILTER_MSG(args)	printf args
47 #else
48 #define	L2_FILTER_MSG(args)
49 #endif	/* BCMDBG_msg */
50 
51 struct arp_table {
52 	parp_entry_t	*parp_table[BCM_PARP_TABLE_SIZE];   /* proxyarp entries in cache table */
53 	parp_entry_t	*parp_candidate_list;		    /* proxyarp entries in candidate list */
54 	uint8 parp_smac[ETHER_ADDR_LEN];		    /* L2 SMAC from DHCP Req */
55 	uint8 parp_cmac[ETHER_ADDR_LEN];		    /* Bootp Client MAC from DHCP Req */
56 };
57 #ifdef DHD_DUMP_ARPTABLE
58 void bcm_l2_parp_dump_table(arp_table_t* arp_tbl);
59 
60 void
bcm_l2_parp_dump_table(arp_table_t * arp_tbl)61 bcm_l2_parp_dump_table(arp_table_t* arp_tbl)
62 {
63 	parp_entry_t *entry;
64 	uint16 idx, ip_len;
65 	arp_table_t *ptable;
66 	ip_len = IPV4_ADDR_LEN;
67 	ptable = arp_tbl;
68 	for (idx = 0; idx < BCM_PARP_TABLE_SIZE; idx++) {
69 		entry = ptable->parp_table[idx];
70 		while (entry) {
71 			printf("Cached entries..\n");
72 			printf("%d: %d.%d.%d.%d", idx, entry->ip.data[0], entry->ip.data[1],
73 				entry->ip.data[2], entry->ip.data[3]);
74 			printf("%02x:%02x:%02x:%02x:%02x:%02x", entry->ea.octet[0],
75 				entry->ea.octet[1], entry->ea.octet[2], entry->ea.octet[3],
76 				entry->ea.octet[4], entry->ea.octet[5]);
77 			printf("\n");
78 			entry = entry->next;
79 		}
80 	}
81 	entry = ptable->parp_candidate_list;
82 	while (entry) {
83 		printf("Candidate entries..\n");
84 		printf("%d.%d.%d.%d", entry->ip.data[0], entry->ip.data[1],
85 			entry->ip.data[2], entry->ip.data[3]);
86 		printf("%02x:%02x:%02x:%02x:%02x:%02x", entry->ea.octet[0],
87 			entry->ea.octet[1], entry->ea.octet[2], entry->ea.octet[3],
88 			entry->ea.octet[4], entry->ea.octet[5]);
89 
90 		printf("\n");
91 		entry = entry->next;
92 	}
93 }
94 #endif /* DHD_DUMP_ARPTABLE */
95 
init_l2_filter_arp_table(osl_t * osh)96 arp_table_t* init_l2_filter_arp_table(osl_t* osh)
97 {
98 	return ((arp_table_t*)MALLOCZ(osh, sizeof(arp_table_t)));
99 }
100 
deinit_l2_filter_arp_table(osl_t * osh,arp_table_t * ptable)101 void deinit_l2_filter_arp_table(osl_t* osh, arp_table_t* ptable)
102 {
103 	MFREE(osh, ptable, sizeof(arp_table_t));
104 }
105 /* returns 0 if gratuitous ARP or unsolicited neighbour advertisement */
106 int
bcm_l2_filter_gratuitous_arp(osl_t * osh,void * pktbuf)107 bcm_l2_filter_gratuitous_arp(osl_t *osh, void *pktbuf)
108 {
109 	uint8 *frame = PKTDATA(osh, pktbuf);
110 	uint16 ethertype;
111 	int send_ip_offset, target_ip_offset;
112 	int iplen;
113 	int minlen;
114 	uint8 *data;
115 	int datalen;
116 	bool snap;
117 
118 	if (get_pkt_ether_type(osh, pktbuf, &data, &datalen, &ethertype, &snap) != BCME_OK)
119 	    return BCME_ERROR;
120 
121 	if (!ETHER_ISBCAST(frame + ETHER_DEST_OFFSET) &&
122 	    bcmp(&ether_ipv6_mcast, frame + ETHER_DEST_OFFSET, sizeof(ether_ipv6_mcast))) {
123 	    return BCME_ERROR;
124 	}
125 
126 	if (ethertype == ETHER_TYPE_ARP) {
127 		L2_FILTER_MSG(("bcm_l2_filter_gratuitous_arp: ARP RX data : %p: datalen : %d\n",
128 			data, datalen));
129 		send_ip_offset = ARP_SRC_IP_OFFSET;
130 		target_ip_offset = ARP_TGT_IP_OFFSET;
131 		iplen = IPV4_ADDR_LEN;
132 		minlen = ARP_DATA_LEN;
133 	} else if (ethertype == ETHER_TYPE_IPV6) {
134 		send_ip_offset = NEIGHBOR_ADVERTISE_SRC_IPV6_OFFSET;
135 		target_ip_offset = NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET;
136 		iplen = IPV6_ADDR_LEN;
137 		minlen = target_ip_offset + iplen;
138 
139 		/* check for neighbour advertisement */
140 		if (datalen >= minlen && (data[IPV6_NEXT_HDR_OFFSET] != IP_PROT_ICMP6 ||
141 		    data[NEIGHBOR_ADVERTISE_TYPE_OFFSET] != NEIGHBOR_ADVERTISE_TYPE))
142 			return BCME_ERROR;
143 
144 		/* Dont drop Unsolicitated NA fm AP with allnode mcast dest addr (HS2-4.5.E) */
145 		if (datalen >= minlen &&
146 			(data[IPV6_NEXT_HDR_OFFSET] == IP_PROT_ICMP6) &&
147 			(data[NEIGHBOR_ADVERTISE_TYPE_OFFSET] == NEIGHBOR_ADVERTISE_TYPE) &&
148 			(data[NEIGHBOR_ADVERTISE_OPTION_OFFSET] == OPT_TYPE_TGT_LINK_ADDR)) {
149 				L2_FILTER_MSG(("Unsolicitated Neighbour Advertisement from AP "
150 					"with allnode mcast dest addr tx'ed (%d)\n", datalen));
151 				return -1;
152 			}
153 
154 	} else {
155 		return BCME_ERROR;
156 	}
157 
158 	if (datalen < minlen) {
159 		L2_FILTER_MSG(("BCM: dhd_gratuitous_arp: truncated packet (%d)\n", datalen));
160 		return BCME_ERROR;
161 	}
162 
163 	if (bcmp(data + send_ip_offset, data + target_ip_offset, iplen) == 0) {
164 		L2_FILTER_MSG((" returning BCME_OK in bcm_l2_filter_gratuitous_arp\n"));
165 		return BCME_OK;
166 	}
167 
168 	return BCME_ERROR;
169 }
170 int
get_pkt_ether_type(osl_t * osh,void * pktbuf,uint8 ** data_ptr,int * len_ptr,uint16 * et_ptr,bool * snap_ptr)171 get_pkt_ether_type(osl_t *osh, void *pktbuf,
172 	uint8 **data_ptr, int *len_ptr, uint16 *et_ptr, bool *snap_ptr)
173 {
174 	uint8 *frame = PKTDATA(osh, pktbuf);
175 	int length = PKTLEN(osh, pktbuf);
176 	uint8 *pt;			/* Pointer to type field */
177 	uint16 ethertype;
178 	bool snap = FALSE;
179 	/* Process Ethernet II or SNAP-encapsulated 802.3 frames */
180 	if (length < ETHER_HDR_LEN) {
181 		L2_FILTER_MSG(("BCM: get_pkt_ether_type: short eth frame (%d)\n",
182 		           length));
183 		return BCME_ERROR;
184 	} else if (ntoh16_ua(frame + ETHER_TYPE_OFFSET) >= ETHER_TYPE_MIN) {
185 		/* Frame is Ethernet II */
186 		pt = frame + ETHER_TYPE_OFFSET;
187 	} else if (length >= ETHER_HDR_LEN + SNAP_HDR_LEN + ETHER_TYPE_LEN &&
188 	           !bcmp(llc_snap_hdr, frame + ETHER_HDR_LEN, SNAP_HDR_LEN)) {
189 		pt = frame + ETHER_HDR_LEN + SNAP_HDR_LEN;
190 		snap = TRUE;
191 	} else {
192 		L2_FILTER_MSG((" get_pkt_ether_type: non-SNAP 802.3 frame\n"));
193 		return BCME_ERROR;
194 	}
195 
196 	ethertype = ntoh16_ua(pt);
197 
198 	/* Skip VLAN tag, if any */
199 	if (ethertype == ETHER_TYPE_8021Q) {
200 		pt += VLAN_TAG_LEN;
201 
202 		if ((pt + ETHER_TYPE_LEN) > (frame + length)) {
203 			L2_FILTER_MSG(("BCM: get_pkt_ether_type: short VLAN frame (%d)\n",
204 			          length));
205 			return BCME_ERROR;
206 		}
207 		ethertype = ntoh16_ua(pt);
208 	}
209 	*data_ptr = pt + ETHER_TYPE_LEN;
210 	*len_ptr = length - (int32)(pt + ETHER_TYPE_LEN - frame);
211 	*et_ptr = ethertype;
212 	*snap_ptr = snap;
213 	return BCME_OK;
214 }
215 
216 int
get_pkt_ip_type(osl_t * osh,void * pktbuf,uint8 ** data_ptr,int * len_ptr,uint8 * prot_ptr)217 get_pkt_ip_type(osl_t *osh, void *pktbuf,
218 	uint8 **data_ptr, int *len_ptr, uint8 *prot_ptr)
219 {
220 	struct ipv4_hdr *iph;		/* IP frame pointer */
221 	int iplen;			/* IP frame length */
222 	uint16 ethertype, iphdrlen, ippktlen;
223 	uint16 iph_frag;
224 	uint8 prot;
225 	bool snap;
226 
227 	if (get_pkt_ether_type(osh, pktbuf, (uint8 **)&iph,
228 	    &iplen, &ethertype, &snap) != 0)
229 		return BCME_ERROR;
230 
231 	if (ethertype != ETHER_TYPE_IP) {
232 		return BCME_ERROR;
233 	}
234 
235 	/* We support IPv4 only */
236 	if (iplen < IPV4_OPTIONS_OFFSET || (IP_VER(iph) != IP_VER_4)) {
237 		return BCME_ERROR;
238 	}
239 
240 	/* Header length sanity */
241 	iphdrlen = IPV4_HLEN(iph);
242 
243 	/*
244 	 * Packet length sanity; sometimes we receive eth-frame size bigger
245 	 * than the IP content, which results in a bad tcp chksum
246 	 */
247 	ippktlen = ntoh16(iph->tot_len);
248 	if (ippktlen < iplen) {
249 		L2_FILTER_MSG(("get_pkt_ip_type: extra frame length ignored\n"));
250 		iplen = ippktlen;
251 	} else if (ippktlen > iplen) {
252 		L2_FILTER_MSG(("get_pkt_ip_type: truncated IP packet (%d)\n",
253 		           ippktlen - iplen));
254 		return BCME_ERROR;
255 	}
256 
257 	if (iphdrlen < IPV4_OPTIONS_OFFSET || iphdrlen > iplen) {
258 		L2_FILTER_ERROR((" get_pkt_ip_type: IP-header-len (%d) out of range (%d-%d)\n",
259 		           iphdrlen, IPV4_OPTIONS_OFFSET, iplen));
260 		return BCME_ERROR;
261 	}
262 
263 	/*
264 	 * We don't handle fragmented IP packets.  A first frag is indicated by the MF
265 	 * (more frag) bit and a subsequent frag is indicated by a non-zero frag offset.
266 	 */
267 	iph_frag = ntoh16(iph->frag);
268 
269 	if ((iph_frag & IPV4_FRAG_MORE) || (iph_frag & IPV4_FRAG_OFFSET_MASK) != 0) {
270 		L2_FILTER_ERROR(("get_pkt_ip_type: IP fragment not handled\n"));
271 		return BCME_ERROR;
272 	}
273 	prot = IPV4_PROT(iph);
274 	*data_ptr = (((uint8 *)iph) + iphdrlen);
275 	*len_ptr = iplen - iphdrlen;
276 	*prot_ptr = prot;
277 	return BCME_OK;
278 }
279 
280 /* Check if packet type is ICMP ECHO */
bcm_l2_filter_block_ping(osl_t * osh,void * pktbuf)281 int bcm_l2_filter_block_ping(osl_t *osh, void *pktbuf)
282 {
283 	struct bcmicmp_hdr *icmph;
284 	int udpl;
285 	uint8 prot;
286 
287 	if (get_pkt_ip_type(osh, pktbuf, (uint8 **)&icmph, &udpl, &prot) != 0)
288 		return BCME_ERROR;
289 	if (prot == IP_PROT_ICMP) {
290 		if (icmph->type == ICMP_TYPE_ECHO_REQUEST)
291 			return BCME_OK;
292 	}
293 	return BCME_ERROR;
294 }
295 
bcm_l2_filter_get_mac_addr_dhcp_pkt(osl_t * osh,void * pktbuf,int ifidx,uint8 ** mac_addr)296 int bcm_l2_filter_get_mac_addr_dhcp_pkt(osl_t *osh, void *pktbuf,
297 	int ifidx, uint8** mac_addr)
298 {
299 	uint8 *eh = PKTDATA(osh, pktbuf);
300 	uint8 *udph;
301 	uint8 *dhcp;
302 	int udpl;
303 	int dhcpl;
304 	uint16 port;
305 	uint8 prot;
306 
307 	if (!ETHER_ISMULTI(eh + ETHER_DEST_OFFSET))
308 	    return BCME_ERROR;
309 	if (get_pkt_ip_type(osh, pktbuf, &udph, &udpl, &prot) != 0)
310 		return BCME_ERROR;
311 	if (prot != IP_PROT_UDP)
312 		return BCME_ERROR;
313 	/* check frame length, at least UDP_HDR_LEN */
314 	if (udpl < UDP_HDR_LEN) {
315 		L2_FILTER_MSG(("BCM: bcm_l2_filter_get_mac_addr_dhcp_pkt: short UDP frame,"
316 			" ignored\n"));
317 		return BCME_ERROR;
318 	}
319 	port = ntoh16_ua(udph + UDP_DEST_PORT_OFFSET);
320 	/* only process DHCP packets from server to client */
321 	if (port != DHCP_PORT_CLIENT)
322 		return BCME_ERROR;
323 
324 	dhcp = udph + UDP_HDR_LEN;
325 	dhcpl = udpl - UDP_HDR_LEN;
326 
327 	if (dhcpl < DHCP_CHADDR_OFFSET + ETHER_ADDR_LEN) {
328 		L2_FILTER_MSG(("BCM: bcm_l2_filter_get_mac_addr_dhcp_pkt: short DHCP frame,"
329 			" ignored\n"));
330 		return BCME_ERROR;
331 	}
332 	/* only process DHCP reply(offer/ack) packets */
333 	if (*(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REPLY)
334 		return BCME_ERROR;
335 	/* chaddr = dhcp + DHCP_CHADDR_OFFSET; */
336 	*mac_addr = dhcp + DHCP_CHADDR_OFFSET;
337 	return BCME_OK;
338 }
339 /* modify the mac address for IP, in arp table */
340 int
bcm_l2_filter_parp_modifyentry(arp_table_t * arp_tbl,struct ether_addr * ea,uint8 * ip,uint8 ip_ver,bool cached,unsigned int entry_tickcnt)341 bcm_l2_filter_parp_modifyentry(arp_table_t* arp_tbl, struct ether_addr *ea,
342 	uint8 *ip, uint8 ip_ver, bool cached, unsigned int entry_tickcnt)
343 {
344 	parp_entry_t *entry;
345 	uint8 idx, ip_len;
346 	arp_table_t *ptable;
347 
348 	if (ip_ver == IP_VER_4 && !IPV4_ADDR_NULL(ip) && !IPV4_ADDR_BCAST(ip)) {
349 		idx = BCM_PARP_TABLE_INDEX(ip[IPV4_ADDR_LEN - 1]);
350 		ip_len = IPV4_ADDR_LEN;
351 	}
352 	else if (ip_ver == IP_VER_6 && !IPV6_ADDR_NULL(ip)) {
353 		idx = BCM_PARP_TABLE_INDEX(ip[IPV6_ADDR_LEN - 1]);
354 		ip_len = IPV6_ADDR_LEN;
355 	}
356 	else {
357 	    return BCME_ERROR;
358 	}
359 
360 	ptable = arp_tbl;
361 	if (cached) {
362 	    entry = ptable->parp_table[idx];
363 	} else {
364 	    entry = ptable->parp_candidate_list;
365 	}
366 	while (entry) {
367 		if (bcmp(entry->ip.data, ip, ip_len) == 0) {
368 			/* entry matches, overwrite mac content and return */
369 			bcopy((void *)ea, (void *)&entry->ea, ETHER_ADDR_LEN);
370 			entry->used = entry_tickcnt;
371 #ifdef DHD_DUMP_ARPTABLE
372 			bcm_l2_parp_dump_table(arp_tbl);
373 #endif
374 			return BCME_OK;
375 		}
376 		entry = entry->next;
377 	}
378 #ifdef DHD_DUMP_ARPTABLE
379 	bcm_l2_parp_dump_table(arp_tbl);
380 #endif
381 	return BCME_ERROR;
382 }
383 
384 /* Add the IP entry in ARP table based on Cached argument, if cached argument is
385  * non zero positive value: it adds to parp_table, else adds to
386  * parp_candidate_list
387  */
388 int
bcm_l2_filter_parp_addentry(osl_t * osh,arp_table_t * arp_tbl,struct ether_addr * ea,uint8 * ip,uint8 ip_ver,bool cached,unsigned int entry_tickcnt)389 bcm_l2_filter_parp_addentry(osl_t *osh, arp_table_t* arp_tbl, struct ether_addr *ea,
390 	uint8 *ip, uint8 ip_ver, bool cached, unsigned int entry_tickcnt)
391 {
392 	parp_entry_t *entry;
393 	uint8 idx, ip_len;
394 	arp_table_t *ptable;
395 
396 	if (ip_ver == IP_VER_4 && !IPV4_ADDR_NULL(ip) && !IPV4_ADDR_BCAST(ip)) {
397 		idx = BCM_PARP_TABLE_INDEX(ip[IPV4_ADDR_LEN - 1]);
398 		ip_len = IPV4_ADDR_LEN;
399 	}
400 	else if (ip_ver == IP_VER_6 && !IPV6_ADDR_NULL(ip)) {
401 		idx = BCM_PARP_TABLE_INDEX(ip[IPV6_ADDR_LEN - 1]);
402 		ip_len = IPV6_ADDR_LEN;
403 	}
404 	else {
405 	    return BCME_ERROR;
406 	}
407 
408 	if ((entry = MALLOCZ(osh, sizeof(parp_entry_t) + ip_len)) == NULL) {
409 	    L2_FILTER_MSG(("Allocating new parp_entry for IPv%d failed!!\n", ip_ver));
410 	    return BCME_NOMEM;
411 	}
412 
413 	bcopy((void *)ea, (void *)&entry->ea, ETHER_ADDR_LEN);
414 	entry->used = entry_tickcnt;
415 	entry->ip.id = ip_ver;
416 	entry->ip.len = ip_len;
417 	bcopy(ip, entry->ip.data, ip_len);
418 	ptable = arp_tbl;
419 	if (cached) {
420 	    entry->next = ptable->parp_table[idx];
421 	    ptable->parp_table[idx] = entry;
422 	} else {
423 	    entry->next = ptable->parp_candidate_list;
424 	    ptable->parp_candidate_list = entry;
425 	}
426 #ifdef DHD_DUMP_ARPTABLE
427 	bcm_l2_parp_dump_table(arp_tbl);
428 #endif
429 	return BCME_OK;
430 }
431 
432 /* Delete the IP entry in ARP table based on Cached argument, if cached argument is
433  * non zero positive value: it delete from parp_table, else delete from
434  * parp_candidate_list
435  */
436 int
bcm_l2_filter_parp_delentry(osl_t * osh,arp_table_t * arp_tbl,struct ether_addr * ea,uint8 * ip,uint8 ip_ver,bool cached)437 bcm_l2_filter_parp_delentry(osl_t* osh, arp_table_t *arp_tbl, struct ether_addr *ea,
438 	uint8 *ip, uint8 ip_ver, bool cached)
439 {
440 	parp_entry_t *entry, *prev = NULL;
441 	uint8 idx, ip_len;
442 	arp_table_t *ptable;
443 
444 	if (ip_ver == IP_VER_4) {
445 		idx = BCM_PARP_TABLE_INDEX(ip[IPV4_ADDR_LEN - 1]);
446 		ip_len = IPV4_ADDR_LEN;
447 	}
448 	else if (ip_ver == IP_VER_6) {
449 		idx = BCM_PARP_TABLE_INDEX(ip[IPV6_ADDR_LEN - 1]);
450 		ip_len = IPV6_ADDR_LEN;
451 	}
452 	else {
453 	    return BCME_ERROR;
454 	}
455 	ptable = arp_tbl;
456 	if (cached) {
457 	    entry = ptable->parp_table[idx];
458 	} else {
459 		entry = ptable->parp_candidate_list;
460 	}
461 	while (entry) {
462 		if (entry->ip.id == ip_ver &&
463 		    bcmp(entry->ip.data, ip, ip_len) == 0 &&
464 		    bcmp(&entry->ea, ea, ETHER_ADDR_LEN) == 0) {
465 			if (prev == NULL) {
466 			    if (cached) {
467 				ptable->parp_table[idx] = entry->next;
468 			    } else {
469 				ptable->parp_candidate_list = entry->next;
470 			    }
471 			} else {
472 			    prev->next = entry->next;
473 			}
474 			break;
475 		}
476 		prev = entry;
477 		entry = entry->next;
478 	}
479 	if (entry != NULL)
480 		MFREE(osh, entry, sizeof(parp_entry_t) + ip_len);
481 #ifdef DHD_DUMP_ARPTABLE
482 	bcm_l2_parp_dump_table(arp_tbl);
483 #endif
484 	return BCME_OK;
485 }
486 
487 /* search the IP entry in ARP table based on Cached argument, if cached argument is
488  * non zero positive value: it searches from parp_table, else search from
489  * parp_candidate_list
490  */
491 parp_entry_t *
bcm_l2_filter_parp_findentry(arp_table_t * arp_tbl,uint8 * ip,uint8 ip_ver,bool cached,unsigned int entry_tickcnt)492 bcm_l2_filter_parp_findentry(arp_table_t* arp_tbl, uint8 *ip, uint8 ip_ver, bool cached,
493 	unsigned int entry_tickcnt)
494 {
495 	parp_entry_t *entry;
496 	uint8 idx, ip_len;
497 	arp_table_t *ptable;
498 
499 	if (ip_ver == IP_VER_4) {
500 		idx = BCM_PARP_TABLE_INDEX(ip[IPV4_ADDR_LEN - 1]);
501 		ip_len = IPV4_ADDR_LEN;
502 	} else if (ip_ver == IP_VER_6) {
503 		idx = BCM_PARP_TABLE_INDEX(ip[IPV6_ADDR_LEN - 1]);
504 		ip_len = IPV6_ADDR_LEN;
505 	} else {
506 		return NULL;
507 	}
508 	ptable = arp_tbl;
509 	if (cached) {
510 	    entry = ptable->parp_table[idx];
511 	} else {
512 	    entry = ptable->parp_candidate_list;
513 	}
514 	while (entry) {
515 	    if (entry->ip.id == ip_ver && bcmp(entry->ip.data, ip, ip_len) == 0) {
516 			/* time stamp of adding the station entry to arp table for ifp */
517 			entry->used = entry_tickcnt;
518 			break;
519 	    }
520 	    entry = entry->next;
521 	}
522 	return entry;
523 }
524 
525 /* update arp table entries for every proxy arp enable interface */
526 void
bcm_l2_filter_arp_table_update(osl_t * osh,arp_table_t * arp_tbl,bool all,uint8 * del_ea,bool periodic,unsigned int tickcnt)527 bcm_l2_filter_arp_table_update(osl_t *osh, arp_table_t* arp_tbl, bool all, uint8 *del_ea,
528 	bool periodic, unsigned int tickcnt)
529 {
530 	parp_entry_t *prev, *entry, *delentry;
531 	uint8 idx, ip_ver;
532 	struct ether_addr ea;
533 	uint8 ip[IPV6_ADDR_LEN];
534 	arp_table_t *ptable;
535 
536 	ptable = arp_tbl;
537 	for (idx = 0; idx < BCM_PARP_TABLE_SIZE; idx++) {
538 		entry = ptable->parp_table[idx];
539 		while (entry) {
540 			/* check if the entry need to be removed */
541 			if (all || (periodic && BCM_PARP_IS_TIMEOUT(tickcnt, entry)) ||
542 			    (del_ea != NULL && !bcmp(del_ea, &entry->ea, ETHER_ADDR_LEN))) {
543 				/* copy frame here */
544 				ip_ver = entry->ip.id;
545 				bcopy(entry->ip.data, ip, entry->ip.len);
546 				bcopy(&entry->ea, &ea, ETHER_ADDR_LEN);
547 				entry = entry->next;
548 				bcm_l2_filter_parp_delentry(osh, ptable, &ea, ip, ip_ver, TRUE);
549 			}
550 			else {
551 				entry = entry->next;
552 			}
553 		}
554 	}
555 
556 	/* remove candidate or promote to real entry */
557 	prev = delentry = NULL;
558 	entry = ptable->parp_candidate_list;
559 	while (entry) {
560 		/* remove candidate */
561 		if (all || (periodic && BCM_PARP_ANNOUNCE_WAIT_REACH(tickcnt, entry)) ||
562 		    (del_ea != NULL && !bcmp(del_ea, (uint8 *)&entry->ea, ETHER_ADDR_LEN))) {
563 			bool promote = (periodic && BCM_PARP_ANNOUNCE_WAIT_REACH(tickcnt, entry)) ?
564 				TRUE: FALSE;
565 			parp_entry_t *node = NULL;
566 
567 			ip_ver = entry->ip.id;
568 
569 			if (prev == NULL)
570 				ptable->parp_candidate_list = entry->next;
571 			else
572 				prev->next = entry->next;
573 
574 			node = bcm_l2_filter_parp_findentry(ptable,
575 				entry->ip.data, IP_VER_6, TRUE, tickcnt);
576 			if (promote && node == NULL) {
577 				bcm_l2_filter_parp_addentry(osh, ptable, &entry->ea,
578 					entry->ip.data, entry->ip.id, TRUE, tickcnt);
579 			}
580 			MFREE(osh, entry, sizeof(parp_entry_t) + entry->ip.len);
581 			if (prev == NULL) {
582 				entry = ptable->parp_candidate_list;
583 			} else {
584 				entry = prev->next;
585 			}
586 		}
587 		else {
588 			prev = entry;
589 			entry = entry->next;
590 		}
591 	}
592 }
593 /* create 42 byte ARP packet for ARP response, aligned the Buffer */
594 void *
bcm_l2_filter_proxyarp_alloc_reply(osl_t * osh,uint16 pktlen,struct ether_addr * src_ea,struct ether_addr * dst_ea,uint16 ea_type,bool snap,void ** p)595 bcm_l2_filter_proxyarp_alloc_reply(osl_t* osh, uint16 pktlen, struct ether_addr *src_ea,
596 	struct ether_addr *dst_ea, uint16 ea_type, bool snap, void **p)
597 {
598 	void *pkt;
599 	uint8 *frame;
600 
601 	/* adjust pktlen since skb->data is aligned to 2 */
602 	pktlen += ALIGN_ADJ_BUFLEN;
603 
604 	if ((pkt = PKTGET(osh, pktlen, FALSE)) == NULL) {
605 		L2_FILTER_ERROR(("bcm_l2_filter_proxyarp_alloc_reply: PKTGET failed\n"));
606 		return NULL;
607 	}
608 	/* adjust for pkt->data aligned */
609 	PKTPULL(osh, pkt, ALIGN_ADJ_BUFLEN);
610 	frame = PKTDATA(osh, pkt);
611 
612 	/* Create 14-byte eth header, plus snap header if applicable */
613 	bcopy(src_ea, frame + ETHER_SRC_OFFSET, ETHER_ADDR_LEN);
614 	bcopy(dst_ea, frame + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
615 	if (snap) {
616 		hton16_ua_store(pktlen, frame + ETHER_TYPE_OFFSET);
617 		bcopy(llc_snap_hdr, frame + ETHER_HDR_LEN, SNAP_HDR_LEN);
618 		hton16_ua_store(ea_type, frame + ETHER_HDR_LEN + SNAP_HDR_LEN);
619 	} else
620 		hton16_ua_store(ea_type, frame + ETHER_TYPE_OFFSET);
621 
622 	*p = (void *)(frame + ETHER_HDR_LEN + (snap ? SNAP_HDR_LEN + ETHER_TYPE_LEN : 0));
623 	return pkt;
624 }
625 /* copy the smac entry from parp_table */
bcm_l2_filter_parp_get_smac(arp_table_t * ptable,void * smac)626 void bcm_l2_filter_parp_get_smac(arp_table_t* ptable, void* smac)
627 {
628 	bcopy(ptable->parp_smac, smac, ETHER_ADDR_LEN);
629 }
630 /* copy the cmac entry from parp_table */
bcm_l2_filter_parp_get_cmac(arp_table_t * ptable,void * cmac)631 void bcm_l2_filter_parp_get_cmac(arp_table_t* ptable, void* cmac)
632 {
633 	bcopy(ptable->parp_cmac, cmac, ETHER_ADDR_LEN);
634 }
635 /* copy the smac entry to smac entry in parp_table */
bcm_l2_filter_parp_set_smac(arp_table_t * ptable,void * smac)636 void bcm_l2_filter_parp_set_smac(arp_table_t* ptable, void* smac)
637 {
638 	bcopy(smac, ptable->parp_smac, ETHER_ADDR_LEN);
639 }
640 /* copy the cmac entry to cmac entry in parp_table */
bcm_l2_filter_parp_set_cmac(arp_table_t * ptable,void * cmac)641 void bcm_l2_filter_parp_set_cmac(arp_table_t* ptable, void* cmac)
642 {
643 	bcopy(cmac, ptable->parp_cmac, ETHER_ADDR_LEN);
644 }
645 
646 uint16
calc_checksum(uint8 * src_ipa,uint8 * dst_ipa,uint32 ul_len,uint8 prot,uint8 * ul_data)647 calc_checksum(uint8 *src_ipa, uint8 *dst_ipa, uint32 ul_len, uint8 prot, uint8 *ul_data)
648 {
649 	uint16 *startpos;
650 	uint32 sum = 0;
651 	int i;
652 	uint16 answer = 0;
653 
654 	if (src_ipa) {
655 		uint8 ph[8] = {0, };
656 		for (i = 0; i < (IPV6_ADDR_LEN / 2); i++) {
657 			sum += *((uint16 *)src_ipa);
658 			src_ipa += 2;
659 		}
660 
661 		for (i = 0; i < (IPV6_ADDR_LEN / 2); i++) {
662 			sum += *((uint16 *)dst_ipa);
663 			dst_ipa += 2;
664 		}
665 
666 		*((uint32 *)ph) = hton32(ul_len);
667 		*((uint32 *)(ph+4)) = 0;
668 		ph[7] = prot;
669 		startpos = (uint16 *)ph;
670 		for (i = 0; i < 4; i++) {
671 			sum += *startpos++;
672 		}
673 	}
674 
675 	startpos = (uint16 *)ul_data;
676 	while (ul_len > 1) {
677 		sum += *startpos++;
678 		ul_len -= 2;
679 	}
680 
681 	if (ul_len == 1) {
682 		*((uint8 *)(&answer)) = *((uint8 *)startpos);
683 		sum += answer;
684 	}
685 
686 	sum = (sum >> 16) + (sum & 0xffff);
687 	sum += (sum >> 16);
688 	answer = ~sum;
689 
690 	return answer;
691 }
692 /*
693  * The length of the option including
694  * the type and length fields in units of 8 octets
695  */
696 bcm_tlv_t *
parse_nd_options(void * buf,int buflen,uint key)697 parse_nd_options(void *buf, int buflen, uint key)
698 {
699 	bcm_tlv_t *elt;
700 	int totlen;
701 
702 	elt = (bcm_tlv_t*)buf;
703 	totlen = buflen;
704 
705 	/* find tagged parameter */
706 	while (totlen >= TLV_HDR_LEN) {
707 		int len = elt->len * 8;
708 
709 		/* validate remaining totlen */
710 		if ((elt->id == key) &&
711 		    (totlen >= len))
712 			return (elt);
713 
714 		elt = (bcm_tlv_t*)((uint8*)elt + len);
715 		totlen -= len;
716 	}
717 
718 	return NULL;
719 }
720 
721 /* returns 0 if tdls set up request or tdls discovery request */
722 int
bcm_l2_filter_block_tdls(osl_t * osh,void * pktbuf)723 bcm_l2_filter_block_tdls(osl_t *osh, void *pktbuf)
724 {
725 	uint16 ethertype;
726 	uint8 *data;
727 	int datalen;
728 	bool snap;
729 	uint8 action_field;
730 
731 	if (get_pkt_ether_type(osh, pktbuf, &data, &datalen, &ethertype, &snap) != BCME_OK)
732 		return BCME_ERROR;
733 
734 	if (ethertype != ETHER_TYPE_89_0D)
735 		return BCME_ERROR;
736 
737 	/* validate payload type */
738 	if (datalen < TDLS_PAYLOAD_TYPE_LEN + 2) {
739 		L2_FILTER_ERROR(("bcm_l2_filter_block_tdls: wrong length for 89-0d eth frame %d\n",
740 			datalen));
741 		return BCME_ERROR;
742 	}
743 
744 	/* validate payload type */
745 	if (*data != TDLS_PAYLOAD_TYPE) {
746 		L2_FILTER_ERROR(("bcm_l2_filter_block_tdls: wrong payload type for 89-0d"
747 			" eth frame %d\n",
748 			*data));
749 		return BCME_ERROR;
750 	}
751 	data += TDLS_PAYLOAD_TYPE_LEN;
752 
753 	/* validate TDLS action category */
754 	if (*data != TDLS_ACTION_CATEGORY_CODE) {
755 		L2_FILTER_ERROR(("bcm_l2_filter_block_tdls: wrong TDLS Category %d\n", *data));
756 		return BCME_ERROR;
757 	}
758 	data++;
759 
760 	action_field = *data;
761 
762 	if ((action_field == TDLS_SETUP_REQ) || (action_field == TDLS_DISCOVERY_REQ))
763 		return BCME_OK;
764 
765 	return BCME_ERROR;
766 }
767