One of the most compelling issues of Ethernet communication is the association between hardware addresses (the interface’s unique id) and IP numbers. Most protocols have a similar problem, but I’m going to pinpoint only the Ethernet-like case here. I’ll try to offer a complete description of the issue, so I’m going to show three situations: ARP, Ethernet headers without ARP (like plip), and non-Ethernet headers.
The usual way to deal with address resolution is by using ARP, the
Address Resolution Protocol. Fortunately, ARP is managed by
the kernel, and an Ethernet interface doesn’t need to do anything
special to support ARP. As long as dev->addr
and
dev->addr_len
are correctly assigned at open time, the driver
doesn’t need to worry about resolving IP numbers to physical addresses;
ether_setup assigns the correct device methods to
dev->hard_header
and dev->rebuild_header
.
When a packet is built, the Ethernet header is laid out
by dev->hard_header
, and it is filled later by
dev->rebuild_header
, which uses the ARP protocol
to map unknown IP numbers to addresses. The driver writer doesn’t need
to know the details of this process to build a working driver.
Simple point-to-point network interfaces like plip might benefit from using Ethernet headers, while avoiding the overhead of sending ARP packets back and forth. The sample code in snull falls into this class of network devices. snull cannot use ARP because the driver changes IP addresses in packets being transmitted, and ARP packets exchange IP addresses as well.
If your device wants to use the usual hardware header without
running ARP, you need to override the default
dev->rebuild_header
method.
This is how snull implements it, as a simple
function made up of three statements:
int snull_rebuild_header(void *buff, struct device *dev, unsigned long dst, struct sk_buff *skb) { struct ethhdr *eth = (struct ethhdr *)buff; memcpy(eth->h_source, dev->dev_addr, dev->addr_len); memcpy(eth->h_dest, dev->dev_addr, dev->addr_len); eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */ return 0; }
As a matter of fact, there’s no actual need to specify the
contents of eth->h_source
and eth->h_dest
, because the
values are used only for the physical delivery of the packet, and a
point-to-point link is guaranteed to deliver the packet to its
destination independent of the hardware addresses. The reason
snull rebuilds the headers is to show you how a rebuild
function should be implemented for a real network interface when
eth_rebuild_header can’t be used.
When a packet is received by the interface, the hardware header is used only by eth_type_trans. We have already seen this call in snull_rx:
skb->protocol = eth_type_trans(skb, dev);
The function extracts the protocol identifier
(ETH_P_IP
in this case) from the Ethernet header; it also
assigns skb->mac.raw
, removes the hardware header from
packet data, and sets skb->pkt_type
. This last item
defaults to PACKET_HOST
at skb
allocation
(which indicates that the packet is directed to this host), but it can
be changed to one of the other values according to the Ethernet
destination address.
If your interface is a point-to-point link, you won’t enjoy
receiving unexpected multicast packets. To avoid this, you must
remember that a destination address whose first octet has 0 as the least
significant bit (LSB) is
directed to a single host (i.e., it is either PACKET_HOST
or
PACKET_OTHERHOST
). The plip driver uses 0xfc as the
first octet of its hardware address, while snull uses 0x00.
Both addresses result in a working Ethernet-like point-to-point
link.
This section briefly describes how hardware headers can be used to encapsulate relevant information. If you need to know the details, you can extract them from the kernel sources or the technical documentation for the particular transmission medium. We have just seen that the hardware header contains some information in addition to the destination address, the most important being the communication protocol.
However, not all information has to be provided by every
protocol. A point-to-point link like plip or
snull could avoid transferring the whole Ethernet
header without losing generality. The hard_header device
method receives the delivery information—both protocol-level and
hardware addresses—from the kernel. It also receives the 16-bit
protocol number. IP, for example, is identified by
ETH_P_IP
. The driver is expected to correctly deliver both
the packet data and the protocol number to the receiving host. A
point-to-point link could omit addresses from its hardware header,
transferring only the protocol number, because delivery is guaranteed
independent of the source and destination addresses. An IP-only link
could even avoid transmitting any hardware header whatsoever. In both
cases, all the work can be performed by hard_header,
leaving rebuild_header nothing to do except return
0
.
When the packet is picked up at the other end of the link, the
receiving function is expected to correctly set skb->protocol
,
skb->pkt_type
, and skb->mac.raw
.
skb->mac.raw
is a char pointer used by the
address-resolution mechanism implemented in higher layers of the
networking code (for instance, net/ipv4/arp.c
). It must point
to a machine address that matches dev->type
. The possible
values for the device type are defined in <linux/if_arp.h>
;
Ethernet interfaces use ARPHRD_ETHER
. For example, here
is how eth_type_trans deals with the Ethernet header for received
packets:
skb->mac.raw=skb->data; skb_pull(skb,dev->hard_header_len);
In the simplest case (a point-to-point link with no headers),
skb->mac.raw
can point to a static buffer containing the hardware
address of this interface, protocol
can be set to
ETH_P_IP
, and packet_type
can be
left with its default value of PACKET_HOST
.
3.144.33.41