err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr) { struct pbuf *p; struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; err_t result = ERR_OK; u8_t k; /* ARP entry index */ /* allocate a pbuf for the outgoing ARP request packet */ p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM); /* could allocate a pbuf for an ARP request? */ if (p != NULL) { struct etharp_hdr *hdr = p->payload; LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_request: sending ARP request.\n")); hdr->opcode = htons(ARP_REQUEST); k = netif->hwaddr_len; while(k > 0) { k--; hdr->shwaddr.addr[k] = srcaddr->addr[k]; /* the hardware address is what we ask for, in * a request it is a don't-care value, we use zeroes */ hdr->dhwaddr.addr[k] = 0x00; } hdr->dipaddr = *(struct ip_addr2 *)ipaddr; hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr; hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, netif->hwaddr_len); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); k = netif->hwaddr_len; while(k > 0) { k--; /* broadcast to all network interfaces on the local network */ hdr->ethhdr.dest.addr[k] = 0xff; hdr->ethhdr.src.addr[k] = srcaddr->addr[k]; } hdr->ethhdr.type = htons(ETHTYPE_ARP); /* send ARP query */ result = netif->linkoutput(netif, p); /* free ARP query packet */ pbuf_free(p); p = NULL; /* could not allocate pbuf for ARP request */ } else { result = ERR_MEM; LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_request: could not allocate pbuf for ARP request.\n")); } return result; }
/** * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache * send out queued IP packets. Updates cache with snooped address pairs. * * Should be called for incoming ARP packets. The pbuf in the argument * is freed by this function. * * @param netif The lwIP network interface on which the ARP packet pbuf arrived. * @param pbuf The ARP packet that arrived on netif. Is freed by this function. * @param ethaddr Ethernet address of netif. * * @return NULL * * @see pbuf_free() */ void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) { struct etharp_hdr *hdr; /* these are aligned properly, whereas the ARP header fields might not be */ struct ip_addr sipaddr, dipaddr; u8_t i; u8_t for_us; LWIP_ASSERT("netif != NULL", netif != NULL); /* drop short ARP packets */ if (p->tot_len < sizeof(struct etharp_hdr)) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, sizeof(struct etharp_hdr))); pbuf_free(p); return; } hdr = p->payload; /* get aligned copies of addresses */ *(struct ip_addr2 *)&sipaddr = hdr->sipaddr; *(struct ip_addr2 *)&dipaddr = hdr->dipaddr; /* this interface is not configured? */ if (netif->ip_addr.addr == 0) { for_us = 0; } else { /* ARP packet directed to us? */ for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr)); } /* ARP message directed to us? */ if (for_us) { /* add IP address in ARP cache; assume requester wants to talk to us. * can result in directly sending the queued packets for this host. */ update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD); /* ARP message not directed to us? */ } else { /* update the source IP address in the cache, if present */ update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0); } /* now act on the message itself */ switch (htons(hdr->opcode)) { /* ARP request? */ case ARP_REQUEST: /* ARP request. If it asked for our address, we send out a * reply. In any case, we time-stamp any existing ARP entry, * and possiby send out an IP packet that was queued on it. */ LWIP_DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); /* ARP request for our address? */ if (for_us) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); /* re-use pbuf to send ARP reply */ hdr->opcode = htons(ARP_REPLY); hdr->dipaddr = hdr->sipaddr; hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr; for(i = 0; i < netif->hwaddr_len; ++i) { hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; hdr->shwaddr.addr[i] = ethaddr->addr[i]; hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; } hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, netif->hwaddr_len); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); hdr->ethhdr.type = htons(ETHTYPE_ARP); /* return ARP reply */ netif->linkoutput(netif, p); /* we are not configured? */ } else if (netif->ip_addr.addr == 0) { /* { for_us == 0 and netif->ip_addr.addr == 0 } */ LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); /* request was not directed to us */ } else { /* { for_us == 0 and netif->ip_addr.addr != 0 } */ LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); } break; case ARP_REPLY: /* ARP reply. We already updated the ARP cache earlier. */ LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); #if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) /* DHCP wants to know about ARP replies from any host with an * IP address also offered to us by the DHCP server. We do not * want to take a duplicate IP address on a single network. * @todo How should we handle redundant (fail-over) interfaces? * */ dhcp_arp_reply(netif, &sipaddr); #endif break; default: LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); break; } /* free ARP packet */ pbuf_free(p); }
/*-----------------------------------------------------------------------------------*/ struct pbuf * etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) { struct eth_addr *dest, *srcaddr, mcastaddr; struct eth_hdr *ethhdr; struct etharp_hdr *hdr; struct pbuf *p; u8_t i; srcaddr = (struct eth_addr *)netif->hwaddr; /* Make room for Ethernet header. */ if(pbuf_header(q, sizeof(struct eth_hdr)) != 0) { /* The pbuf_header() call shouldn't fail, and we'll just bail out if it does.. */ DEBUGF(ETHARP_DEBUG, ("etharp_output: could not allocate room for header.\n")); #ifdef LINK_STATS ++stats.link.lenerr; #endif /* LINK_STATS */ return NULL; } dest = NULL; /* Construct Ethernet header. Start with looking up deciding which MAC address to use as a destination address. Broadcasts and multicasts are special, all other addresses are looked up in the ARP table. */ if(ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, &(netif->netmask))) { dest = (struct eth_addr *)ðbroadcast; } else if(ip_addr_ismulticast(ipaddr)) { /* Hash IP multicast address to MAC address. */ mcastaddr.addr[0] = 0x01; mcastaddr.addr[1] = 0x0; mcastaddr.addr[2] = 0x5e; mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; mcastaddr.addr[4] = ip4_addr3(ipaddr); mcastaddr.addr[5] = ip4_addr4(ipaddr); dest = &mcastaddr; } else { #ifdef __PAULOS__ /* abort on insane conditions */ if (!ip_addr_maskcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) return NULL; if (ipaddr->addr == netif->ip_addr.addr) return NULL; #else if(!ip_addr_maskcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) { /* Use the IP address of the default gateway if the destination is NOT on the same subnet as we are. ("NOT" added 20021113 psheer@) */ ipaddr = &(netif->gw); } #endif /* We try to find a stable mapping. */ for(i = 0; i < arp_table_last; ++i) { if((arp_table[i].state == ETHARP_STATE_STABLE || arp_table[i].state == ETHARP_STATE_STATIC) && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { dest = &arp_table[i].ethaddr; #if 0 // FIXME: remove this test code if (!((int) rand() % 2)) { dest = NULL; arp_table[i].state = ETHARP_STATE_EMPTY; if (arp_table[i].p) pbuf_free (arp_table[i].p); arp_table[i].p = NULL; arp_table[i].payload = NULL; arp_table[i].len = arp_table[0].tot_len = 0; } #endif break; } } } if(dest == NULL) { /* No destination address has been found, so we'll have to send out an ARP request for the IP address. The outgoing packet is queued unless the queue is full. */ /* We check if we are already querying for this address. If so, we'll bail out. */ for(i = 0; i < arp_table_last; ++i) { if(arp_table[i].state == ETHARP_STATE_PENDING && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { DEBUGF(ETHARP_DEBUG, ("etharp_output: already queued\n")); return NULL; } } hdr = q->payload; for(i = 0; i < 6; ++i) hdr->ethhdr.src.addr[i] = srcaddr->addr[i]; hdr->ethhdr.type = htons(ETHTYPE_IP); i = etharp_new_entry(q, ipaddr, NULL, ETHARP_STATE_PENDING); /* We allocate a pbuf for the outgoing ARP request packet. */ p = pbuf_alloc(PBUF_RAW, sizeof(struct etharp_hdr) + 2, PBUF_RAM); if(p == NULL) { /* No ARP request packet could be allocated, so we forget about the ARP table entry. */ if(i != ARP_TABLE_SIZE) { arp_table[i].state = ETHARP_STATE_EMPTY; /* We decrease the reference count of the queued pbuf (which now is dequeued). */ DEBUGF(ETHARP_DEBUG, ("etharp_output: couldn't alloc pbuf for query, dequeueing %p\n", q)); } return NULL; } pbuf_header (p, (s16_t) -2); hdr = p->payload; hdr->opcode = htons(ARP_REQUEST); for(i = 0; i < 6; ++i) { hdr->dhwaddr.addr[i] = 0x00; hdr->shwaddr.addr[i] = srcaddr->addr[i]; } memcpy (&(hdr->dipaddr), ipaddr, sizeof (hdr->dipaddr)); memcpy (&(hdr->sipaddr), &(netif->ip_addr), sizeof (hdr->sipaddr)); hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, 6); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); for(i = 0; i < 6; ++i) { hdr->ethhdr.dest.addr[i] = 0xff; hdr->ethhdr.src.addr[i] = srcaddr->addr[i]; } hdr->ethhdr.type = htons(ETHTYPE_ARP); return p; /* (1) */ } else { /* A valid IP->MAC address mapping was found, so we construct the Ethernet header for the outgoing packet. */ ethhdr = q->payload; for(i = 0; i < 6; i++) { ethhdr->dest.addr[i] = dest->addr[i]; ethhdr->src.addr[i] = srcaddr->addr[i]; } ethhdr->type = htons(ETHTYPE_IP); pbuf_ref (q); /* <--- this is important, because the reference must parallel that when returning over here (1). Callers must then ALWAYS do a pbuf_free on the return value of etharp_output(). */ return q; } }
/*-----------------------------------------------------------------------------------*/ struct pbuf * etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p, struct pbuf **queued) { struct etharp_hdr *hdr; u8_t i; if(p->tot_len < sizeof(struct etharp_hdr)) { DEBUGF(ETHARP_DEBUG, ("etharp_etharp_input: packet too short (%d/%d)\n", (int) p->tot_len, (int) sizeof(struct etharp_hdr))); return NULL; } hdr = p->payload; switch(htons(hdr->opcode)) { case ARP_REQUEST: *queued = update_arp_entry(&(hdr->sipaddr), &(hdr->shwaddr), 0); /* ARP request. If it asked for our address, we send out a reply. */ DEBUGF(ETHARP_DEBUG, ("etharp_arp_input: ARP request\n")); if(!memcmp(&(hdr->dipaddr), &(netif->ip_addr), sizeof (hdr->dipaddr))) { pbuf_ref(p); hdr->opcode = htons(ARP_REPLY); memcpy (&(hdr->dipaddr), &(hdr->sipaddr), sizeof (hdr->dipaddr)); memcpy (&(hdr->sipaddr), &(netif->ip_addr), sizeof (hdr->sipaddr)); for(i = 0; i < 6; ++i) { hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; hdr->shwaddr.addr[i] = ethaddr->addr[i]; hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; } hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, 6); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); hdr->ethhdr.type = htons(ETHTYPE_ARP); return p; } break; case ARP_REPLY: /* ARP reply. We insert or update the ARP table. */ DEBUGF(ETHARP_DEBUG, ("etharp_arp_input: ARP reply\n")); if(!memcmp (&(hdr->dipaddr), &(netif->ip_addr), sizeof (hdr->dipaddr))) { #if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) dhcp_arp_reply(&hdr->sipaddr); #endif /* add_arp_entry() will return a pbuf that has previously been queued waiting for an ARP reply. */ } /* whether its destined for us or not, we update the arp table */ return add_arp_entry(&(hdr->sipaddr), &(hdr->shwaddr)); break; default: DEBUGF(ETHARP_DEBUG, ("etharp_arp_input: unknown type %d\n", htons(hdr->opcode))); break; } return NULL; }
/** * Send an ARP request for the given IP address. * * Sends an ARP request for the given IP address, unless * a request for this address is already pending. Optionally * queues an outgoing packet on the resulting ARP entry. * * @param netif The lwIP network interface where ipaddr * must be queried for. * @param ipaddr The IP address to be resolved. * @param q If non-NULL, a pbuf that must be queued on the * ARP entry for the ipaddr IP address. * * @return NULL. * * @note Might be used in the future by manual IP configuration * as well. * * TODO: use the ctime field to see how long ago an ARP request was sent, * possibly retry. */ err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) { struct eth_addr *srcaddr; struct etharp_hdr *hdr; struct pbuf *p; err_t result = ERR_OK; u8_t i; u8_t perform_arp_request = 1; /* prevent 'unused argument' warning if ARP_QUEUEING == 0 */ (void)q; srcaddr = (struct eth_addr *)netif->hwaddr; /* bail out if this IP address is pending */ for (i = 0; i < ARP_TABLE_SIZE; ++i) { if (ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { if (arp_table[i].state == ETHARP_STATE_PENDING) { DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already pending as entry %u\n", i)); /* break out of for-loop, user may wish to queue a packet on a stable entry */ /* TODO: we will issue a new ARP request, which should not occur too often */ /* we might want to run a faster timer on ARP to limit this */ break; } else if (arp_table[i].state == ETHARP_STATE_STABLE) { DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already stable as entry %u\n", i)); /* user may wish to queue a packet on a stable entry, so we proceed without ARP requesting */ /* TODO: even if the ARP entry is stable, we might do an ARP request anyway */ perform_arp_request = 0; break; } } } /* queried address not yet in ARP table? */ if (i == ARP_TABLE_SIZE) { DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: IP address not found in ARP table\n")); /* find an available entry */ i = find_arp_entry(); /* bail out if no ARP entries are available */ if (i == ARP_TABLE_SIZE) { DEBUGF(ETHARP_DEBUG | 2, ("etharp_query: no more ARP entries available.\n")); return ERR_MEM; } /* we will now recycle entry i */ DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: created ARP table entry %u.\n", i)); /* i is available, create ARP entry */ ip_addr_set(&arp_table[i].ipaddr, ipaddr); arp_table[i].ctime = 0; arp_table[i].state = ETHARP_STATE_PENDING; #if ARP_QUEUEING /* free queued packet, as entry is now invalidated */ if (arp_table[i].p != NULL) { pbuf_free(arp_table[i].p); arp_table[i].p = NULL; DEBUGF(ETHARP_DEBUG | DBG_TRACE | 3, ("etharp_query: dropped packet on ARP queue. Should not occur.\n")); } #endif } #if ARP_QUEUEING /* any pbuf to queue and queue is empty? */ if (q != NULL) { /* yield later packets over older packets? */ #if ARP_QUEUE_FIRST == 0 /* earlier queued packet on this entry? */ if (arp_table[i].p != NULL) { pbuf_free(arp_table[i].p); arp_table[i].p = NULL; DEBUGF(ETHARP_DEBUG | DBG_TRACE | 3, ("etharp_query: dropped packet on ARP queue. Should not occur.\n")); /* fall-through into next if */ } #endif /* packet can be queued? */ if (arp_table[i].p == NULL) { /* copy PBUF_REF referenced payloads into PBUF_RAM */ q = pbuf_take(q); /* remember pbuf to queue, if any */ arp_table[i].p = q; /* pbufs are queued, increase the reference count */ pbuf_ref(q); DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: queued packet %p on ARP entry %u.\n", (void *)q, i)); } } #endif /* ARP request? */ if (perform_arp_request) { /* allocate a pbuf for the outgoing ARP request packet */ p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM); /* could allocate pbuf? */ if (p != NULL) { u8_t j; DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: sending ARP request.\n")); hdr = p->payload; hdr->opcode = htons(ARP_REQUEST); for(j = 0; j < netif->hwaddr_len; ++j) { hdr->dhwaddr.addr[j] = 0x00; hdr->shwaddr.addr[j] = srcaddr->addr[j]; } ip_addr_set(&(hdr->dipaddr), ipaddr); ip_addr_set(&(hdr->sipaddr), &(netif->ip_addr)); hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, netif->hwaddr_len); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); for(j = 0; j < netif->hwaddr_len; ++j) { hdr->ethhdr.dest.addr[j] = 0xff; hdr->ethhdr.src.addr[j] = srcaddr->addr[j]; } hdr->ethhdr.type = htons(ETHTYPE_ARP); /* send ARP query */ result = netif->linkoutput(netif, p); /* free ARP query packet */ pbuf_free(p); p = NULL; } else { result = ERR_MEM; DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_query: could not allocate pbuf for ARP request.\n")); } } return result; }
/** * Responds to ARP requests, updates ARP entries and sends queued IP packets. * * Should be called for incoming ARP packets. The pbuf in the argument * is freed by this function. * * @param netif The lwIP network interface on which the ARP packet pbuf arrived. * @param pbuf The ARP packet that arrived on netif. Is freed by this function. * @param ethaddr Ethernet address of netif. * * @return NULL * * @see pbuf_free() */ struct pbuf * etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) { struct etharp_hdr *hdr; u8_t i; /* drop short ARP packets */ if (p->tot_len < sizeof(struct etharp_hdr)) { DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet too short (%ld/%u)\n", p->tot_len, sizeof(struct etharp_hdr))); pbuf_free(p); return NULL; } hdr = p->payload; switch (htons(hdr->opcode)) { /* ARP request? */ case ARP_REQUEST: /* ARP request. If it asked for our address, we send out a reply. In any case, we time-stamp any existing ARP entry, and possiby send out an IP packet that was queued on it. */ DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); /* we are not configured? */ if (netif->ip_addr.addr == 0) { DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); pbuf_free(p); return NULL; } /* update the ARP cache */ update_arp_entry(netif, &(hdr->sipaddr), &(hdr->shwaddr), 0); /* ARP request for our address? */ if (ip_addr_cmp(&(hdr->dipaddr), &(netif->ip_addr))) { DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); /* re-use pbuf to send ARP reply */ hdr->opcode = htons(ARP_REPLY); ip_addr_set(&(hdr->dipaddr), &(hdr->sipaddr)); ip_addr_set(&(hdr->sipaddr), &(netif->ip_addr)); for(i = 0; i < netif->hwaddr_len; ++i) { hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; hdr->shwaddr.addr[i] = ethaddr->addr[i]; hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; } hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, netif->hwaddr_len); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); hdr->ethhdr.type = htons(ETHTYPE_ARP); /* return ARP reply */ netif->linkoutput(netif, p); } else { DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request was not for us.\n")); } break; case ARP_REPLY: /* ARP reply. We insert or update the ARP table. */ DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); #if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) /* DHCP needs to know about ARP replies */ dhcp_arp_reply(netif, &hdr->sipaddr); #endif /* ARP reply directed to us? */ if (ip_addr_cmp(&(hdr->dipaddr), &(netif->ip_addr))) { DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply is for us\n")); /* update_the ARP cache, ask to insert */ update_arp_entry(netif, &(hdr->sipaddr), &(hdr->shwaddr), ARP_INSERT_FLAG); /* ARP reply not directed to us */ } else { DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply is not for us\n")); /* update the destination address pair */ update_arp_entry(netif, &(hdr->sipaddr), &(hdr->shwaddr), 0); /* update the destination address pair */ update_arp_entry(netif, &(hdr->dipaddr), &(hdr->dhwaddr), 0); } break; default: DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %d\n", htons(hdr->opcode))); break; } /* free ARP packet */ pbuf_free(p); p = NULL; /* nothing to send, we did it! */ return NULL; }
/** * Send an ARP request for the given IP address. * * Sends an ARP request for the given IP address, unless * a request for this address is already pending. Optionally * queues an outgoing packet on the resulting ARP entry. * * @param netif The lwIP network interface where ipaddr * must be queried for. * @param ipaddr The IP address to be resolved. * @param q If non-NULL, a pbuf that must be queued on the * ARP entry for the ipaddr IP address. * * @return NULL. * * @note Might be used in the future by manual IP configuration * as well. * * TODO: use the ctime field to see how long ago an ARP request was sent, * possibly retry. */ err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) { struct eth_addr *srcaddr; struct etharp_hdr *hdr; err_t result = ERR_OK; s8_t i; u8_t perform_arp_request = 1; /* prevent 'unused argument' warning if ARP_QUEUEING == 0 */ (void)q; srcaddr = (struct eth_addr *)netif->hwaddr; /* bail out if this IP address is pending */ for (i = 0; i < ARP_TABLE_SIZE; ++i) { if (arp_table[i].state != ETHARP_STATE_EMPTY && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { if (arp_table[i].state == ETHARP_STATE_PENDING) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already pending as entry %u\n", i)); /* break out of for-loop, user may wish to queue a packet on a pending entry */ /* TODO: we will issue a new ARP request, which should not occur too often */ /* we might want to run a faster timer on ARP to limit this */ break; } else if (arp_table[i].state == ETHARP_STATE_STABLE) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | DBG_STATE, ("etharp_query: requested IP already stable as entry %u\n", i)); /* User wishes to queue a packet on a stable entry (or does she want to send * out the packet immediately, we will not know), so we force an ARP request. * Upon response we will send out the queued packet in etharp_update(). * * Alternatively, we could accept the stable entry, and just send out the packet * immediately. I chose to implement the former approach. */ perform_arp_request = (q?1:0); break; } } } /* queried address not yet in ARP table? */ if (i == ARP_TABLE_SIZE) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: IP address not found in ARP table\n")); /* find an available (unused or old) entry */ i = find_arp_entry(); /* bail out if no ARP entries are available */ if (i == ERR_MEM) { LWIP_DEBUGF(ETHARP_DEBUG | 2, ("etharp_query: no more ARP entries available. Should seldom occur.\n")); return ERR_MEM; } /* i is available, create ARP entry */ arp_table[i].state = ETHARP_STATE_PENDING; ip_addr_set(&arp_table[i].ipaddr, ipaddr); } /* { i is now valid } */ #if ARP_QUEUEING /* queue packet (even on a stable entry, see above) */ /* copy any PBUF_REF referenced payloads into PBUF_RAM */ q = pbuf_take(q); pbuf_queue(arp_table[i].p, q); #endif /* ARP request? */ if (perform_arp_request) { struct pbuf *p; /* allocate a pbuf for the outgoing ARP request packet */ p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM); /* could allocate pbuf? */ if (p != NULL) { u8_t j; LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: sending ARP request.\n")); hdr = p->payload; hdr->opcode = htons(ARP_REQUEST); for (j = 0; j < netif->hwaddr_len; ++j) { hdr->shwaddr.addr[j] = srcaddr->addr[j]; /* the hardware address is what we ask for, in * a request it is a don't-care, we use 0's */ hdr->dhwaddr.addr[j] = 0x00; } hdr->dipaddr = *(struct ip_addr2 *)ipaddr; hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr; hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, netif->hwaddr_len); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); for (j = 0; j < netif->hwaddr_len; ++j) { hdr->ethhdr.dest.addr[j] = 0xff; hdr->ethhdr.src.addr[j] = srcaddr->addr[j]; } hdr->ethhdr.type = htons(ETHTYPE_ARP); /* send ARP query */ result = netif->linkoutput(netif, p); /* free ARP query packet */ pbuf_free(p); p = NULL; } else { result = ERR_MEM; LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_query: could not allocate pbuf for ARP request.\n")); } } return result; }
/** * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache * send out queued IP packets. Updates cache with snooped address pairs. * * Should be called for incoming ARP packets. The pbuf in the argument * is freed by this function. * * @param netif The lwIP network interface on which the ARP packet pbuf arrived. * @param pbuf The ARP packet that arrived on netif. Is freed by this function. * @param ethaddr Ethernet address of netif. * * @return NULL * * @see pbuf_free() */ void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) { struct etharp_hdr *hdr; /* these are aligned properly, whereas the ARP header fields might not be */ struct ip_addr sipaddr, dipaddr; u8_t i; u8_t for_us; /* drop short ARP packets */ if (p->tot_len < sizeof(struct etharp_hdr)) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%d/%d)\n", p->tot_len, sizeof(struct etharp_hdr))); pbuf_free(p); return; } hdr = p->payload; /* get aligned copies of addresses */ *(struct ip_addr2 *)&sipaddr = hdr->sipaddr; *(struct ip_addr2 *)&dipaddr = hdr->dipaddr; /* this interface is not configured? */ if (netif->ip_addr.addr == 0) { for_us = 0; } else { /* ARP packet directed to us? */ for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr)); } /* ARP message directed to us? */ if (for_us) { /* add IP address in ARP cache; assume requester wants to talk to us. * can result in directly sending the queued packets for this host. */ update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_CREATE); /* ARP message not directed to us? */ } else { /* update the source IP address in the cache, if present */ update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0); } /* now act on the message itself */ switch (htons(hdr->opcode)) { /* ARP request? */ case ARP_REQUEST: /* ARP request. If it asked for our address, we send out a * reply. In any case, we time-stamp any existing ARP entry, * and possiby send out an IP packet that was queued on it. */ LWIP_DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); /* ARP request for our address? */ if (for_us) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); /* re-use pbuf to send ARP reply */ hdr->opcode = htons(ARP_REPLY); hdr->dipaddr = hdr->sipaddr; hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr; for(i = 0; i < netif->hwaddr_len; ++i) { hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; hdr->shwaddr.addr[i] = ethaddr->addr[i]; hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; } hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, netif->hwaddr_len); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); hdr->ethhdr.type = htons(ETHTYPE_ARP); /* return ARP reply */ netif->linkoutput(netif, p); #ifdef LINKLOCAL_IP //Ron Add for Rendzvous 12/10/04 if( mRENVEnable && (LinkLocal_get_current_state() != NO_USE ) && is_linklocal_ip( dipaddr.addr ) ) { if( (LinkLocal_get_current_state() != IDLE) && ip_conflict_cnt++ == 0 ) linklocal_alarm(); else if( (LinkLocal_get_current_state() == IDLE) && ip_conflict_cnt++ == 0) { linklocal_alarm(); } } #endif /* we are not configured? */ } else if (netif->ip_addr.addr == 0) { /* { for_us == 0 and netif->ip_addr.addr == 0 } */ #ifdef LINKLOCAL_IP //Ron Add for Rendzvous 12/10/04 if( mRENVEnable && (LinkLocal_get_current_state() != NO_USE ) && is_linklocal_ip( dipaddr.addr ) ) { if( (LinkLocal_get_current_state() != IDLE) && ip_conflict_cnt++ == 0 ) linklocal_alarm(); else if( (LinkLocal_get_current_state() == IDLE) && ip_conflict_cnt++ == 0) { linklocal_alarm(); } hdr->opcode = htons(ARP_REPLY); hdr->sipaddr = hdr->dipaddr; hdr->dipaddr = *(struct ip_addr2 *)&netif->ip_addr; // hdr->dipaddr = hdr->sipaddr; // hdr->sipaddr = *(struct ip_addr2 *)&netif->ip_addr; for(i = 0; i < netif->hwaddr_len; ++i) { hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; hdr->shwaddr.addr[i] = ethaddr->addr[i]; hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; } hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, netif->hwaddr_len); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); hdr->ethhdr.type = htons(ETHTYPE_ARP); netif->linkoutput(netif, p); } #endif LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); /* request was not directed to us */ } else { /* { for_us == 0 and netif->ip_addr.addr != 0 } */ LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); } break; case ARP_REPLY: /* ARP reply. We already updated the ARP cache earlier. */ LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); #if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) /* DHCP wants to know about ARP replies to our wanna-have-address */ if (for_us){ dhcp_arp_reply(netif, &sipaddr); } #endif #ifdef LINKLOCAL_IP //Ron Add for Rendzvous 12/10/04 if( mRENVEnable && (LinkLocal_get_current_state() != NO_USE ) && is_linklocal_ip( sipaddr.addr ) ) { if( (LinkLocal_get_current_state() != IDLE) && ip_conflict_cnt++ == 0 ) linklocal_alarm(); else if( (LinkLocal_get_current_state() == IDLE) && ip_conflict_cnt++ == 0) { linklocal_alarm(); } } #endif break; default: LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %d\n", htons(hdr->opcode))); break; } /* free ARP packet */ pbuf_free(p); }
/** * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache * send out queued IP packets. Updates cache with snooped address pairs. * * Should be called for incoming ARP packets. The pbuf in the argument * is freed by this function. * * @param netif The lwIP network interface on which the ARP packet pbuf arrived. * @param pbuf The ARP packet that arrived on netif. Is freed by this function. * @param ethaddr Ethernet address of netif. * * @return NULL * * @see pbuf_free() */ struct pbuf * etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) { struct etharp_hdr *hdr; u8_t i; u8_t for_us; /* drop short ARP packets */ if (p->tot_len < sizeof(struct etharp_hdr)) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%d/%d)\n", p->tot_len, sizeof(struct etharp_hdr))); pbuf_free(p); return NULL; } hdr = p->payload; /* this interface is not configured? */ if (netif->ip_addr.addr == 0) { for_us = 0; } else { /* ARP packet directed to us? */ for_us = ip_addr_cmp(&(hdr->dipaddr), &(netif->ip_addr)); } /* add or update entries in the ARP cache */ if (for_us) { /* insert IP address in ARP cache (assume requester wants to talk to us) * we might even send out a queued packet to this host */ update_arp_entry(netif, &(hdr->sipaddr), &(hdr->shwaddr), ARP_INSERT_FLAG); /* request was not directed to us, but snoop anyway */ } else { /* update the source IP address in the cache */ update_arp_entry(netif, &(hdr->sipaddr), &(hdr->shwaddr), 0); } switch (htons(hdr->opcode)) { /* ARP request? */ case ARP_REQUEST: /* ARP request. If it asked for our address, we send out a reply. In any case, we time-stamp any existing ARP entry, and possiby send out an IP packet that was queued on it. */ LWIP_DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); /* we are not configured? */ if (netif->ip_addr.addr == 0) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); pbuf_free(p); return NULL; } /* ARP request for our address? */ if (for_us) { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); /* re-use pbuf to send ARP reply */ hdr->opcode = htons(ARP_REPLY); ip_addr_set(&(hdr->dipaddr), &(hdr->sipaddr)); ip_addr_set(&(hdr->sipaddr), &(netif->ip_addr)); for(i = 0; i < netif->hwaddr_len; ++i) { hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; hdr->shwaddr.addr[i] = ethaddr->addr[i]; hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; } hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, netif->hwaddr_len); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); hdr->ethhdr.type = htons(ETHTYPE_ARP); /* return ARP reply */ netif->linkoutput(netif, p); /* request was not directed to us */ } else { LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request was not for us.\n")); } break; case ARP_REPLY: /* ARP reply. We insert or update the ARP table later. */ LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); #if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) /* DHCP wants to know about ARP replies to our wanna-have-address */ if (for_us) dhcp_arp_reply(netif, &hdr->sipaddr); #endif break; default: LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %d\n", htons(hdr->opcode))); break; } /* free ARP packet */ pbuf_free(p); p = NULL; /* nothing to send, we did it! */ return NULL; }