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, ðertype, &snap) != BCME_OK)
119 return BCME_ERROR;
120
121 if (!ETHER_ISBCAST(frame + ETHER_DEST_OFFSET) &&
122 bcmp(ðer_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, ðertype, &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, ðertype, &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