diff options
| -rw-r--r-- | include/linux/etherdevice.h | 42 | ||||
| -rw-r--r-- | net/ethernet/eth.c | 6 |
2 files changed, 45 insertions, 3 deletions
diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 0e5e97060034..1cb0f0b90926 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/if_ether.h> | 27 | #include <linux/if_ether.h> |
| 28 | #include <linux/netdevice.h> | 28 | #include <linux/netdevice.h> |
| 29 | #include <linux/random.h> | 29 | #include <linux/random.h> |
| 30 | #include <asm/unaligned.h> | ||
| 30 | 31 | ||
| 31 | #ifdef __KERNEL__ | 32 | #ifdef __KERNEL__ |
| 32 | extern __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev); | 33 | extern __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev); |
| @@ -140,6 +141,47 @@ static inline unsigned compare_ether_addr(const u8 *addr1, const u8 *addr2) | |||
| 140 | BUILD_BUG_ON(ETH_ALEN != 6); | 141 | BUILD_BUG_ON(ETH_ALEN != 6); |
| 141 | return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0; | 142 | return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0; |
| 142 | } | 143 | } |
| 144 | |||
| 145 | static inline unsigned long zap_last_2bytes(unsigned long value) | ||
| 146 | { | ||
| 147 | #ifdef __BIG_ENDIAN | ||
| 148 | return value >> 16; | ||
| 149 | #else | ||
| 150 | return value << 16; | ||
| 151 | #endif | ||
| 152 | } | ||
| 153 | |||
| 154 | /** | ||
| 155 | * compare_ether_addr_64bits - Compare two Ethernet addresses | ||
| 156 | * @addr1: Pointer to an array of 8 bytes | ||
| 157 | * @addr2: Pointer to an other array of 8 bytes | ||
| 158 | * | ||
| 159 | * Compare two ethernet addresses, returns 0 if equal. | ||
| 160 | * Same result than "memcmp(addr1, addr2, ETH_ALEN)" but without conditional | ||
| 161 | * branches, and possibly long word memory accesses on CPU allowing cheap | ||
| 162 | * unaligned memory reads. | ||
| 163 | * arrays = { byte1, byte2, byte3, byte4, byte6, byte7, pad1, pad2} | ||
| 164 | * | ||
| 165 | * Please note that alignment of addr1 & addr2 is only guaranted to be 16 bits. | ||
| 166 | */ | ||
| 167 | |||
| 168 | static inline unsigned compare_ether_addr_64bits(const u8 addr1[6+2], | ||
| 169 | const u8 addr2[6+2]) | ||
| 170 | { | ||
| 171 | #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS | ||
| 172 | unsigned long fold = ((*(unsigned long *)addr1) ^ | ||
| 173 | (*(unsigned long *)addr2)); | ||
| 174 | |||
| 175 | if (sizeof(fold) == 8) | ||
| 176 | return zap_last_2bytes(fold) != 0; | ||
| 177 | |||
| 178 | fold |= zap_last_2bytes((*(unsigned long *)(addr1 + 4)) ^ | ||
| 179 | (*(unsigned long *)(addr2 + 4))); | ||
| 180 | return fold != 0; | ||
| 181 | #else | ||
| 182 | return compare_ether_addr(addr1, addr2); | ||
| 183 | #endif | ||
| 184 | } | ||
| 143 | #endif /* __KERNEL__ */ | 185 | #endif /* __KERNEL__ */ |
| 144 | 186 | ||
| 145 | #endif /* _LINUX_ETHERDEVICE_H */ | 187 | #endif /* _LINUX_ETHERDEVICE_H */ |
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index a87a171d9914..280352aba403 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c | |||
| @@ -165,8 +165,8 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) | |||
| 165 | skb_pull(skb, ETH_HLEN); | 165 | skb_pull(skb, ETH_HLEN); |
| 166 | eth = eth_hdr(skb); | 166 | eth = eth_hdr(skb); |
| 167 | 167 | ||
| 168 | if (is_multicast_ether_addr(eth->h_dest)) { | 168 | if (unlikely(is_multicast_ether_addr(eth->h_dest))) { |
| 169 | if (!compare_ether_addr(eth->h_dest, dev->broadcast)) | 169 | if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast)) |
| 170 | skb->pkt_type = PACKET_BROADCAST; | 170 | skb->pkt_type = PACKET_BROADCAST; |
| 171 | else | 171 | else |
| 172 | skb->pkt_type = PACKET_MULTICAST; | 172 | skb->pkt_type = PACKET_MULTICAST; |
| @@ -181,7 +181,7 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) | |||
| 181 | */ | 181 | */ |
| 182 | 182 | ||
| 183 | else if (1 /*dev->flags&IFF_PROMISC */ ) { | 183 | else if (1 /*dev->flags&IFF_PROMISC */ ) { |
| 184 | if (unlikely(compare_ether_addr(eth->h_dest, dev->dev_addr))) | 184 | if (unlikely(compare_ether_addr_64bits(eth->h_dest, dev->dev_addr))) |
| 185 | skb->pkt_type = PACKET_OTHERHOST; | 185 | skb->pkt_type = PACKET_OTHERHOST; |
| 186 | } | 186 | } |
| 187 | 187 | ||
