diff options
Diffstat (limited to 'roms/ipxe/src/net/arp.c')
| -rw-r--r-- | roms/ipxe/src/net/arp.c | 220 | 
1 files changed, 220 insertions, 0 deletions
| diff --git a/roms/ipxe/src/net/arp.c b/roms/ipxe/src/net/arp.c new file mode 100644 index 00000000..261e681e --- /dev/null +++ b/roms/ipxe/src/net/arp.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <byteswap.h> +#include <errno.h> +#include <ipxe/if_ether.h> +#include <ipxe/if_arp.h> +#include <ipxe/iobuf.h> +#include <ipxe/netdevice.h> +#include <ipxe/neighbour.h> +#include <ipxe/arp.h> + +/** @file + * + * Address Resolution Protocol + * + * This file implements the address resolution protocol as defined in + * RFC826.  The implementation is media-independent and + * protocol-independent; it is not limited to Ethernet or to IPv4. + * + */ + +struct net_protocol arp_protocol __net_protocol; + +/** + * Transmit ARP request + * + * @v netdev		Network device + * @v net_protocol	Network-layer protocol + * @v net_dest		Destination network-layer address + * @v net_source	Source network-layer address + * @ret rc		Return status code + */ +static int arp_tx_request ( struct net_device *netdev, +			    struct net_protocol *net_protocol, +			    const void *net_dest, const void *net_source ) { +	struct ll_protocol *ll_protocol = netdev->ll_protocol; +	struct io_buffer *iobuf; +	struct arphdr *arphdr; +	int rc; + +	/* Allocate ARP packet */ +	iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *arphdr ) + +			    ( 2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) ) ); +	if ( ! iobuf ) +		return -ENOMEM; +	iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); + +	/* Build up ARP request */ +	arphdr = iob_put ( iobuf, sizeof ( *arphdr ) ); +	arphdr->ar_hrd = ll_protocol->ll_proto; +	arphdr->ar_hln = ll_protocol->ll_addr_len; +	arphdr->ar_pro = net_protocol->net_proto; +	arphdr->ar_pln = net_protocol->net_addr_len; +	arphdr->ar_op = htons ( ARPOP_REQUEST ); +	memcpy ( iob_put ( iobuf, ll_protocol->ll_addr_len ), +		 netdev->ll_addr, ll_protocol->ll_addr_len ); +	memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), +		 net_source, net_protocol->net_addr_len ); +	memset ( iob_put ( iobuf, ll_protocol->ll_addr_len ), +		 0, ll_protocol->ll_addr_len ); +	memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), +		 net_dest, net_protocol->net_addr_len ); + +	/* Transmit ARP request */ +	if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol, +			     netdev->ll_broadcast, netdev->ll_addr ) ) != 0 ) { +		DBGC ( netdev, "ARP %s %s %s could not transmit request: %s\n", +		       netdev->name, net_protocol->name, +		       net_protocol->ntoa ( net_dest ), strerror ( rc ) ); +		return rc; +	} + +	return 0; +} + +/** ARP neighbour discovery protocol */ +struct neighbour_discovery arp_discovery = { +	.name = "ARP", +	.tx_request = arp_tx_request, +}; + +/** + * Identify ARP protocol + * + * @v net_proto			Network-layer protocol, in network-endian order + * @ret arp_net_protocol	ARP protocol, or NULL + * + */ +static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) { +	struct arp_net_protocol *arp_net_protocol; + +	for_each_table_entry ( arp_net_protocol, ARP_NET_PROTOCOLS ) { +		if ( arp_net_protocol->net_protocol->net_proto == net_proto ) +			return arp_net_protocol; +	} +	return NULL; +} + +/** + * Process incoming ARP packets + * + * @v iobuf		I/O buffer + * @v netdev		Network device + * @v ll_source		Link-layer source address + * @v flags		Packet flags + * @ret rc		Return status code + */ +static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev, +		    const void *ll_dest __unused, +		    const void *ll_source __unused, +		    unsigned int flags __unused ) { +	struct arphdr *arphdr = iobuf->data; +	struct arp_net_protocol *arp_net_protocol; +	struct net_protocol *net_protocol; +	struct ll_protocol *ll_protocol; +	int rc; + +	/* Identify network-layer and link-layer protocols */ +	arp_net_protocol = arp_find_protocol ( arphdr->ar_pro ); +	if ( ! arp_net_protocol ) { +		rc = -EPROTONOSUPPORT; +		goto done; +	} +	net_protocol = arp_net_protocol->net_protocol; +	ll_protocol = netdev->ll_protocol; + +	/* Sanity checks */ +	if ( ( arphdr->ar_hrd != ll_protocol->ll_proto ) || +	     ( arphdr->ar_hln != ll_protocol->ll_addr_len ) || +	     ( arphdr->ar_pln != net_protocol->net_addr_len ) ) { +		rc = -EINVAL; +		goto done; +	} + +	/* Update neighbour cache entry for this sender, if any */ +	neighbour_update ( netdev, net_protocol, arp_sender_pa ( arphdr ), +			   arp_sender_ha ( arphdr ) ); + +	/* If it's not a request, there's nothing more to do */ +	if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) ) { +		rc = 0; +		goto done; +	} + +	/* See if we own the target protocol address */ +	if ( arp_net_protocol->check ( netdev, arp_target_pa ( arphdr ) ) != 0){ +		rc = 0; +		goto done; +	} + +	/* Change request to a reply */ +	DBGC2 ( netdev, "ARP %s %s %s reply => %s %s\n", +		netdev->name, net_protocol->name, +		net_protocol->ntoa ( arp_target_pa ( arphdr ) ), +		ll_protocol->name, ll_protocol->ntoa ( netdev->ll_addr ) ); +	arphdr->ar_op = htons ( ARPOP_REPLY ); +	memswap ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ), +		 arphdr->ar_hln + arphdr->ar_pln ); +	memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln ); + +	/* Send reply */ +	if ( ( rc = net_tx ( iob_disown ( iobuf ), netdev, &arp_protocol, +			     arp_target_ha ( arphdr ), +			     netdev->ll_addr ) ) != 0 ) { +		DBGC ( netdev, "ARP %s %s %s could not transmit reply: %s\n", +		       netdev->name, net_protocol->name, +		       net_protocol->ntoa ( arp_target_pa ( arphdr ) ), +		       strerror ( rc ) ); +		goto done; +	} + +	/* Success */ +	rc = 0; + + done: +	free_iob ( iobuf ); +	return rc; +} + +/** + * Transcribe ARP address + * + * @v net_addr	ARP address + * @ret string	"<ARP>" + * + * This operation is meaningless for the ARP protocol. + */ +static const char * arp_ntoa ( const void *net_addr __unused ) { +	return "<ARP>"; +} + +/** ARP network protocol */ +struct net_protocol arp_protocol __net_protocol = { +	.name = "ARP", +	.net_proto = htons ( ETH_P_ARP ), +	.rx = arp_rx, +	.ntoa = arp_ntoa, +}; | 
