diff options
author | Fabien Chouteau <chouteau@adacore.com> | 2011-08-03 12:52:54 +0200 |
---|---|---|
committer | Jan Kiszka <jan.kiszka@siemens.com> | 2011-08-03 12:57:06 +0200 |
commit | 1a0ca1e1f6011a8623ec0653a1b35bbfc3f576c9 (patch) | |
tree | 1a65c353785ecb37b6444403dfd07fca535d5b55 /slirp/slirp.c | |
parent | bafc72ab01cd5a058f1d07d1bb80ae0b27ff190a (diff) |
Simple ARP table
This patch adds a simple ARP table in Slirp and also adds handling of
gratuitous ARP requests.
Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Diffstat (limited to 'slirp/slirp.c')
-rw-r--r-- | slirp/slirp.c | 63 |
1 files changed, 16 insertions, 47 deletions
diff --git a/slirp/slirp.c b/slirp/slirp.c index df787ea1d..4a9a4d512 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -31,11 +31,11 @@ struct in_addr loopback_addr; /* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */ -static const uint8_t special_ethaddr[6] = { +static const uint8_t special_ethaddr[ETH_ALEN] = { 0x52, 0x55, 0x00, 0x00, 0x00, 0x00 }; -static const uint8_t zero_ethaddr[6] = { 0, 0, 0, 0, 0, 0 }; +static const uint8_t zero_ethaddr[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; /* XXX: suppress those select globals */ fd_set *global_readfds, *global_writefds, *global_xfds; @@ -599,42 +599,8 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, global_xfds = NULL; } -#define ETH_ALEN 6 -#define ETH_HLEN 14 - -#define ETH_P_IP 0x0800 /* Internet Protocol packet */ -#define ETH_P_ARP 0x0806 /* Address Resolution packet */ - -#define ARPOP_REQUEST 1 /* ARP request */ -#define ARPOP_REPLY 2 /* ARP reply */ - -struct ethhdr -{ - unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ - unsigned char h_source[ETH_ALEN]; /* source ether addr */ - unsigned short h_proto; /* packet type ID field */ -}; - -struct arphdr -{ - unsigned short ar_hrd; /* format of hardware address */ - unsigned short ar_pro; /* format of protocol address */ - unsigned char ar_hln; /* length of hardware address */ - unsigned char ar_pln; /* length of protocol address */ - unsigned short ar_op; /* ARP opcode (command) */ - - /* - * Ethernet looks like this : This bit is variable sized however... - */ - unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ - uint32_t ar_sip; /* sender IP address */ - unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ - uint32_t ar_tip ; /* target IP address */ -} __attribute__((packed)); - static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) { - struct ethhdr *eh = (struct ethhdr *)pkt; struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN); uint8_t arp_reply[max(ETH_HLEN + sizeof(struct arphdr), 64)]; struct ethhdr *reh = (struct ethhdr *)arp_reply; @@ -645,6 +611,12 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) ar_op = ntohs(ah->ar_op); switch(ar_op) { case ARPOP_REQUEST: + if (ah->ar_tip == ah->ar_sip) { + /* Gratuitous ARP */ + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); + return; + } + if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) == slirp->vnetwork_addr.s_addr) { if (ah->ar_tip == slirp->vnameserver_addr.s_addr || @@ -657,8 +629,8 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) return; arp_ok: memset(arp_reply, 0, sizeof(arp_reply)); - /* XXX: make an ARP request to have the client address */ - memcpy(slirp->client_ethaddr, eh->h_source, ETH_ALEN); + + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); /* ARP request for alias/dns mac address */ memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); @@ -679,11 +651,7 @@ static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) } break; case ARPOP_REPLY: - /* reply to request of client mac address ? */ - if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN) && - ah->ar_sip == slirp->client_ipaddr.s_addr) { - memcpy(slirp->client_ethaddr, ah->ar_sha, ETH_ALEN); - } + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); break; default: break; @@ -729,15 +697,16 @@ void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len) { uint8_t buf[1600]; struct ethhdr *eh = (struct ethhdr *)buf; + uint8_t ethaddr[ETH_ALEN]; + const struct ip *iph = (const struct ip *)ip_data; if (ip_data_len + ETH_HLEN > sizeof(buf)) return; - - if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN)) { + + if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) { uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)]; struct ethhdr *reh = (struct ethhdr *)arp_req; struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN); - const struct ip *iph = (const struct ip *)ip_data; /* If the client addr is not known, there is no point in sending the packet to it. Normally the sender should have @@ -765,7 +734,7 @@ void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len) slirp->client_ipaddr = iph->ip_dst; slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); } else { - memcpy(eh->h_dest, slirp->client_ethaddr, ETH_ALEN); + memcpy(eh->h_dest, ethaddr, ETH_ALEN); memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); /* XXX: not correct */ memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); |