GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / proxyarp / proxyarp_common.c
blob12ee220bc8d81151bbaf53b76326465499ca1fea
1 /*
2 * Proxy ARP capabilitiy enables an AP to indicate that the non-AP STA will not
3 * receive ARP frames. The Proxy ARP cappability enables the non-AP STA to remain
4 * in power-save for longer period of time.
5 * Original implements in wlc_l2_filter.c for HSPOT2.0. Network Power Save(NPS)
6 * was also defined to support the capability. We abstract core Proxy ARP implement
7 * and moved it from wlc_l2_filter.c to a standalone module to support
8 * dual band usbap.
10 * Copyright (C) 2012, Broadcom Corporation
11 * All Rights Reserved.
13 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
14 * the contents of this file may not be disclosed to third parties, copied
15 * or duplicated in any form, in whole or in part, without the prior
16 * written permission of Broadcom Corporation.
18 * $Id: $
20 #include <typedefs.h>
21 #include <bcmdefs.h>
22 #include <osl.h>
23 #include <bcmutils.h>
24 #include <siutils.h>
25 #include <bcmendian.h>
26 #include <proto/802.11.h>
27 #include <proto/802.3.h>
28 #include <proto/vlan.h>
29 #include <proto/bcmip.h>
30 #include <proto/bcmarp.h>
31 #include <proto/bcmipv6.h>
32 #include <proto/bcmudp.h>
33 #include <proto/bcmdhcp.h>
34 #include <proto/bcmproto.h>
35 #include <proxyarp/proxyarp.h>
37 #define ALIGN_ADJ_BUFLEN 2 /* Adjust for ETHER_HDR_LEN pull in linux */
38 #define EAIP_TUPLE_TIMEOUT 600 * 1000
39 #define EAIP_TUPLE_TABLE_SIZE 256
41 /* This marks the start of a packed structure section. */
42 #include <packed_section_start.h>
43 struct aging_link {
44 struct aging_link *prev;
45 struct aging_link *next;
48 BWL_PRE_PACKED_STRUCT struct eaip_tuple {
49 struct eaip_tuple *next;
50 struct ether_addr ea;
51 struct aging_link alink;
52 uint32 used; /* time stamp */
53 bcm_tlv_t ip;
54 } BWL_POST_PACKED_STRUCT;
55 /* This marks the end of a packed structure section. */
56 #include <packed_section_end.h>
58 typedef struct eaip_tuple eaip_tuple_t;
59 static eaip_tuple_t *eaip_tbl[EAIP_TUPLE_TABLE_SIZE];
60 static struct aging_link *aq_head, *aq_tail; /* aging queue head/tail */
62 static proxyarp_info_t *pa_info;
63 #ifdef BCMDBG
64 static uint8 proxyarp_msglevel = 0xff;
65 #endif /* BCMDBG */
67 #define ND_OPTION_LEN_ETHER 1
69 static inline void
70 aging_queue_enq(struct aging_link *node)
72 node->prev = NULL;
73 node->next = aq_head;
75 if (aq_head != NULL)
76 aq_head->prev = node;
77 aq_head = node;
79 if (aq_tail == NULL)
80 aq_tail = node;
83 static inline void
84 aging_queue_deq(struct aging_link *node)
86 if (node->prev == NULL)
87 aq_head = node->next;
88 else
89 node->prev->next = node->next;
91 if (node->next == NULL)
92 aq_tail = node->prev;
93 else
94 node->next->prev = node->prev;
96 #ifdef BCMDBG
97 static inline uint8 *
98 eaip_str(uint8 *ea, uint8 *ip, uint8 ip_ver, uint8 *buf, uint32 buflen)
100 char eastr[18];
101 char ipstr[64];
103 bzero(eastr, 18);
104 bzero(ipstr, 64);
105 bzero(buf, buflen);
107 bcm_ether_ntoa((struct ether_addr *)ea, eastr);
108 if (ip_ver == IP_VER_4)
109 bcm_ip_ntoa((struct ipv4_addr *)ip, ipstr);
110 else
111 bcm_format_hex(ipstr, ip, 16);
113 snprintf(buf, buflen - 1, "%s(%s)", eastr, ipstr);
115 return buf;
117 #endif /* BCMDBG */
118 static int
119 eaip_add(proxyarp_info_t *pah, uint8 *ea, uint8 *ip, uint8 ip_ver)
121 eaip_tuple_t *node;
122 uint16 idx, ip_len;
124 switch (ip_ver) {
125 case IP_VER_4:
126 if (IPV4_ADDR_NULL(ip) || IPV4_ADDR_BCAST(ip))
127 return BCME_ERROR;
129 idx = ip[IPV4_ADDR_LEN - 1];
130 ip_len = IPV4_ADDR_LEN;
131 break;
132 case IP_VER_6:
133 if (IPV6_ADDR_NULL(ip))
134 return BCME_ERROR;
136 idx = ip[IPV6_ADDR_LEN - 1];
137 ip_len = IPV6_ADDR_LEN;
138 break;
139 default:
140 return BCME_ERROR;
141 break;
144 if ((node = MALLOC(pah->osh, sizeof(eaip_tuple_t) + ip_len)) == NULL) {
145 PROXYARP_ERR("allocating new eaip for ipv%d error !!\n", ip_ver);
146 return BCME_NOMEM;
149 bzero(node, sizeof(eaip_tuple_t) + ip_len);
150 bcopy(ea, &node->ea, ETHER_ADDR_LEN);
151 node->used = OSL_SYSUPTIME();
152 node->ip.id = ip_ver;
153 node->ip.len = ip_len;
154 bcopy(ip, node->ip.data, ip_len);
155 node->next = eaip_tbl[idx];
156 eaip_tbl[idx] = node;
158 pah->count++;
160 /* add to aging queue head */
161 aging_queue_enq(&node->alink);
163 return BCME_OK;
166 static int
167 eaip_del(proxyarp_info_t *pah, uint8 *ea, uint8 *ip, uint8 ip_ver)
169 eaip_tuple_t *prev, *node;
170 uint16 idx, ip_len;
172 prev = NULL;
173 switch (ip_ver) {
174 case IP_VER_4:
175 idx = ip[3];
176 ip_len = IPV4_ADDR_LEN;
177 break;
178 case IP_VER_6:
179 idx = ip[15];
180 ip_len = IPV6_ADDR_LEN;
181 break;
182 default:
183 return BCME_ERROR;
184 break;
187 node = eaip_tbl[idx];
188 while (node) {
189 /* found target */
190 if (node->ip.id == ip_ver &&
191 bcmp(node->ip.data, ip, ip_len) == 0 &&
192 bcmp((uint8 *)&node->ea, ea, ETHER_ADDR_LEN) == 0) {
193 if (prev == NULL)
194 eaip_tbl[idx] = node->next;
195 else
196 prev->next = node->next;
197 break;
199 prev = node;
200 node = node->next;
203 if (node != NULL) {
204 MFREE(pah->osh, node, sizeof(eaip_tuple_t) + ip_len);
205 pah->count--;
208 return BCME_OK;
211 static bool
212 eaip_timeout(eaip_tuple_t *node)
214 uint32 now = OSL_SYSUPTIME();
216 if (now - node->used > EAIP_TUPLE_TIMEOUT)
217 return TRUE;
219 return FALSE;
222 static eaip_tuple_t *
223 eaip_find_by_ip(uint8 *ip, uint8 ip_ver)
225 eaip_tuple_t *node;
226 uint16 ip_len;
228 switch (ip_ver) {
229 case IP_VER_4:
230 node = eaip_tbl[ip[3]];
231 ip_len = IPV4_ADDR_LEN;
232 break;
233 case IP_VER_6:
234 node = eaip_tbl[ip[15]];
235 ip_len = IPV6_ADDR_LEN;
236 break;
237 default:
238 return NULL;
239 break;
242 while (node) {
243 /* found target */
244 if (node->ip.id == ip_ver && bcmp(node->ip.data, ip, ip_len) == 0) {
245 node->used = OSL_SYSUPTIME();
247 /* removed from aging queue and re-add to head of aging queue */
248 aging_queue_deq(&node->alink);
249 aging_queue_enq(&node->alink);
250 break;
252 node = node->next;
255 return node;
258 static int
259 proxyarp_update(uint8 *ea, uint8 *ip, uint8 ip_ver)
261 eaip_tuple_t *node;
262 int ret = BCME_OK;
264 /* basic ether addr check */
265 if (ETHER_ISNULLADDR(ea) || ETHER_ISBCAST(ea) || ETHER_ISMULTI(ea)) {
266 PROXYARP_ERR("Invalid Ether addr\n");
267 return BCME_OK;
270 PA_LOCK(pa_info);
271 node = eaip_find_by_ip(ip, ip_ver);
273 if (node != NULL) {
274 if (bcmp(ea, (uint8 *)&node->ea, ETHER_ADDR_LEN) != 0) {
275 /* conflct ip address ! */
276 PROXYARP_ERR("Ipaddr Conflict !!!!\n");
277 ret = BCME_ERROR;
280 else {
281 /* no eaip tuple. we need to add this tuple */
282 if (eaip_add(pa_info, ea, ip, ip_ver) == BCME_OK) {
283 #ifdef BCMDBG
284 char eaip_buf[128];
285 #endif /* BCMDBG */
286 PROXYARP_INFO("Create IPv%d %s\n", ip_ver,
287 eaip_str(ea, ip, ip_ver, eaip_buf, 128));
290 PA_UNLOCK(pa_info);
292 return ret;
295 static void *
296 proxyarp_alloc_reply(osl_t *osh, uint32 pktlen, uint8 *src_ea, uint8 *dst_ea,
297 uint16 ea_type, bool snap, void **p)
299 void *pkt;
300 uint8 *frame;
302 /* adjust pktlen since skb->data is aligned to 2 */
303 pktlen += ALIGN_ADJ_BUFLEN;
305 if ((pkt = PKTGET(osh, pktlen, FALSE)) == NULL) {
306 PROXYARP_ERR("%s %d: PKTGET failed\n", __func__, __LINE__);
307 return NULL;
309 /* adjust for pkt->data aligned */
310 PKTPULL(osh, pkt, ALIGN_ADJ_BUFLEN);
311 frame = PKTDATA(osh, pkt);
313 /* Create 14-byte eth header, plus snap header if applicable */
314 bcopy(src_ea, frame + ETHER_SRC_OFFSET, ETHER_ADDR_LEN);
315 bcopy(dst_ea, frame + ETHER_DEST_OFFSET, ETHER_ADDR_LEN);
316 if (snap) {
317 hton16_ua_store(pktlen, frame + ETHER_TYPE_OFFSET);
318 bcopy(llc_snap_hdr, frame + ETHER_HDR_LEN, SNAP_HDR_LEN);
319 hton16_ua_store(ea_type, frame + ETHER_HDR_LEN + SNAP_HDR_LEN);
320 } else
321 hton16_ua_store(ea_type, frame + ETHER_TYPE_OFFSET);
323 *p = (void *)(frame + ETHER_HDR_LEN + (snap ? SNAP_HDR_LEN + ETHER_TYPE_LEN : 0));
325 return pkt;
328 * Moved from wlc_l2_filter.c
330 static uint8
331 proxyarp_arpreply(osl_t *osh, struct bcmarp *arp_req, bool snap, void **reply)
333 struct bcmarp *arp_reply;
334 eaip_tuple_t *target;
335 void *pkt;
336 uint16 pktlen = ETHER_HDR_LEN + ARP_DATA_LEN + (snap ? SNAP_HDR_LEN + ETHER_TYPE_LEN : 0);
338 /* no valid entry, return */
339 PA_LOCK(pa_info);
340 target = eaip_find_by_ip(arp_req->dst_ip, IP_VER_4);
341 PA_UNLOCK(pa_info);
343 /* no target exist, return */
344 if (target == NULL) {
345 return FRAME_NOP;
348 /* src_ea equals to dst_ea, should be dropped */
349 if (bcmp(arp_req->src_eth, &target->ea, ETHER_ADDR_LEN) == 0) {
350 PROXYARP_INFO("ARP-Req ask for its own ea, avoid reply to these frame\n");
351 return FRAME_DROP;
354 /* Create 42-byte arp-reply data frame */
355 if ((pkt = proxyarp_alloc_reply(osh, pktlen, (uint8 *)&target->ea, arp_req->src_eth,
356 ETHER_TYPE_ARP, snap, (void **)&arp_reply)) == NULL) {
357 return FRAME_NOP;
360 /* construct 28-byte arp-reply data frame */
361 /* copy first 6 bytes as-is; i.e., htype, ptype, hlen, plen */
362 bcopy(arp_req, arp_reply, ARP_OPC_OFFSET);
363 arp_reply->oper = HTON16(ARP_OPC_REPLY);
364 /* Copy dst eth and ip addresses */
365 bcopy(&target->ea, arp_reply->src_eth, ETHER_ADDR_LEN);
366 bcopy(&target->ip.data, arp_reply->src_ip, IPV4_ADDR_LEN);
367 bcopy(arp_req->src_eth, arp_reply->dst_eth, ETHER_ADDR_LEN);
368 bcopy(arp_req->src_ip, arp_reply->dst_ip, IPV4_ADDR_LEN);
370 *reply = (void *)pkt;
372 return FRAME_TAKEN;
375 * moved from wlc_l2_filter.c. The length of the option including
376 * the type and length fields in units of 8 octets
378 static bcm_tlv_t *
379 parse_nd_options(void *buf, int buflen, uint key)
381 bcm_tlv_t *elt;
382 int totlen;
384 elt = (bcm_tlv_t*)buf;
385 totlen = buflen;
387 /* find tagged parameter */
388 while (totlen >= TLV_HDR_LEN) {
389 int len = elt->len * 8;
391 /* validate remaining totlen */
392 if ((elt->id == key) &&
393 (totlen >= len))
394 return (elt);
396 elt = (bcm_tlv_t*)((uint8*)elt + len);
397 totlen -= len;
400 return NULL;
403 * moved from wlc_l2_filter.c
405 static uint16
406 calc_checksum(uint8 *src_ipa, uint8 *dst_ipa, uint32 ul_len, uint8 prot, uint8 *ul_data)
408 uint16 *startpos;
409 uint32 sum = 0;
410 int i;
411 uint16 answer = 0;
413 if (src_ipa) {
414 uint8 ph[8];
415 for (i = 0; i < (IPV6_ADDR_LEN / 2); i++) {
416 sum += *((uint16 *)src_ipa);
417 src_ipa += 2;
420 for (i = 0; i < (IPV6_ADDR_LEN / 2); i++) {
421 sum += *((uint16 *)dst_ipa);
422 dst_ipa += 2;
425 *((uint32 *)ph) = hton32(ul_len);
426 *((uint32 *)(ph+4)) = 0;
427 ph[7] = prot;
428 startpos = (uint16 *)ph;
429 for (i = 0; i < 4; i++) {
430 sum += *startpos++;
434 startpos = (uint16 *)ul_data;
435 while (ul_len > 1) {
436 sum += *startpos++;
437 ul_len -= 2;
440 if (ul_len == 1) {
441 *((uint8 *)(&answer)) = *((uint8 *)startpos);
442 sum += answer;
445 sum = (sum >> 16) + (sum & 0xffff);
446 sum += (sum >> 16);
447 answer = ~sum;
449 return answer;
452 * moved from wlc_l2_filter.c
454 static uint8
455 proxyarp_nareply(osl_t *osh, uint8 *data, bool snap, uint8 *src_mac, uint8 dup_ip, void **reply)
457 void *pkt;
458 uint8 *na;
459 uint16 pktlen = ETHER_HDR_LEN + NEIGHBOR_ADVERTISE_DATA_LEN +
460 (snap ? SNAP_HDR_LEN + ETHER_TYPE_LEN: 0);
461 uint16 checksum;
462 eaip_tuple_t *target;
463 uint8 ipv6_mcast_allnode[16] = {0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
464 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1};
466 /* no valid entry, return */
467 PA_LOCK(pa_info);
468 target = eaip_find_by_ip(data + NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET, IP_VER_6);
469 PA_UNLOCK(pa_info);
471 if (target == NULL) {
472 return FRAME_NOP;
475 if (bcmp(src_mac, (uint8 *)&target->ea, 6) == 0) {
476 return FRAME_DROP;
479 /* Create 72 bytes neighbor advertisement data frame */
480 if ((pkt = proxyarp_alloc_reply(osh, pktlen, (uint8 *)&target->ea, src_mac,
481 ETHER_TYPE_IPV6, snap, (void **)&na)) == NULL) {
482 return FRAME_NOP;
485 /* construct 40 bytes ipv6 header */
486 bcopy(data, na, IPV6_SRC_IP_OFFSET);
487 hton16_ua_store((NEIGHBOR_ADVERTISE_DATA_LEN - NEIGHBOR_ADVERTISE_TYPE_OFFSET),
488 (na + IPV6_PAYLOAD_LEN_OFFSET));
489 *(na + IPV6_HOP_LIMIT_OFFSET) = 255;
490 bcopy(data+NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET, na+IPV6_SRC_IP_OFFSET, IPV6_ADDR_LEN);
492 if (dup_ip)
493 bcopy(ipv6_mcast_allnode, na + IPV6_DEST_IP_OFFSET, IPV6_ADDR_LEN);
494 else
495 bcopy(data+IPV6_SRC_IP_OFFSET, na+IPV6_DEST_IP_OFFSET, IPV6_ADDR_LEN);
497 /* Create 32 bytes icmpv6 NA frame body */
498 bzero((na + NEIGHBOR_ADVERTISE_TYPE_OFFSET),
499 (NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET - NEIGHBOR_ADVERTISE_TYPE_OFFSET));
500 *(na + NEIGHBOR_ADVERTISE_TYPE_OFFSET) = NEIGHBOR_ADVERTISE_TYPE;
501 *(na + NEIGHBOR_ADVERTISE_FLAGS_OFFSET) = NEIGHBOR_ADVERTISE_FLAGS_VALUE;
502 bcopy(data+NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET, na+NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET,
503 IPV6_ADDR_LEN);
504 *(na + NEIGHBOR_ADVERTISE_OPTION_OFFSET) = OPT_TYPE_TGT_LINK_ADDR;
505 *(na + NEIGHBOR_ADVERTISE_OPTION_OFFSET + TLV_LEN_OFF) = ND_OPTION_LEN_ETHER;
506 bcopy(&target->ea, na + NEIGHBOR_ADVERTISE_OPTION_OFFSET + TLV_BODY_OFF,
507 ETHER_ADDR_LEN);
509 /* calculate ICMPv6 check sum */
510 checksum = calc_checksum(na+IPV6_SRC_IP_OFFSET, na+IPV6_DEST_IP_OFFSET,
511 NEIGHBOR_ADVERTISE_DATA_LEN - NEIGHBOR_ADVERTISE_TYPE_OFFSET,
512 IP_PROT_ICMP6, na + NEIGHBOR_ADVERTISE_TYPE_OFFSET);
513 *((uint16 *)(na + NEIGHBOR_ADVERTISE_CHECKSUM_OFFSET)) = checksum;
515 *reply = (void *)pkt;
516 return FRAME_TAKEN;
519 static struct {
520 uint8 tid[4];
521 uint8 l2_ea[ETHER_ADDR_LEN];
522 uint8 req_ea[ETHER_ADDR_LEN];
523 } dhcpreq;
525 static void
526 proxyarp_dhcpreq_handle(frame_proto_t *fp)
528 struct bcmudp_hdr *udph;
529 uint8 *dhcp;
530 bcm_tlv_t *msg_type;
531 struct ether_header *eh;
532 uint16 opt_len, offset = DHCP_OPT_OFFSET;
534 udph = (struct bcmudp_hdr *)fp->l4;
536 /* check if the packet is a dhcp-reply */
537 if (ntoh16(udph->dst_port) != DHCP_PORT_SERVER)
538 return;
540 /* we got dhcp-reply. update arp entry */
541 dhcp = (uint8 *)(fp->l4 + UDP_HDR_LEN);
543 /* only process DHCP reply(offer/ack) packets */
544 if (*(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REQUEST)
545 return;
547 /* First option must be magic cookie */
548 if ((dhcp[offset + 0] != 0x63) || (dhcp[offset + 1] != 0x82) ||
549 (dhcp[offset + 2] != 0x53) || (dhcp[offset + 3] != 0x63))
550 return;
552 /* shift to real opt start offst */
553 offset += 4;
554 opt_len = fp->l4_len - offset - UDP_HDR_LEN;
555 msg_type = bcm_parse_tlvs(&dhcp[offset], opt_len, DHCP_OPT_MSGTYPE);
556 if (msg_type == NULL || msg_type->data[0] != DHCP_OPT_MSGTYPE_REQ)
557 return;
559 eh = (struct ether_header *)fp->l2;
560 bcopy(&dhcp[DHCP_TID_OFFSET], dhcpreq.tid, 4);
561 bcopy(eh->ether_shost, dhcpreq.l2_ea, ETHER_ADDR_LEN);
562 bcopy(&dhcp[DHCP_CHADDR_OFFSET], dhcpreq.req_ea, ETHER_ADDR_LEN);
565 static void
566 proxyarp_dhcpack_handle(frame_proto_t *fp)
568 struct bcmudp_hdr *udph;
569 uint8 *dhcp;
570 #ifdef BCMDBG
571 char eaip_buf[128];
572 #endif /* BCMDBG */
573 bcm_tlv_t *msg_type;
574 uint16 opt_len, offset = DHCP_OPT_OFFSET;
576 udph = (struct bcmudp_hdr *)fp->l4;
578 /* check if the packet is a dhcp-reply */
579 if (ntoh16(udph->dst_port) != DHCP_PORT_CLIENT)
580 return;
582 /* we got dhcp-reply. update arp entry */
583 dhcp = (uint8 *)(fp->l4 + UDP_HDR_LEN);
585 /* only process DHCP reply(offer/ack) packets */
586 if (*(dhcp + DHCP_TYPE_OFFSET) != DHCP_TYPE_REPLY)
587 return;
589 if (IPV4_ADDR_NULL(&dhcp[16]))
590 return;
592 /* First option must be magic cookie */
593 if ((dhcp[offset + 0] != 0x63) || (dhcp[offset + 1] != 0x82) ||
594 (dhcp[offset + 2] != 0x53) || (dhcp[offset + 3] != 0x63))
595 return;
597 offset += 4;
598 opt_len = fp->l4_len - offset - UDP_HDR_LEN;
599 msg_type = bcm_parse_tlvs(&dhcp[offset], opt_len, DHCP_OPT_MSGTYPE);
600 if (msg_type == NULL || msg_type->data[0] != DHCP_OPT_MSGTYPE_ACK)
601 return;
603 /* update ipv4 */
605 if (bcmp((dhcp + DHCP_TID_OFFSET), dhcpreq.tid, 4) == 0 &&
606 bcmp((dhcp + DHCP_CHADDR_OFFSET), dhcpreq.req_ea, ETHER_ADDR_LEN) == 0) {
607 PROXYARP_INFO("DHCP_ACK: %s\n", eaip_str(dhcpreq.l2_ea,
608 &dhcp[DHCP_YIADDR_OFFSET], IP_VER_4, eaip_buf, 128));
610 proxyarp_update(dhcpreq.l2_ea, (dhcp + DHCP_YIADDR_OFFSET), IP_VER_4);
612 else {
613 PROXYARP_ERR("DHCP matching error\n");
616 bzero((uint8 *)&dhcpreq, sizeof(dhcpreq));
619 static uint8
620 proxyarp_arp_handle(osl_t *osh, frame_proto_t *fp, void **reply)
622 #ifdef BCMDBG
623 char srceaip_buf[128];
624 char dsteaip_buf[128];
625 #endif /* BCMDBG */
626 struct bcmarp *arp = (struct bcmarp *)fp->l3;
627 uint8 op = ntoh16(arp->oper);
629 if (op > ARP_OPC_REPLY) {
630 PROXYARP_ERR("%s %d: Invalid ARP operation(%d)\n",
631 __func__, __LINE__, op);
632 return FRAME_NOP;
635 /* we proxy ARP request when receiving it */
636 /* update arp info when we receiving arp packet */
637 #ifdef BCMDBG
638 if (op == ARP_OPC_REQUEST)
639 PROXYARP_INFO("ARP_REQ: src %s - dst %s\n",
640 eaip_str(arp->src_eth, arp->src_ip, IP_VER_4, srceaip_buf, 128),
641 eaip_str(arp->dst_eth, arp->dst_ip, IP_VER_4, dsteaip_buf, 128));
642 #endif /* BCMDBG */
644 /* update ipv4 */
645 proxyarp_update((uint8 *)&arp->src_eth, (uint8 *)&arp->src_ip, IP_VER_4);
647 if (op == ARP_OPC_REQUEST) {
648 bool snap = FALSE;
650 if (fp->l2_t == FRAME_L2_SNAP_H || fp->l2_t == FRAME_L2_SNAPVLAN_H)
651 snap = TRUE;
653 return proxyarp_arpreply(osh, arp, snap, reply);
656 return FRAME_NOP;
659 static uint8
660 proxyarp_icmp6_handle(osl_t *osh, frame_proto_t *fp, void **reply)
662 bcm_tlv_t *link_addr;
663 uint16 ip_off;
664 uint8 link_type;
665 uint8 *ea;
666 uint8 dup_ip = 0;
667 uint8 is_null_ip = 0;
668 uint8 *data = fp->l3;
669 uint16 datalen = fp->l3_len;
670 uint8 *frame = fp->l2;
672 if ((datalen < NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET + IPV6_ADDR_LEN) ||
673 (data[IPV6_NEXT_HDR_OFFSET] != IP_PROT_ICMP6))
674 return FRAME_NOP;
676 /* check for ICMPv6 neighbour operation */
677 if (data[NEIGHBOR_ADVERTISE_TYPE_OFFSET] == NEIGHBOR_ADVERTISE_TYPE) {
678 ip_off = NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET;
679 link_type = OPT_TYPE_TGT_LINK_ADDR;
681 else if (data[NEIGHBOR_ADVERTISE_TYPE_OFFSET] == NEIGHBOR_SOLICITATION_TYPE) {
682 link_type = OPT_TYPE_SRC_LINK_ADDR;
683 if (IPV6_ADDR_NULL((uint8)(data + NEIGHBOR_ADVERTISE_SRC_IPV6_OFFSET))) {
684 ip_off = NEIGHBOR_ADVERTISE_TGT_IPV6_OFFSET;
685 is_null_ip = 1;
687 else
688 ip_off = NEIGHBOR_ADVERTISE_SRC_IPV6_OFFSET;
690 else
691 return FRAME_NOP;
693 link_addr = parse_nd_options(data + NEIGHBOR_ADVERTISE_OPTION_OFFSET,
694 datalen-NEIGHBOR_ADVERTISE_OPTION_OFFSET, link_type);
696 if (link_addr && link_addr->len == ND_OPTION_LEN_ETHER)
697 ea = (uint8 *)&link_addr->data;
698 else
699 ea = frame + ETHER_SRC_OFFSET;
701 /* update local address */
702 dup_ip = proxyarp_update(ea, data + ip_off, IP_VER_6);
704 /* duplicate address detection, reply with all-node multicast address */
705 if (is_null_ip) {
706 PROXYARP_INFO("receive DAD(duplicate address detection) frame, dup_ip = %d\n",
707 dup_ip? 1: 0);
710 if (link_type == OPT_TYPE_SRC_LINK_ADDR) {
711 bool snap = FALSE;
712 if (fp->l2_t == FRAME_L2_SNAP_H || fp->l2_t == FRAME_L2_SNAPVLAN_H)
713 snap = TRUE;
715 return proxyarp_nareply(osh, data, snap, frame + ETHER_SRC_OFFSET, dup_ip, reply);
717 else if (link_type == OPT_TYPE_TGT_LINK_ADDR) {
719 else {
720 PROXYARP_ERR("not a src_link or target_link (%x)\n", link_type);
723 return FRAME_NOP;
727 * handle ARP request frame in send path, update database in both send/receive path
729 uint8
730 proxyarp_packets_handle(osl_t *osh, void *sdu, void *fproto, bool send, void **reply)
732 frame_proto_t *fp = (frame_proto_t *)fproto;
733 *reply = NULL;
735 if (send) {
736 /* update ipv4 and reply */
737 if (fp->l3_t == FRAME_L3_ARP_H)
738 return proxyarp_arp_handle(osh, (frame_proto_t *)fproto, reply);
740 /* update ipv6 and reply */
741 if (fp->l4_t == FRAME_L4_ICMP6_H)
742 return proxyarp_icmp6_handle(osh, (frame_proto_t *)fproto, reply);
744 /* update ipv4 when AP sending dhcp-ack frame */
745 if (fp->l4_t == FRAME_L4_UDP_H)
746 proxyarp_dhcpack_handle((frame_proto_t *)fproto);
749 else {
750 /* update eaip_tuple timestamp */
751 if (fp->l3_t == FRAME_L3_IP_H || fp->l3_t == FRAME_L3_IP6_H) {
752 uint8 *ip = (fp->l3_t == FRAME_L3_IP_H)?
753 fp->l3 + IPV4_SRC_IP_OFFSET: fp->l3 + IPV6_SRC_IP_OFFSET;
755 PA_LOCK(pa_info);
756 eaip_find_by_ip(ip, fp->l3_t);
757 PA_UNLOCK(pa_info);
760 /* update ipv4 when AP sending dhcp-req frame */
761 if (fp->l4_t == FRAME_L4_UDP_H)
762 proxyarp_dhcpreq_handle((frame_proto_t *)fproto);
764 return FRAME_NOP;
767 void
768 _proxyarp_watchdog(bool all, uint8 *del_ea)
770 /* clean up database */
771 eaip_tuple_t *cur;
772 uint16 idx, ip_ver;
773 uint8 ea[ETHER_ADDR_LEN];
774 uint8 ip[IPV6_ADDR_LEN];
775 #ifdef BCMDBG
776 uint8 eaip_buf[128];
777 #endif /* BCMDBG */
779 if (pa_info == NULL) {
780 PROXYARP_ERR("Invalid clean-up\n");
781 return;
784 PA_LOCK(pa_info);
786 /* clean all table */
787 if (all == TRUE) {
788 for (idx = 0; idx < EAIP_TUPLE_TABLE_SIZE; idx++) {
789 cur = eaip_tbl[idx];
791 while (cur != NULL) {
792 bcopy((uint8 *)&cur->ea, ea, ETHER_ADDR_LEN);
793 ip_ver = cur->ip.id;
795 if (ip_ver == IP_VER_4) {
796 bcopy((uint8 *)&cur->ip.data, ip, IPV4_ADDR_LEN);
797 ip[IPV4_ADDR_LEN] = 0;
799 else
800 bcopy((uint8 *)&cur->ip.data, ip, IPV6_ADDR_LEN);
802 PROXYARP_INFO("flush IPv%d - %s\n", ip_ver,
803 eaip_str(ea, ip, ip_ver, eaip_buf, 128));
805 cur = cur->next;
807 eaip_del(pa_info, ea, ip, ip_ver);
810 aq_head = aq_tail = NULL;
812 else {
813 struct aging_link *node = aq_tail;
815 while (node) {
816 cur = (eaip_tuple_t *)((void *)node - OFFSETOF(struct eaip_tuple, alink));
818 /* remove specific node if ea exists */
819 if ((del_ea && bcmp(del_ea, (uint8 *)&cur->ea, 6) == 0) ||
820 eaip_timeout(cur) == TRUE) {
821 bcopy((uint8 *)&cur->ea, ea, ETHER_ADDR_LEN);
822 ip_ver = cur->ip.id;
824 if (ip_ver == IP_VER_4) {
825 bcopy((uint8 *)&cur->ip.data, ip, IPV4_ADDR_LEN);
826 ip[IPV4_ADDR_LEN] = 0;
828 else
829 bcopy((uint8 *)&cur->ip.data, ip, IPV6_ADDR_LEN);
831 aging_queue_deq(node);
832 node = node->prev;
834 PROXYARP_INFO("Aging IPv%d - %s\n", ip_ver,
835 eaip_str(ea, ip, ip_ver, eaip_buf, 128));
837 eaip_del(pa_info, ea, ip, ip_ver);
839 else {
840 /* no eaip to aging. Stop */
841 if (del_ea == NULL)
842 break;
843 node = node->prev;
848 PA_UNLOCK(pa_info);
851 bool BCMFASTPATH
852 proxyarp_get(void)
854 return pa_info->enabled? TRUE: FALSE;
857 void
858 proxyarp_set(bool enabled)
860 pa_info->enabled = enabled;
863 void
864 proxyarp_init(proxyarp_info_t *spa_info)
866 if (spa_info == NULL) {
867 PROXYARP_ERR("Init Error: handle is null\n");
868 return;
871 bzero(&eaip_tbl, sizeof(eaip_tbl));
872 aq_head = aq_tail = NULL;
873 pa_info = spa_info;
874 pa_info->enabled = FALSE;
876 return;
879 void
880 proxyarp_deinit(void)
882 _proxyarp_watchdog(TRUE, NULL);
883 aq_head = aq_tail = NULL;
884 bzero(&eaip_tbl, sizeof(eaip_tbl));
885 pa_info = NULL;