aboutsummaryrefslogtreecommitdiff
path: root/net/ethernet/eth.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ethernet/eth.c')
-rw-r--r--net/ethernet/eth.c200
1 files changed, 133 insertions, 67 deletions
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 2d31bf3f05c..5dc638cad2e 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -12,14 +12,14 @@
* Mark Evans, <evansmp@uhura.aston.ac.uk>
* Florian La Roche, <rzsfl@rz.uni-sb.de>
* Alan Cox, <gw4pts@gw4pts.ampr.org>
- *
+ *
* Fixes:
* Mr Linux : Arp problems
* Alan Cox : Generic queue tidyup (very tiny here)
* Alan Cox : eth_header ntohs should be htons
* Alan Cox : eth_rebuild_header missing an htons and
* minor other things.
- * Tegge : Arp bug fixes.
+ * Tegge : Arp bug fixes.
* Florian : Removed many unnecessary functions, code cleanup
* and changes for new arp and skbuff.
* Alan Cox : Redid header building to reflect new format.
@@ -40,7 +40,6 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
@@ -58,8 +57,8 @@
#include <net/sock.h>
#include <net/ipv6.h>
#include <net/ip.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
+#include <net/dsa.h>
+#include <linux/uaccess.h>
__setup("ether=", netdev_boot_setup);
@@ -73,15 +72,16 @@ __setup("ether=", netdev_boot_setup);
* @len: packet length (<= skb->len)
*
*
- * Set the protocol type. For a packet of type ETH_P_802_3 we put the length
- * in here instead. It is up to the 802.2 layer to carry protocol information.
+ * Set the protocol type. For a packet of type ETH_P_802_3/2 we put the length
+ * in here instead.
*/
-int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
- void *daddr, void *saddr, unsigned len)
+int eth_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type,
+ const void *daddr, const void *saddr, unsigned int len)
{
struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
- if (type != ETH_P_802_3)
+ if (type != ETH_P_802_3 && type != ETH_P_802_2)
eth->h_proto = htons(type);
else
eth->h_proto = htons(len);
@@ -92,10 +92,10 @@ int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
if (!saddr)
saddr = dev->dev_addr;
- memcpy(eth->h_source, saddr, dev->addr_len);
+ memcpy(eth->h_source, saddr, ETH_ALEN);
if (daddr) {
- memcpy(eth->h_dest, daddr, dev->addr_len);
+ memcpy(eth->h_dest, daddr, ETH_ALEN);
return ETH_HLEN;
}
@@ -104,12 +104,13 @@ int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type,
*/
if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
- memset(eth->h_dest, 0, dev->addr_len);
+ memset(eth->h_dest, 0, ETH_ALEN);
return ETH_HLEN;
}
return -ETH_HLEN;
}
+EXPORT_SYMBOL(eth_header);
/**
* eth_rebuild_header- rebuild the Ethernet MAC header.
@@ -128,20 +129,21 @@ int eth_rebuild_header(struct sk_buff *skb)
switch (eth->h_proto) {
#ifdef CONFIG_INET
- case __constant_htons(ETH_P_IP):
+ case htons(ETH_P_IP):
return arp_find(eth->h_dest, skb);
#endif
default:
- printk(KERN_DEBUG
+ netdev_dbg(dev,
"%s: unable to resolve type %X addresses.\n",
- dev->name, (int)eth->h_proto);
+ dev->name, ntohs(eth->h_proto));
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
break;
}
return 0;
}
+EXPORT_SYMBOL(eth_rebuild_header);
/**
* eth_type_trans - determine the packet's protocol ID.
@@ -154,45 +156,48 @@ int eth_rebuild_header(struct sk_buff *skb)
*/
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
- struct ethhdr *eth;
- unsigned char *rawp;
+ unsigned short _service_access_point;
+ const unsigned short *sap;
+ const struct ethhdr *eth;
- skb->mac.raw = skb->data;
- skb_pull(skb, ETH_HLEN);
+ skb->dev = dev;
+ skb_reset_mac_header(skb);
+ skb_pull_inline(skb, ETH_HLEN);
eth = eth_hdr(skb);
- if (is_multicast_ether_addr(eth->h_dest)) {
- if (!compare_ether_addr(eth->h_dest, dev->broadcast))
+ if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
+ if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
}
+ else if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
+ dev->dev_addr)))
+ skb->pkt_type = PACKET_OTHERHOST;
/*
- * This ALLMULTI check should be redundant by 1.4
- * so don't forget to remove it.
- *
- * Seems, you forgot to remove it. All silly devices
- * seems to set IFF_PROMISC.
+ * Some variants of DSA tagging don't have an ethertype field
+ * at all, so we check here whether one of those tagging
+ * variants has been configured on the receiving interface,
+ * and if so, set skb->protocol without looking at the packet.
*/
+ if (unlikely(netdev_uses_dsa_tags(dev)))
+ return htons(ETH_P_DSA);
- else if (1 /*dev->flags&IFF_PROMISC */ ) {
- if (unlikely(compare_ether_addr(eth->h_dest, dev->dev_addr)))
- skb->pkt_type = PACKET_OTHERHOST;
- }
+ if (unlikely(netdev_uses_trailer_tags(dev)))
+ return htons(ETH_P_TRAILER);
- if (ntohs(eth->h_proto) >= 1536)
+ if (likely(ntohs(eth->h_proto) >= ETH_P_802_3_MIN))
return eth->h_proto;
- rawp = skb->data;
-
/*
* This is a magic hack to spot IPX packets. Older Novell breaks
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
* layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
* won't work for fault tolerant netware but does for the rest.
*/
- if (*(unsigned short *)rawp == 0xFFFF)
+ sap = skb_header_pointer(skb, 0, sizeof(*sap), &_service_access_point);
+ if (sap && *sap == 0xFFFF)
return htons(ETH_P_802_3);
/*
@@ -207,37 +212,40 @@ EXPORT_SYMBOL(eth_type_trans);
* @skb: packet to extract header from
* @haddr: destination buffer
*/
-static int eth_header_parse(struct sk_buff *skb, unsigned char *haddr)
+int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr)
{
- struct ethhdr *eth = eth_hdr(skb);
+ const struct ethhdr *eth = eth_hdr(skb);
memcpy(haddr, eth->h_source, ETH_ALEN);
return ETH_ALEN;
}
+EXPORT_SYMBOL(eth_header_parse);
/**
* eth_header_cache - fill cache entry from neighbour
* @neigh: source neighbour
* @hh: destination cache entry
+ * @type: Ethernet type field
+ *
* Create an Ethernet header template from the neighbour.
*/
-int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh)
+int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type)
{
- __be16 type = hh->hh_type;
struct ethhdr *eth;
- struct net_device *dev = neigh->dev;
+ const struct net_device *dev = neigh->dev;
eth = (struct ethhdr *)
(((u8 *) hh->hh_data) + (HH_DATA_OFF(sizeof(*eth))));
- if (type == __constant_htons(ETH_P_802_3))
+ if (type == htons(ETH_P_802_3))
return -1;
eth->h_proto = type;
- memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
- memcpy(eth->h_dest, neigh->ha, dev->addr_len);
+ memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
+ memcpy(eth->h_dest, neigh->ha, ETH_ALEN);
hh->hh_len = ETH_HLEN;
return 0;
}
+EXPORT_SYMBOL(eth_header_cache);
/**
* eth_header_cache_update - update cache entry
@@ -247,30 +255,66 @@ int eth_header_cache(struct neighbour *neigh, struct hh_cache *hh)
*
* Called by Address Resolution module to notify changes in address.
*/
-void eth_header_cache_update(struct hh_cache *hh, struct net_device *dev,
- unsigned char *haddr)
+void eth_header_cache_update(struct hh_cache *hh,
+ const struct net_device *dev,
+ const unsigned char *haddr)
{
memcpy(((u8 *) hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)),
- haddr, dev->addr_len);
+ haddr, ETH_ALEN);
}
+EXPORT_SYMBOL(eth_header_cache_update);
+
+/**
+ * eth_prepare_mac_addr_change - prepare for mac change
+ * @dev: network device
+ * @p: socket address
+ */
+int eth_prepare_mac_addr_change(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ if (!(dev->priv_flags & IFF_LIVE_ADDR_CHANGE) && netif_running(dev))
+ return -EBUSY;
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+ return 0;
+}
+EXPORT_SYMBOL(eth_prepare_mac_addr_change);
+
+/**
+ * eth_commit_mac_addr_change - commit mac change
+ * @dev: network device
+ * @p: socket address
+ */
+void eth_commit_mac_addr_change(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+}
+EXPORT_SYMBOL(eth_commit_mac_addr_change);
/**
* eth_mac_addr - set new Ethernet hardware address
* @dev: network device
* @p: socket address
+ *
* Change hardware address of device.
*
* This doesn't change hardware matching, so needs to be overridden
* for most real devices.
*/
-static int eth_mac_addr(struct net_device *dev, void *p)
+int eth_mac_addr(struct net_device *dev, void *p)
{
- struct sockaddr *addr = p;
- if (netif_running(dev))
- return -EBUSY;
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ int ret;
+
+ ret = eth_prepare_mac_addr_change(dev, p);
+ if (ret < 0)
+ return ret;
+ eth_commit_mac_addr_change(dev, p);
return 0;
}
+EXPORT_SYMBOL(eth_mac_addr);
/**
* eth_change_mtu - set new MTU size
@@ -280,45 +324,60 @@ static int eth_mac_addr(struct net_device *dev, void *p)
* Allow changing MTU size. Needs to be overridden for devices
* supporting jumbo frames.
*/
-static int eth_change_mtu(struct net_device *dev, int new_mtu)
+int eth_change_mtu(struct net_device *dev, int new_mtu)
{
if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
+EXPORT_SYMBOL(eth_change_mtu);
+
+int eth_validate_addr(struct net_device *dev)
+{
+ if (!is_valid_ether_addr(dev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ return 0;
+}
+EXPORT_SYMBOL(eth_validate_addr);
+
+const struct header_ops eth_header_ops ____cacheline_aligned = {
+ .create = eth_header,
+ .parse = eth_header_parse,
+ .rebuild = eth_rebuild_header,
+ .cache = eth_header_cache,
+ .cache_update = eth_header_cache_update,
+};
/**
* ether_setup - setup Ethernet network device
* @dev: network device
+ *
* Fill in the fields of the device structure with Ethernet-generic values.
*/
void ether_setup(struct net_device *dev)
{
- dev->change_mtu = eth_change_mtu;
- dev->hard_header = eth_header;
- dev->rebuild_header = eth_rebuild_header;
- dev->set_mac_address = eth_mac_addr;
- dev->hard_header_cache = eth_header_cache;
- dev->header_cache_update= eth_header_cache_update;
- dev->hard_header_parse = eth_header_parse;
-
+ dev->header_ops = &eth_header_ops;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
- dev->tx_queue_len = 1000; /* Ethernet wants good queues */
+ dev->tx_queue_len = 1000; /* Ethernet wants good queues */
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
-
+ dev->priv_flags |= IFF_TX_SKB_SHARING;
+
memset(dev->broadcast, 0xFF, ETH_ALEN);
}
EXPORT_SYMBOL(ether_setup);
/**
- * alloc_etherdev - Allocates and sets up an Ethernet device
+ * alloc_etherdev_mqs - Allocates and sets up an Ethernet device
* @sizeof_priv: Size of additional driver-private structure to be allocated
* for this Ethernet device
+ * @txqs: The number of TX queues this device has.
+ * @rxqs: The number of RX queues this device has.
*
* Fill in the fields of the device structure with Ethernet-generic
* values. Basically does everything except registering the device.
@@ -328,8 +387,15 @@ EXPORT_SYMBOL(ether_setup);
* this private data area.
*/
-struct net_device *alloc_etherdev(int sizeof_priv)
+struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs,
+ unsigned int rxqs)
+{
+ return alloc_netdev_mqs(sizeof_priv, "eth%d", ether_setup, txqs, rxqs);
+}
+EXPORT_SYMBOL(alloc_etherdev_mqs);
+
+ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len)
{
- return alloc_netdev(sizeof_priv, "eth%d", ether_setup);
+ return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr);
}
-EXPORT_SYMBOL(alloc_etherdev);
+EXPORT_SYMBOL(sysfs_format_mac);