/**
* @file
* Ethernet Interface Skeleton
*
*/
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
* Author: H. Peter Anvin <hpa@@zytor.com>
* Author: Eric Biederman <ebiederm@xmission.com>
*
*/
/*
* This file is a skeleton for developing Ethernet network interface
* drivers for lwIP. Add code to the low_level functions and do a
* search-and-replace for the word "ethernetif" to replace it with
* something that better describes your network interface.
*/
/* other headers include deprintf.h too early */
#define UNDIIF_ID_FULL_DEBUG (UNDIIF_ID_DEBUG | UNDIIF_DEBUG)
#if UNDIIF_ID_FULL_DEBUG
# ifndef DEBUG
# define DEBUG 1
# endif
# ifndef DEBUG_PORT
# define DEBUG_PORT 0x3f8
# endif
#endif /* UNDIIF_ID_FULL_DEBUG */
#include <core.h>
#include "lwip/opt.h"
#define LWIP_UNDIIF_DBG(debug) \
( ((debug) & LWIP_DBG_ON) && \
((debug) & LWIP_DBG_TYPES_ON) && \
(((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL) )
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include <lwip/stats.h>
#include <lwip/snmp.h>
#include "netif/etharp.h"
#include "netif/ppp_oe.h"
#include "lwip/netifapi.h"
#include "lwip/tcpip.h"
#include "../../../fs/pxe/pxe.h"
#include <inttypes.h>
#include <string.h>
#include <syslinux/pxe_api.h>
#include <dprintf.h>
/* debug extras */
#include "ipv4/lwip/icmp.h"
#include "lwip/tcp_impl.h"
#include "lwip/udp.h"
#if LWIP_AUTOIP
#error "AUTOIP not supported"
#endif
#if ETH_PAD_SIZE
#error "ETH_PAD_SIZE not supported"
#endif
#if NETIF_MAX_HWADDR_LEN != MAC_MAX
#error "hwaddr_len mismatch"
#endif
/** the time an ARP entry stays valid after its last update,
* for ARP_TMR_INTERVAL = 5000, this is
* (240 * 5) seconds = 20 minutes.
*/
#define UNDIARP_MAXAGE 240
/** the time an ARP entry stays pending after first request,
* for ARP_TMR_INTERVAL = 5000, this is
* (2 * 5) seconds = 10 seconds.
*
* @internal Keep this number at least 2, otherwise it might
* run out instantly if the timeout occurs directly after a request.
*/
#define UNDIARP_MAXPENDING 2
typedef u8_t hwaddr_t[NETIF_MAX_HWADDR_LEN];
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
/** the ARP message */
struct arp_hdr {
PACK_STRUCT_FIELD(u16_t hwtype);
PACK_STRUCT_FIELD(u16_t proto);
PACK_STRUCT_FIELD(u8_t hwlen);
PACK_STRUCT_FIELD(u8_t protolen);
PACK_STRUCT_FIELD(u16_t opcode);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
static inline int arp_hdr_len(struct netif *netif)
{
return sizeof(struct arp_hdr) + (netif->hwaddr_len + sizeof(uint32_t))*2;
}
enum undiarp_state {
UNDIARP_STATE_EMPTY = 0,
UNDIARP_STATE_PENDING,
UNDIARP_STATE_STABLE
};
struct undiarp_entry {
#if ARP_QUEUEING
/**
* Pointer to queue of pending outgoing packets on this ARP entry.
*/
struct etharp_q_entry *q;
#endif
struct ip_addr ipaddr;
u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
enum undiarp_state state;
u8_t ctime;
struct netif *netif;
};
#define PKTBUF_SIZE 2048
/* Define those to better describe your network interface. */
#define IFNAME0 'u'
#define IFNAME1 'n'
static struct netif undi_netif;
static struct undiarp_entry arp_table[ARP_TABLE_SIZE];
#if !LWIP_NETIF_HWADDRHINT
static u8_t undiarp_cached_entry;
#endif
/**
* Try hard to create a new entry - we want the IP address to appear in
* the cache (even if this means removing an active entry or so). */
#define UNDIARP_TRY_HARD 1
#define UNDIARP_FIND_ONLY 2
#define UNIDIF_ID_STRLEN 300
static inline bool undi_is_ethernet(struct netif *netif)
{
(void)netif;
return MAC_type == ETHER_TYPE;
}
#if 0
static void print_pbuf(struct pbuf *p)
{
struct pbuf *q;
int off;
for( off = 0, q = p; q != NULL; q = q->next) {
unsigned char *byte, *end;
byte = q->payload;
end = byte + q->len;
for (; byte < end; byte++, off++ ) {
if ((off & 0xf) == 0) {
printf("%04x: ", off);
}
printf("%02x ", *byte);
if ((off & 0xf) == 0xf) {
printf("\n");
}
}
}
printf("\n");
}
#endif
#if 0
static void print_arp_pbuf(struct netif *netif, struct pbuf *p)
{
struct arp_hdr *hdr;
u8_t *hdr_ptr;
int i;
hdr = p->payload;
hdr_ptr = (unsigned char *)(hdr + 1);
/* Fixed fields */
printf("arp: %04x %04x %04x %04x ",
hdr->hwtype,
hdr->proto,
hdr->_hwlen_protolen);
/* Source hardware address */
for(i = 0; i < netif->hwaddr_len; i++, hdr_ptr++) {
printf("%02x%c", *hdr_ptr,(i +1) == netif->hwaddr_len?' ':':');
}
/* Source ip address */
printf("%d.%d.%d.%d ", hdr_ptr[0], hdr_ptr[1], hdr_ptr[2], hdr_ptr[3]);
hdr_ptr += 4;
/* Destination hardware address */
for(i = 0; i < netif->hwaddr_len; i++, hdr_ptr++) {
printf("%02x%c", *hdr_ptr, (i +1) == netif->hwaddr_len?' ':':');
}
/* Destination ip address */
printf("%d.%d.%d.%d ", hdr_ptr[0], hdr_ptr[1], hdr_ptr[2], hdr_ptr[3]);
hdr_ptr += 4;
}
#endif
#if LWIP_UNDIIF_DBG(UNDIIF_ID_FULL_DEBUG)
int snprintf_eth_hdr(char *str, size_t size, char head[],
struct eth_hdr *ethhdr, char dir, char status,
char tail[])
{
u8_t *d = ethhdr->dest.addr;
u8_t *s = ethhdr->src.addr;
return snprintf(str, size,
"%s: d:%02x:%02x:%02x:%02x:%02x:%02x"
" s:%02x:%02x:%02x:%02x:%02x:%02x"
" t:%4hx %c%c%s\n", head,
d[0], d[1], d[2], d[3], d[4], d[5],
s[0], s[1], s[2], s[3], s[4], s[5],
(unsigned)htons(ethhdr->type),
dir, status, tail);
}
int snprintf_arp_hdr(char *str, size_t size, char head[],
struct eth_hdr *ethhdr, char dir,
char status, char tail[])
{
struct etharp_hdr *arphdr;
u8_t *d, *s;
struct ip_addr *sip, *dip;
if (ntohs(ethhdr->type) == ETHTYPE_ARP) {
arphdr = (struct etharp_hdr *)((void *)ethhdr + 14);
d = arphdr->dhwaddr.addr;
s = arphdr->shwaddr.addr;
sip = (struct ip_addr *) &(arphdr->sipaddr);
dip = (struct ip_addr *) &(arphdr->dipaddr);
return snprintf(str, size,
"%s: s:%02x:%02x:%02x:%02x:%02x:%02x"
" %3d.%3d.%3d.%3d"
" %02x:%02x:%02x:%02x:%02x:%02x"
" %3d.%3d.%3d.%3d"
" %c%c%s\n", head,
s[0], s[1], s[2], s[3], s[4], s[5],
ip4_addr1(sip), ip4_addr2(sip),
ip4_addr3(sip), ip4_addr4(sip),
d[0], d[1], d[2], d[3], d[4], d[5],
ip4_addr1(dip), ip4_addr2(dip),
ip4_addr3(dip), ip4_addr4(dip),
dir, status, tail);
} else {
return 0;
}
}
int snprintf_ip_hdr(char *str, size_t size, char head[],
struct eth_hdr *ethhdr, char dir,
char status, char tail[])
{
struct ip_hdr *iphdr;
if (ntohs(ethhdr->type) == ETHTYPE_IP) {
iphdr = (struct ip_hdr *)((void *)ethhdr + 14);
return snprintf(str, size,
"%s: s:%3d.%3d.%3d.%3d %3d.%3d.%3d.%3d l:%5d"
" i:%04x p:%04x c:%04x hl:%3d"
" %c%c%s\n", head,
ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src),
ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src),
ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest),
ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest),
ntohs(IPH_LEN(iphdr)), ntohs(IPH_ID(iphdr)),
IPH_PROTO(iphdr), ntohs(IPH_CHKSUM(iphdr)),
(IPH_HL(iphdr) << 2),
dir, status, tail);
} else {
return 0;
}
}
int snprintf_icmp_hdr(char *str, size_t size, char head[],
struct eth_hdr *ethhdr, char dir,
char status, char tail[])
{
struct ip_hdr *iphdr;
struct icmp_echo_hdr *icmphdr;
if (ntohs(ethhdr->type) == ETHTYPE_IP) {
iphdr = (struct ip_hdr *)((void *)ethhdr + 14);
if (IPH_PROTO(iphdr) == IP_PROTO_ICMP) {
icmphdr = (struct icmp_echo_hdr *)((void *)iphdr + (IPH_HL(iphdr) << 2));
return snprintf(str, size,
"%s: t:%02x c:%02x k:%04x"
" i:%04x s:%04x "
" %c%c%s\n", head,
icmphdr->type, icmphdr->code, ntohs(icmphdr->chksum),
ntohs(icmphdr->id), ntohs(icmphdr->seqno),
dir, status, tail);
} else {
return 0;
}
} else {
return 0;
}
}
int snprintf_tcp_hdr(char *str, size_t size, char head[],
struct eth_hdr *ethhdr, char dir,
char status, char tail[])
{
struct ip_hdr *iphdr;
struct tcp_hdr *tcphdr;
if (ntohs(ethhdr->type) == ETHTYPE_IP) {
iphdr = (struct ip_hdr *)((void *)ethhdr + 14);
if (IPH_PROTO(iphdr) == IP_PROTO_TCP) {
tcphdr = (struct tcp_hdr *)((void *)iphdr + (IPH_HL(iphdr) << 2));
u16_t lenfl = ntohs(tcphdr->_hdrlen_rsvd_flags);
return snprintf(str, size,
"%s: s:%5d %5d q:%08x a:%08x lf:%04x k:%04x"
" %c%c%s\n", head,
ntohs(tcphdr->src), ntohs(tcphdr->dest),
ntohl(tcphdr->seqno), ntohl(tcphdr->ackno),
lenfl, ntohs(tcphdr->chksum),
dir, status, tail);
} else {
return 0;
}
} else {
return 0;
}
}
int snprintf_udp_hdr(char *str, size_t size, char head[],
struct eth_hdr *ethhdr, char dir,
char status, char tail[])
{
struct ip_hdr *iphdr;
struct udp_hdr *udphdr;
if (ntohs(ethhdr->type) == ETHTYPE_IP) {
iphdr = (struct ip_hdr *)((void *)ethhdr + 14);
if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
udphdr = (struct udp_hdr *)((void *)iphdr + (IPH_HL(iphdr) << 2));
return snprintf(str, size,
"%s: s:%5d %5d l:%d c:%04x"
" %c%c%s\n", head,
ntohs(udphdr->src), ntohs(udphdr->dest),
ntohs(udphdr->len), ntohs(udphdr->chksum),
dir, status, tail);
} else {
return 0;
}
} else {
return 0;
}
}
#endif /* UNDIIF_ID_FULL_DEBUG */
/**
* In this function, the hardware should be initialized.
* Called from undiif_init().
*
* @param netif the already initialized lwip network interface structure
* for this undiif
*/
static void
low_level_init(struct netif *netif)
{
static __lowmem t_PXENV_UNDI_OPEN undi_open;
int i;
/* MAC_type and MAC_len should always match what is returned by
* PXENV_UNDI_GET_INFORMATION. At the moment the both seem to be
* reliable but if they disagree that is a sign of a nasty bug
* somewhere so abort.
*/
/* If we are in conflict abort */
if (MAC_type != pxe_undi_info.HwType) {
printf("HwType conflicit: %u != %u\n",
MAC_type, pxe_undi_info.HwType);
kaboom();
}
if (MAC_len != pxe_undi_info.HwAddrLen) {
printf("HwAddrLen conflict: %u != %u\n",
MAC_len, pxe_undi_info.HwAddrLen);
kaboom();
}
/* set MAC hardware address length */
netif->hwaddr_len = MAC_len;
/* set MAC hardware address */
memcpy(netif->hwaddr, MAC, MAC_len);
/* maximum transfer unit */
netif->mtu = pxe_undi_info.MaxTranUnit;
dprintf("UNDI: hw address");
for (i = 0; i < netif->hwaddr_len; i++)
dprintf("%c%02x", i ? ':' : ' ', (uint8_t)netif->hwaddr[i]);
dprintf("\n");
/* device capabilities */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_LINK_UP;
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
if (undi_is_ethernet(netif))
netif->flags |= NETIF_FLAG_ETHARP;
/* Install the interrupt vector */
pxe_start_isr();
/* Open the UNDI stack - you'd think the BC would have done this... */
undi_open.PktFilter = 0x0003; /* FLTR_DIRECTED | FLTR_BRDCST */
pxe_call(PXENV_UNDI_OPEN, &undi_open);
}
/**
* This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this undiif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
* an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
* to become availale since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
extern volatile uint32_t pxe_irq_count;
extern volatile uint8_t pxe_need_poll;
static err_t
undi_transmit(struct netif *netif, struct pbuf *pbuf,
hwaddr_t *dest, uint16_t undi_protocol)
{
struct pxe_xmit {
t_PXENV_UNDI_TRANSMIT xmit;
t_PXENV_UNDI_TBD tbd;
};
static __lowmem struct pxe_xmit pxe;
static __lowmem hwaddr_t low_dest;
static __lowmem char pkt_buf[PKTBUF_SIZE];
uint32_t now;
static uint32_t first_xmit;
#if LWIP_UNDIIF_DBG(UNDIIF_ID_FULL_DEBUG)
char *str = malloc(UNIDIF_ID_STRLEN);
int strpos = 0;
struct eth_hdr *ethhdr = pbuf->payload;
strpos += snprintf(str + strpos, UNIDIF_ID_STRLEN - strpos,
"undi xmit thd '%s'\n", current()->name);
strpos += snprintf_eth_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
"undi", ethhdr, 'x', '0', "");
strpos += snprintf_arp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" arp", ethhdr, 'x', '0', "");
strpos += snprintf_ip_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" ip", ethhdr, 'x', '0', "");
strpos += snprintf_icmp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" icmp", ethhdr, 'x', '0', "");
strpos += snprintf_tcp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" tcp", ethhdr, 'x', '0', "");
strpos += snprintf_udp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" udp", ethhdr, 'x', '0', "");
LWIP_DEBUGF(UNDIIF_ID_FULL_DEBUG, ("%s", str));
free(str);
#endif /* UNDIIF_ID_FULL_DEBUG */
/* Drop jumbo frames */
if ((pbuf->tot_len > sizeof(pkt_buf)) || (pbuf->tot_len > netif->mtu))
return ERR_ARG;
if (__unlikely(!pxe_irq_count)) {
now = ms_timer();
if (!first_xmit) {
first_xmit = now;
} else if (now - first_xmit > 3000) {
/* 3 seconds after first transmit, and no interrupts */
LWIP_PLATFORM_DIAG(("undiif: forcing polling\n"));
asm volatile("orb $1,%0" : "+m" (pxe_need_poll));
asm volatile("incl %0" : "+m" (pxe_irq_count));
}
}
pbuf_copy_partial( pbuf, pkt_buf, pbuf->tot_len, 0);
if (dest)
memcpy(low_dest, dest, netif->hwaddr_len);
do {
memset(&pxe, 0, sizeof pxe);
pxe.xmit.Protocol = undi_protocol;
pxe.xmit.XmitFlag = dest? XMT_DESTADDR : XMT_BROADCAST;
pxe.xmit.DestAddr = FAR_PTR(&low_dest);
pxe.xmit.TBD = FAR_PTR(&pxe.tbd);
pxe.tbd.ImmedLength = pbuf->tot_len;
pxe.tbd.Xmit = FAR_PTR(pkt_buf);
pxe_call(PXENV_UNDI_TRANSMIT, &pxe.xmit);
} while (pxe.xmit.Status == PXENV_STATUS_OUT_OF_RESOURCES);
LINK_STATS_INC(link.xmit);
return ERR_OK;
}
static err_t
undi_send_unknown(struct netif *netif, struct pbuf *pbuf)
{
return undi_transmit(netif, pbuf, NULL, P_UNKNOWN);
}
static err_t
undi_send_ip(struct netif *netif, struct pbuf *pbuf, hwaddr_t *dst)
{
return undi_transmit(netif, pbuf, dst, P_IP);
}
static err_t
undi_send_arp(struct netif *netif, struct pbuf *pbuf, hwaddr_t *dst)
{
return undi_transmit(netif, pbuf, dst, P_ARP);
}
/**
* Send an ARP request packet asking for ipaddr.
*
* @param netif the lwip network interface on which to send the request
* @param ipaddr the IP address for which to ask
* @return ERR_OK if the request has been sent
* ERR_MEM if the ARP packet couldn't be allocated
* any other err_t on failure
*/
static err_t
undiarp_request(struct netif *netif, struct ip_addr *ipaddr)
{
struct pbuf *p;
err_t result = ERR_OK;
struct arp_hdr *hdr;
u8_t *hdr_ptr;
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_request: sending ARP request.\n"));
/* allocate a pbuf for the outgoing ARP request packet */
p = pbuf_alloc(PBUF_RAW, arp_hdr_len(netif), PBUF_RAM);
/* could allocate a pbuf for an ARP request? */
if (p == NULL) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
("undiarp_raw: could not allocate pbuf for ARP request.\n"));
ETHARP_STATS_INC(etharp.memerr);
return ERR_MEM;
}
LWIP_ASSERT("check that first pbuf can hold arp_hdr_len bytesr",
(p->len >= arp_hdr_len(netif)));
hdr = p->payload;
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_request: sending raw ARP packet.\n"));
hdr->opcode = htons(ARP_REQUEST);
hdr->hwtype = htons(MAC_type);
hdr->proto = htons(ETHTYPE_IP);
hdr->hwlen = netif->hwaddr_len;
hdr->protolen = sizeof(struct ip_addr);
hdr_ptr = (unsigned char *)(hdr + 1);
memcpy(hdr_ptr, netif->hwaddr, netif->hwaddr_len);
hdr_ptr += netif->hwaddr_len;
memcpy(hdr_ptr, &netif->ip_addr, 4);
hdr_ptr += 4;
memset(hdr_ptr, 0, netif->hwaddr_len);
hdr_ptr += netif->hwaddr_len;
memcpy(hdr_ptr, ipaddr, 4);
/* send ARP query */
result = undi_send_arp(netif, p, NULL);
ETHARP_STATS_INC(etharp.xmit);
/* free ARP query packet */
pbuf_free(p);
p = NULL;
/* could not allocate pbuf for ARP request */
return result;
}
#if ARP_QUEUEING
/**
* Free a complete queue of etharp entries
*
* @param q a qeueue of etharp_q_entry's to free
*/
static void
free_undiarp_q(struct etharp_q_entry *q)
{
struct etharp_q_entry *r;
LWIP_ASSERT("q != NULL", q != NULL);
LWIP_ASSERT("q->p != NULL", q->p != NULL);
while (q) {
r = q;
q = q->next;
LWIP_ASSERT("r->p != NULL", (r->p != NULL));
pbuf_free(r->p);
memp_free(MEMP_ARP_QUEUE, r);
}
}
#endif
/**
* Clears expired entries in the ARP table.
*
* This function should be called every ETHARP_TMR_INTERVAL microseconds (5 seconds),
* in order to expire entries in the ARP table.
*/
void
undiarp_tmr(void)
{
u8_t i;
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG, ("undiarp_timer\n"));
/* remove expired entries from the ARP table */
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
arp_table[i].ctime++;
if (((arp_table[i].state == UNDIARP_STATE_STABLE) &&
(arp_table[i].ctime >= UNDIARP_MAXAGE)) ||
((arp_table[i].state == UNDIARP_STATE_PENDING) &&
(arp_table[i].ctime >= UNDIARP_MAXPENDING))) {
/* pending or stable entry has become old! */
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG , ("undiarp_timer: expired %s entry %"U16_F".\n",
arp_table[i].state == UNDIARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
/* clean up entries that have just been expired */
/* remove from SNMP ARP index tree */
snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
#if ARP_QUEUEING
/* and empty packet queue */
if (arp_table[i].q != NULL) {
/* remove all queued packets */
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG , ("undiarp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
free_undiarp_q(arp_table[i].q);
arp_table[i].q = NULL;
}
#endif
/* recycle entry for re-use */
arp_table[i].state = UNDIARP_STATE_EMPTY;
}
#if ARP_QUEUEING
/* still pending entry? (not expired) */
if (arp_table[i].state == UNDIARP_STATE_PENDING) {
/* resend an ARP query here? */
}
#endif
}
}
/**
* Search the ARP table for a matching or new entry.
*
* If an IP address is given, return a pending or stable ARP entry that matches
* the address. If no match is found, create a new entry with this address set,
* but in state ETHARP_EMPTY. The caller must check and possibly change the
* state of the returned entry.
*
* If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
*
* In all cases, attempt to create new entries from an empty entry. If no
* empty entries are available and UNDIARP_TRY_HARD flag is set, recycle
* old entries. Heuristic choose the least important entry for recycling.
*
* @param ipaddr IP address to find in ARP cache, or to add if not found.
* @param flags
* - UNDIARP_TRY_HARD: Try hard to create a entry by allowing recycling of
* active (stable or pending) entries.
*
* @return The ARP entry index that matched or is created, ERR_MEM if no
* entry is found or could be recycled.
*/
static s8_t
#if LWIP_NETIF_HWADDRHINT
find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif)
#else /* LWIP_NETIF_HWADDRHINT */
find_entry(struct ip_addr *ipaddr, u8_t flags)
#endif /* LWIP_NETIF_HWADDRHINT */
{
s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
s8_t empty = ARP_TABLE_SIZE;
u8_t i = 0, age_pending = 0, age_stable = 0;
#if ARP_QUEUEING
/* oldest entry with packets on queue */
s8_t old_queue = ARP_TABLE_SIZE;
/* its age */
u8_t age_queue = 0;
#endif
/* First, test if the last call to this function asked for the
* same address. If so, we're really fast! */
if (ipaddr) {
/* ipaddr to search for was given */
#if LWIP_NETIF_HWADDRHINT
if ((netif != NULL) && (netif->addr_hint != NULL)) {
/* per-pcb cached entry was given */
u8_t per_pcb_cache = *(netif->addr_hint);
if ((per_pcb_cache < ARP_TABLE_SIZE) && arp_table[per_pcb_cache].state == UNDIARP_STATE_STABLE) {
/* the per-pcb-cached entry is stable */
if (ip_addr_cmp(ipaddr, &arp_table[per_pcb_cache].ipaddr)) {
/* per-pcb cached entry was the right one! */
ETHARP_STATS_INC(etharp.cachehit);
return per_pcb_cache;
}
}
}
#else /* #if LWIP_NETIF_HWADDRHINT */
if (arp_table[undiarp_cached_entry].state == UNDIARP_STATE_STABLE) {
/* the cached entry is stable */
if (ip_addr_cmp(ipaddr, &arp_table[undiarp_cached_entry].ipaddr)) {
/* cached entry was the right one! */
ETHARP_STATS_INC(etharp.cachehit);
return undiarp_cached_entry;
}
}
#endif /* #if LWIP_NETIF_HWADDRHINT */
}
/**
* a) do a search through the cache, remember candidates
* b) select candidate entry
* c) create new entry
*/
/* a) in a single search sweep, do all of this
* 1) remember the first empty entry (if any)
* 2) remember the oldest stable entry (if any)
* 3) remember the oldest pending entry without queued packets (if any)
* 4) remember the oldest pending entry with queued packets (if any)
* 5) search for a matching IP entry, either pending or stable
* until 5 matches, or all entries are searched for.
*/
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
/* no empty entry found yet and now we do find one? */
if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == UNDIARP_STATE_EMPTY)) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG , ("find_entry: found empty entry %"U16_F"\n", (u16_t)i));
/* remember first empty entry */
empty = i;
}
/* pending entry? */
else if (arp_table[i].state == UNDIARP_STATE_PENDING) {
/* if given, does IP address match IP address in ARP entry? */
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i));
/* found exact IP address match, simply bail out */
#if LWIP_NETIF_HWADDRHINT
NETIF_SET_HINT(netif, i);
#else /* #if LWIP_NETIF_HWADDRHINT */
undiarp_cached_entry = i;
#endif /* #if LWIP_NETIF_HWADDRHINT */
return i;
#if ARP_QUEUEING
/* pending with queued packets? */
} else if (arp_table[i].q != NULL) {
if (arp_table[i].ctime >= age_queue) {
old_queue = i;
age_queue = arp_table[i].ctime;
}
#endif
/* pending without queued packets? */
} else {
if (arp_table[i].ctime >= age_pending) {
old_pending = i;
age_pending = arp_table[i].ctime;
}
}
}
/* stable entry? */
else if (arp_table[i].state == UNDIARP_STATE_STABLE) {
/* if given, does IP address match IP address in ARP entry? */
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i));
/* found exact IP address match, simply bail out */
#if LWIP_NETIF_HWADDRHINT
NETIF_SET_HINT(netif, i);
#else /* #if LWIP_NETIF_HWADDRHINT */
undiarp_cached_entry = i;
#endif /* #if LWIP_NETIF_HWADDRHINT */
return i;
/* remember entry with oldest stable entry in oldest, its age in maxtime */
} else if (arp_table[i].ctime >= age_stable) {
old_stable = i;
age_stable = arp_table[i].ctime;
}
}
}
/* { we have no match } => try to create a new entry */
/* no empty entry found and not allowed to recycle? */
if (((empty == ARP_TABLE_SIZE) && ((flags & UNDIARP_TRY_HARD) == 0))
/* or don't create new entry, only search? */
|| ((flags & UNDIARP_FIND_ONLY) != 0)) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: no empty entry found and not allowed to recycle\n"));
return (s8_t)ERR_MEM;
}
/* b) choose the least destructive entry to recycle:
* 1) empty entry
* 2) oldest stable entry
* 3) oldest pending entry without queued packets
* 4) oldest pending entry with queued packets
*
* { UNDIARP_TRY_HARD is set at this point }
*/
/* 1) empty entry available? */
if (empty < ARP_TABLE_SIZE) {
i = empty;
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
}
/* 2) found recyclable stable entry? */
else if (old_stable < ARP_TABLE_SIZE) {
/* recycle oldest stable*/
i = old_stable;
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
#if ARP_QUEUEING
/* no queued packets should exist on stable entries */
LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
#endif
/* 3) found recyclable pending entry without queued packets? */
} else if (old_pending < ARP_TABLE_SIZE) {
/* recycle oldest pending */
i = old_pending;
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
#if ARP_QUEUEING
/* 4) found recyclable pending entry with queued packets? */
} else if (old_queue < ARP_TABLE_SIZE) {
/* recycle oldest pending */
i = old_queue;
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
free_undiarp_q(arp_table[i].q);
arp_table[i].q = NULL;
#endif
/* no empty or recyclable entries found */
} else {
return (s8_t)ERR_MEM;
}
/* { empty or recyclable entry found } */
LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
if (arp_table[i].state != UNDIARP_STATE_EMPTY)
{
snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
}
/* recycle entry (no-op for an already empty entry) */
arp_table[i].state = UNDIARP_STATE_EMPTY;
/* IP address given? */
if (ipaddr != NULL) {
/* set IP address */
ip_addr_set(&arp_table[i].ipaddr, ipaddr);
}
arp_table[i].ctime = 0;
#if LWIP_NETIF_HWADDRHINT
NETIF_SET_HINT(netif, i);
#else /* #if LWIP_NETIF_HWADDRHINT */
undiarp_cached_entry = i;
#endif /* #if LWIP_NETIF_HWADDRHINT */
return (err_t)i;
}
/**
* Send an ARP request for the given IP address and/or queue a packet.
*
* If the IP address was not yet in the cache, a pending ARP cache entry
* is added and an ARP request is sent for the given address. The packet
* is queued on this entry.
*
* If the IP address was already pending in the cache, a new ARP request
* is sent for the given address. The packet is queued on this entry.
*
* If the IP address was already stable in the cache, and a packet is
* given, it is directly sent and no ARP request is sent out.
*
* If the IP address was already stable in the cache, and no packet is
* given, an ARP request is sent out.
*
* @param netif The lwIP network interface on which ipaddr
* must be queried for.
* @param ipaddr The IP address to be resolved.
* @param q If non-NULL, a pbuf that must be delivered to the IP address.
* q is not freed by this function.
*
* @note q must only be ONE packet, not a packet queue!
*
* @return
* - ERR_BUF Could not make room for Ethernet header.
* - ERR_MEM Hardware address unknown, and no more ARP entries available
* to query for address or queue the packet.
* - ERR_MEM Could not queue packet due to memory shortage.
* - ERR_RTE No route to destination (no gateway to external networks).
* - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
*
*/
static err_t
undiarp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
{
err_t result = ERR_MEM;
s8_t i; /* ARP entry index */
/* non-unicast address? */
if (ip_addr_isbroadcast(ipaddr, netif) ||
ip_addr_ismulticast(ipaddr) ||
ip_addr_isany(ipaddr)) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: will not add non-unicast IP address to ARP cache\n"));
return ERR_ARG;
}
/* find entry in ARP cache, ask to create entry if queueing packet */
#if LWIP_NETIF_HWADDRHINT
i = find_entry(ipaddr, UNDIARP_TRY_HARD, netif);
#else /* LWIP_NETIF_HWADDRHINT */
i = find_entry(ipaddr, UNDIARP_TRY_HARD);
#endif /* LWIP_NETIF_HWADDRHINT */
/* could not find or create entry? */
if (i < 0) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: could not create ARP entry\n"));
if (q) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: packet dropped\n"));
ETHARP_STATS_INC(etharp.memerr);
}
return (err_t)i;
}
/* mark a fresh entry as pending (we just sent a request) */
if (arp_table[i].state == UNDIARP_STATE_EMPTY) {
arp_table[i].state = UNDIARP_STATE_PENDING;
}
/* { i is either a STABLE or (new or existing) PENDING entry } */
LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
((arp_table[i].state == UNDIARP_STATE_PENDING) ||
(arp_table[i].state == UNDIARP_STATE_STABLE)));
/* do we have a pending entry? or an implicit query request? */
if ((arp_table[i].state == UNDIARP_STATE_PENDING) || (q == NULL)) {
/* try to resolve it; send out ARP request */
result = undiarp_request(netif, ipaddr);
if (result != ERR_OK) {
/* ARP request couldn't be sent */
/* We don't re-send arp request in undiarp_tmr, but we still queue packets,
since this failure could be temporary, and the next packet calling
etharp_query again could lead to sending the queued packets. */
}
}
/* packet given? */
if (q != NULL) {
/* stable entry? */
if (arp_table[i].state == UNDIARP_STATE_STABLE) {
/* we have a valid IP->hardware address mapping */
/* send the packet */
result = undi_send_ip(netif, q, &(arp_table[i].hwaddr));
/* pending entry? (either just created or already pending */
} else if (arp_table[i].state == UNDIARP_STATE_PENDING) {
#if ARP_QUEUEING /* queue the given q packet */
struct pbuf *p;
int copy_needed = 0;
/* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
* to copy the whole queue into a new PBUF_RAM (see bug #11400)
* PBUF_ROMs can be left as they are, since ROM must not get changed. */
p = q;
while (p) {
LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
if(p->type != PBUF_ROM) {
copy_needed = 1;
break;
}
p = p->next;
}
if(copy_needed) {
/* copy the whole packet into new pbufs */
p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
if(p != NULL) {
if (pbuf_copy(p, q) != ERR_OK) {
pbuf_free(p);
p = NULL;
}
}
} else {
/* referencing the old pbuf is enough */
p = q;
pbuf_ref(p);
}
/* packet could be taken over? */
if (p != NULL) {
/* queue packet ... */
struct etharp_q_entry *new_entry;
/* allocate a new arp queue entry */
new_entry = memp_malloc(MEMP_ARP_QUEUE);
if (new_entry != NULL) {
new_entry->next = 0;
new_entry->p = p;
if(arp_table[i].q != NULL) {
/* queue was already existent, append the new entry to the end */
struct etharp_q_entry *r;
r = arp_table[i].q;
while (r->next != NULL) {
r = r->next;
}
r->next = new_entry;
} else {
/* queue did not exist, first item in queue */
arp_table[i].q = new_entry;
}
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
result = ERR_OK;
} else {
/* the pool MEMP_ARP_QUEUE is empty */
pbuf_free(p);
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
/* { result == ERR_MEM } through initialization */
}
} else {
ETHARP_STATS_INC(etharp.memerr);
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
/* { result == ERR_MEM } through initialization */
}
#else /* ARP_QUEUEING == 0 */
/* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */
/* { result == ERR_MEM } through initialization */
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q));
#endif
}
}
return result;
}
/**
* Resolve and fill-in address header for outgoing IP packet.
*
* For IP multicast and broadcast, corresponding Ethernet addresses
* are selected and the packet is transmitted on the link.
*
* For unicast addresses, the packet is submitted to etharp_query(). In
* case the IP address is outside the local network, the IP address of
* the gateway is used.
*
* @param netif The lwIP network interface which the IP packet will be sent on.
* @param q The pbuf(s) containing the IP packet to be sent.
* @param ipaddr The IP address of the packet destination.
*
* @return
* - ERR_RTE No route to destination (no gateway to external networks),
* or the return type of either etharp_query() or etharp_send_ip().
*/
static err_t
undiarp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr)
{
static __lowmem t_PXENV_UNDI_GET_MCAST_ADDR get_mcast;
hwaddr_t *dest;
if (undi_is_ethernet(netif))
return etharp_output(netif, q, ipaddr);
/* Assume unresolved hardware address */
dest = NULL;
/* Determine on destination hardware address. Broadcasts and multicasts
* are special, other IP addresses are looked up in the ARP table.
*/
if (ip_addr_isbroadcast(ipaddr, netif)) {
dest = NULL;
}
else if (ip_addr_ismulticast(ipaddr)) {
memset(&get_mcast, 0, sizeof get_mcast);
memcpy(&get_mcast.InetAddr, ipaddr, sizeof(get_mcast.InetAddr));
pxe_call(PXENV_UNDI_GET_MCAST_ADDR, &get_mcast);
dest = (hwaddr_t *)&get_mcast.MediaAddr;
}
else {
/* outside local network? */
if (!ip_addr_netcmp(ipaddr, &netif->ip_addr, &netif->netmask)) {
/* interface has default gateway? */
if (netif->gw.addr != 0) {
/* send to hardware address of default gateway IP address */
ipaddr = &(netif->gw);
/* no default gateway available */
} else {
/* no route to destination error (default gateway missing) */
return ERR_RTE;
}
}
/* queue on destination Ethernet address belonging to ipaddr */
return undiarp_query(netif, ipaddr, q);
}
/* continuation for multicast/broadcast destinations */
/* obtain source Ethernet address of the given interface */
/* send packet directly on the link */
return undi_send_ip(netif, q, dest);
}
static void get_packet_fragment(t_PXENV_UNDI_ISR *isr)
{
do {
isr->FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
pxe_call(PXENV_UNDI_ISR, &isr);
} while (isr->FuncFlag != PXENV_UNDI_ISR_OUT_RECEIVE);
}
/**
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this undiif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf *
low_level_input(t_PXENV_UNDI_ISR *isr)
{
struct pbuf *p, *q;
const char *r;
int len;
/* Obtain the size of the packet and put it into the "len"
variable. */
len = isr->FrameLength;
//printf("undiif_input, len = %d\n", len);
/* We allocate a pbuf chain of pbufs from the pool. */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if (p != NULL) {
/*
* We iterate over the pbuf chain until we have read the entire
* packet into the pbuf.
*/
r = GET_PTR(isr->Frame);
for (q = p; q != NULL; q = q->next) {
/*
* Read enough bytes to fill this pbuf in the chain. The
* available data in the pbuf is given by the q->len
* variable.
*/
char *s = q->payload;
int ql = q->len;
while (ql) {
int qb = isr->BufferLength < ql ? isr->BufferLength : ql;
if (!qb) {
/*
* Only received a partial frame, must get the next one...
*/
get_packet_fragment(isr);
r = GET_PTR(isr->Frame);
} else {
memcpy(s, r, qb);
s += qb;
r += qb;
ql -= qb;
}
}
}
LINK_STATS_INC(link.recv);
} else {
/*
* Dropped packet: we really should make sure we drain any partial
* frame here...
*/
while ((len -= isr->BufferLength) > 0)
get_packet_fragment(isr);
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
}
return p;
}
/**
* Update (or insert) a IP/MAC address pair in the ARP cache.
*
* If a pending entry is resolved, any queued packets will be sent
* at this point.
*
* @param ipaddr IP address of the inserted ARP entry.
* @param ethaddr Ethernet address of the inserted ARP entry.
* @param flags Defines behaviour:
* - ETHARP_TRY_HARD Allows ARP to insert this as a new item. If not specified,
* only existing ARP entries will be updated.
*
* @return
* - ERR_OK Succesfully updated ARP cache.
* - ERR_MEM If we could not add a new ARP entry when ETHARP_TRY_HARD was set.
* - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
*
* @see pbuf_free()
*/
static err_t
update_arp_entry(struct netif *netif, struct ip_addr *ipaddr,
hwaddr_t *lladdr, u8_t flags)
{
s8_t i;
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiif:update_arp_entry()\n"));
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiif:update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
ip4_addr1(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr),
(*lladdr)[0], (*lladdr)[1], (*lladdr)[2],
(*lladdr)[3], (*lladdr)[4], (*lladdr)[5]));
/* non-unicast address? */
if (ip_addr_isany(ipaddr) ||
ip_addr_isbroadcast(ipaddr, netif) ||
ip_addr_ismulticast(ipaddr)) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiif:update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
return ERR_ARG;
}
/* find or create ARP entry */
#if LWIP_NETIF_HWADDRHINT
i = find_entry(ipaddr, flags, netif);
#else /* LWIP_NETIF_HWADDRHINT */
i = find_entry(ipaddr, flags);
#endif /* LWIP_NETIF_HWADDRHINT */
/* bail out if no entry could be found */
if (i < 0)
return (err_t)i;
/* mark it stable */
arp_table[i].state = UNDIARP_STATE_STABLE;
/* record network interface */
arp_table[i].netif = netif;
/* insert in SNMP ARP index tree */
snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiif:update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
/* update address */
memcpy(arp_table[i].hwaddr, lladdr, netif->hwaddr_len);
/* reset time stamp */
arp_table[i].ctime = 0;
#if ARP_QUEUEING
/* this is where we will send out queued packets! */
while (arp_table[i].q != NULL) {
struct pbuf *p;
/* remember remainder of queue */
struct etharp_q_entry *q = arp_table[i].q;
/* pop first item off the queue */
arp_table[i].q = q->next;
/* get the packet pointer */
p = q->p;
/* now queue entry can be freed */
memp_free(MEMP_ARP_QUEUE, q);
/* send the queued IP packet */
undi_send_ip(netif, p, lladdr);
/* free the queued IP packet */
pbuf_free(p);
}
#endif
return ERR_OK;
}
/**
* 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 ethaddr Ethernet address of netif.
* @param p The ARP packet that arrived on netif. Is freed by this function.
*
* @return NULL
*
* @see pbuf_free()
*/
static void
undiarp_input(struct netif *netif, struct pbuf *p)
{
struct arp_hdr *hdr;
/* these are aligned properly, whereas the ARP header fields might not be */
struct ip_addr sipaddr, dipaddr;
hwaddr_t hwaddr_remote;
u8_t *hdr_ptr;
u8_t for_us;
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
/* drop short ARP packets: we have to check for p->len instead of p->tot_len here
since a struct arp_hdr is pointed to p->payload, so it musn't be chained! */
if (p->len < arp_hdr_len(netif)) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("undiarp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len,
(s16_t)SIZEOF_ETHARP_PACKET));
printf("short arp packet\n");
ETHARP_STATS_INC(etharp.lenerr);
ETHARP_STATS_INC(etharp.drop);
pbuf_free(p);
return;
}
hdr = p->payload;
/* RFC 826 "Packet Reception": */
if ((hdr->hwtype != htons(MAC_type)) ||
(hdr->hwlen != netif->hwaddr_len) ||
(hdr->protolen != sizeof(struct ip_addr)) ||
(hdr->proto != htons(ETHTYPE_IP))) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("undiarp_input: packet dropped, wrong hw type, hwlen, proto, or protolen (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen));
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
printf("malformed arp packet\n");
pbuf_free(p);
return;
}
ETHARP_STATS_INC(etharp.recv);
/* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
* structure packing (not using structure copy which breaks strict-aliasing rules). */
hdr_ptr = (unsigned char *)(hdr + 1);
memcpy(hwaddr_remote, hdr_ptr, netif->hwaddr_len);
hdr_ptr += netif->hwaddr_len;
memcpy(&sipaddr, hdr_ptr, sizeof(sipaddr));
hdr_ptr += sizeof(sipaddr);
hdr_ptr += netif->hwaddr_len;
memcpy(&dipaddr, hdr_ptr, sizeof(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, &hwaddr_remote, UNDIARP_TRY_HARD);
/* ARP message not directed to us? */
} else {
/* update the source IP address in the cache, if present */
update_arp_entry(netif, &sipaddr, &hwaddr_remote, 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 (UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: incoming ARP request\n"));
/* ARP request for our address? */
if (for_us) {
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: replying to ARP request for our IP address\n"));
/* Re-use pbuf to send ARP reply.
Since we are re-using an existing pbuf, we can't call etharp_raw since
that would allocate a new pbuf. */
hdr->opcode = htons(ARP_REPLY);
hdr_ptr = (unsigned char *)(hdr + 1);
memcpy(hdr_ptr, &netif->hwaddr, netif->hwaddr_len);
hdr_ptr += netif->hwaddr_len;
memcpy(hdr_ptr, &dipaddr, sizeof(dipaddr));
hdr_ptr += sizeof(dipaddr);
memcpy(hdr_ptr, &hwaddr_remote, netif->hwaddr_len);
hdr_ptr += netif->hwaddr_len;
memcpy(hdr_ptr, &sipaddr, sizeof(sipaddr));
/* return ARP reply */
undi_send_arp(netif, p, &hwaddr_remote);
/* we are not configured? */
} else if (netif->ip_addr.addr == 0) {
/* { for_us == 0 and netif->ip_addr.addr == 0 } */
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_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(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: ARP request was not for us.\n"));
}
break;
case ARP_REPLY:
/* ARP reply. We already updated the ARP cache earlier. */
LWIP_DEBUGF(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_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(UNDIIF_ARP_DEBUG | UNDIIF_DEBUG | LWIP_DBG_TRACE, ("undiarp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
ETHARP_STATS_INC(etharp.err);
break;
}
/* free ARP packet */
pbuf_free(p);
}
/**
* This function should be called when a packet is ready to be read
* from the interface. It uses the function low_level_input() that
* should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this undiif
*/
void undiif_input(t_PXENV_UNDI_ISR *isr)
{
struct pbuf *p;
u8_t undi_prot;
u16_t llhdr_len;
/* From the first isr capture the essential information */
undi_prot = isr->ProtType;
llhdr_len = isr->FrameHeaderLength;
/* move received packet into a new pbuf */
p = low_level_input(isr);
/* no packet could be read, silently ignore this */
if (p == NULL) return;
if (undi_is_ethernet(&undi_netif)) {
/* points to packet payload, which starts with an Ethernet header */
struct eth_hdr *ethhdr = p->payload;
#if LWIP_UNDIIF_DBG(UNDIIF_ID_FULL_DEBUG)
char *str = malloc(UNIDIF_ID_STRLEN);
int strpos = 0;
strpos += snprintf(str + strpos, UNIDIF_ID_STRLEN - strpos,
"undi recv thd '%s'\n", current()->name);
strpos += snprintf_eth_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
"undi", ethhdr, 'r', '0', "");
strpos += snprintf_arp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" arp", ethhdr, 'r', '0', "");
strpos += snprintf_ip_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" ip", ethhdr, 'r', '0', "");
strpos += snprintf_icmp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" icmp", ethhdr, 'r', '0', "");
strpos += snprintf_tcp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" tcp", ethhdr, 'r', '0', "");
strpos += snprintf_udp_hdr(str + strpos, UNIDIF_ID_STRLEN - strpos,
" udp", ethhdr, 'r', '0', "");
LWIP_DEBUGF(UNDIIF_ID_FULL_DEBUG, ("%s", str));
free(str);
#endif /* UNDIIF_ID_FULL_DEBUG */
switch (htons(ethhdr->type)) {
/* IP or ARP packet? */
case ETHTYPE_IP:
case ETHTYPE_ARP:
#if PPPOE_SUPPORT
/* PPPoE packet? */
case ETHTYPE_PPPOEDISC:
case ETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
/* full packet send to tcpip_thread to process */
if (tcpip_input(p, &undi_netif)!=ERR_OK)
{ LWIP_DEBUGF(UNDIIF_NET_DEBUG | UNDIIF_DEBUG, ("undiif_input: IP input error\n"));
pbuf_free(p);
p = NULL;
}
break;
default:
pbuf_free(p);
p = NULL;
break;
}
} else {
if (pbuf_header(p, -(s16_t)llhdr_len)) {
LWIP_ASSERT("Can't move link level header in packet", 0);
pbuf_free(p);
p = NULL;
} else {
switch(undi_prot) {
case P_IP:
/* pass to IP layer */
tcpip_input(p, &undi_netif);
break;
case P_ARP:
/* pass p to ARP module */
undiarp_input(&undi_netif, p);
break;
default:
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
pbuf_free(p);
p = NULL;
break;
}
}
}
}
/**
* Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this undiif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
static err_t
undiif_init(struct netif *netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "undi";
#endif /* LWIP_NETIF_HOSTNAME */
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
netif->state = NULL; /* Private pointer if we need it */
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
netif->output = undiarp_output;
netif->linkoutput = undi_send_unknown;
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw)
{
err_t err;
// This should be done *after* the threading system and receive thread
// have both been started.
dprintf("undi_netif: ip %d.%d.%d.%d netmask %d.%d.%d.%d gw %d.%d.%d.%d\n",
((uint8_t *)&ip)[0],
((uint8_t *)&ip)[1],
((uint8_t *)&ip)[2],
((uint8_t *)&ip)[3],
((uint8_t *)&netmask)[0],
((uint8_t *)&netmask)[1],
((uint8_t *)&netmask)[2],
((uint8_t *)&netmask)[3],
((uint8_t *)&gw)[0],
((uint8_t *)&gw)[1],
((uint8_t *)&gw)[2],
((uint8_t *)&gw)[3]);
err = netifapi_netif_add(&undi_netif,
(struct ip_addr *)&ip, (struct ip_addr *)&netmask, (struct ip_addr *)&gw,
NULL, undiif_init, tcpip_input);
if (err)
return err;
netif_set_up(&undi_netif);
netif_set_default(&undi_netif); /* Make this interface the default route */
return ERR_OK;
}