078719b32865048f182d5c948e3865f8715682e3
[people/oremanj/gpxe.git] / src / net / ethernet.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdint.h>
20 #include <string.h>
21 #include <byteswap.h>
22 #include <assert.h>
23 #include <gpxe/if_ether.h>
24 #include <gpxe/netdevice.h>
25 #include <gpxe/pkbuff.h>
26 #include <gpxe/arp.h>
27
28 /** @file
29  *
30  * Ethernet protocol
31  *
32  */
33
34 /** Ethernet broadcast MAC address */
35 static uint8_t eth_broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
36
37 /**
38  * Build Ethernet link-layer header
39  *
40  * @v netdev    Network device
41  * @v pkb       Packet buffer
42  * @ret rc      Return status code
43  *
44  * This constructs the Ethernet link-layer header (destination MAC,
45  * source MAC, network-layer protocol) based on the metadata found in
46  * @c pkb.
47  *
48  * If the destination MAC address cannot be determined, an ARP request
49  * is sent for the requested network-layer address instead.
50  */
51 int eth_build_llh ( struct net_device *netdev, struct pk_buff *pkb ) {
52         struct ethhdr *ethhdr = pkb->data;
53         const void *eth_dest;
54         int rc;
55
56         /* Do the easy bits */
57         ethhdr->h_protocol = pkb->net_proto;
58         memcpy ( ethhdr->h_source, netdev->ll_addr,
59                  sizeof ( ethhdr->h_source ) );
60
61         /* Work out the destination MAC address */
62         if ( pkb->flags & PKB_FL_RAW_NET_ADDR ) {
63                 eth_dest = pkb->net_addr;
64         } else if ( pkb->flags & PKB_FL_BROADCAST ) {
65                 eth_dest = eth_broadcast;
66         } else if ( pkb->flags & PKB_FL_MULTICAST ) {
67                 /* IP multicast is a special case; there exists a
68                  * direct mapping from IP address to MAC address
69                  */
70                 assert ( pkb->net_proto == htons ( ETH_P_IP ) );
71                 ethhdr->h_dest[0] = 0x01;
72                 ethhdr->h_dest[1] = 0x00;
73                 ethhdr->h_dest[2] = 0x5e;
74                 ethhdr->h_dest[3] = *( ( char * ) pkb->net_addr + 1 ) & 0x7f;
75                 ethhdr->h_dest[4] = *( ( char * ) pkb->net_addr + 2 );
76                 ethhdr->h_dest[5] = *( ( char * ) pkb->net_addr + 3 );
77                 eth_dest = ethhdr->h_dest;
78         } else {
79                 /* Otherwise, look up the address using ARP */
80                 if ( ( rc = arp_resolve ( netdev, pkb, &eth_dest ) ) != 0 )
81                         return rc;
82         }
83
84         /* Fill in destination MAC address */
85         memcpy ( ethhdr->h_dest, eth_dest, sizeof ( ethhdr->h_dest ) );
86
87         return 0;
88 }
89
90 /**
91  * Parse Ethernet link-layer header
92  *
93  * @v netdev    Network device
94  * @v pkb       Packet buffer
95  * @ret rc      Return status code
96  *
97  * This parses the Ethernet link-layer header (destination MAC, source
98  * MAC, network-layer protocol) and fills in the metadata in @c pkb.
99  */
100 int eth_parse_llh ( struct net_device *netdev __unused, struct pk_buff *pkb ) {
101         struct ethhdr *ethhdr = pkb->data;
102
103         pkb->net_proto = ethhdr->h_protocol;
104         pkb->flags = PKB_FL_RAW_NET_ADDR;
105         pkb->net_addr_len = sizeof ( ethhdr->h_dest );
106         pkb->net_addr = ethhdr->h_dest;
107
108         if ( memcmp ( ethhdr->h_dest, eth_broadcast,
109                       sizeof ( ethhdr->h_dest ) ) == 0 ) {
110                 pkb->flags |= PKB_FL_BROADCAST;
111         } else if ( ethhdr->h_dest[0] & 0x01 ) {
112                 pkb->flags |= PKB_FL_MULTICAST;
113         }
114
115         return 0;
116 }