bonding support for IPv6 transmit hashing
Subsystems (4)
v4 cover bonding support for IPv6 transmit hashing
Currently the "bonding" driver does not support load balancing outgoing traffic in LACP mode for IPv6 traffic. IPv4 (and TCP or UDP over IPv4) are currently supported; this patch adds transmit hashing for IPv6 (and TCP or UDP over IPv6), bringing IPv6 up to par with IPv4 support in the bonding driver.
The algorithm chosen (xor'ing the bottom three quads and then xor'ing the bottom three bytes of that) was chosen after testing almost 400,000 unique IPv6 addresses harvested from server logs. This algorithm had the most even distribution for both big- and little-endian architectures while still using few instructions.
The IPv6 flow label was intentionally not included in the hash as it appears to be unset in the vast majority of IPv6 traffic sampled, and the current algorithm not using the flow label already offers a very even distribution.
Fragmented IPv6 packets are handled the same way as fragmented IPv4 packets, ie, they are not balanced based on layer 4 information. Additionally, IPv6 packets with intermediate headers are not balanced based on layer 4 information. In practice these intermediate headers are not common and this should not cause any problems, and the alternative (a packet-parsing loop and look-up table) seemed slow and complicated for little gain.
This is an update to a prior patch I submitted. This version includes a clarified description, thorough bounds checking, updates functions to call bond_xmit_hash_policy_l2 rather than re-implement the same logic, incorporates Jay's style suggestions, and patches against net-next. Patch has been tested and performs as expected.
John Eaglesham (2):
Add support for IPv6 and bounds checking to transmit hashing
functions.
Update bonding driver documentation to include IPv6 transmit hashing
algorithm.
Documentation/networking/bonding.txt | 31 ++++++++++-- drivers/net/bonding/bond_main.c | 91 +++++++++++++++++++++++++----------- 2 files changed, 90 insertions(+), 32 deletions(-)
Signature · 1 line
1.7.11
PATCH v4 1/2 Add support for IPv6 and bounds checking to transmit hashing functions.
--- drivers/net/bonding/bond_main.c | 91 +++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 27 deletions(-)
drivers/net/bonding/bond_main.c +64 -27
@@ -3345,56 +3345,93 @@ static struct notifier_block bond_netdev_notifier = {/*---------------------------- Hashing Policies -----------------------------*/ /* +* Hash for the output device based upon layer 2 data +*/ +static int bond_xmit_hash_policy_l2 (struct sk_buff *skb,int count) +{ +struct ethhdr *data = (struct ethhdr *)skb->data; + +if (skb_headlen (skb) >= offsetof(struct ethhdr , h_proto)) +return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; + +return 0 ; +} + +/* * Hash for the output device based upon layer 2 and layer 3 data. If -* the packet is not IP mimic bond_xmit_hash_policy_l2() +* the packet is not IP, fall back on bond_xmit_hash_policy_l2() */ static int bond_xmit_hash_policy_l23 (struct sk_buff *skb,int count) {struct ethhdr *data = (struct ethhdr *)skb->data; -struct iphdr *iph =ip_hdr (skb); +struct iphdr *iph; +struct ipv6hdr *ipv6h; +u32 v6hash; -if (skb->protocol ==htons (ETH_P_IP )) { +if (skb->protocol ==htons (ETH_P_IP ) && +skb_network_header_len (skb) >=sizeof (struct iphdr )) { + iph =ip_hdr (skb);return ((ntohl (iph->saddr ^ iph->daddr) &0xffff ) ^ (data->h_dest[5 ] ^ data->h_source[5 ])) % count; - } - -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; + }else if (skb->protocol ==htons (ETH_P_IPV6 ) && +skb_network_header_len (skb) >=sizeof (struct ipv6hdr )) { + ipv6h =ipv6_hdr (skb); + v6hash = + (ipv6h->saddr.s6_addr32[1 ] ^ ipv6h->daddr.s6_addr32[1 ]) ^ + (ipv6h->saddr.s6_addr32[2 ] ^ ipv6h->daddr.s6_addr32[2 ]) ^ + (ipv6h->saddr.s6_addr32[3 ] ^ ipv6h->daddr.s6_addr32[3 ]); + v6hash = (v6hash >>16 ) ^ (v6hash >>8 ) ^ v6hash; +return (v6hash ^ data->h_dest[5 ] ^ data->h_source[5 ]) % count; + } + +return bond_xmit_hash_policy_l2 (skb, count); }/* * Hash for the output device based upon layer 3 and layer 4 data. If * the packet is a frag or not TCP or UDP, just use layer 3 data. If it is -* altogether not IP, mimic bond_xmit_hash_policy_l2() +* altogether not IP, fall back on bond_xmit_hash_policy_l2() */ static int bond_xmit_hash_policy_l34 (struct sk_buff *skb,int count) { -struct ethhdr *data = (struct ethhdr *)skb->data; -struct iphdr *iph =ip_hdr (skb); -__be16 *layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); -int layer4_xor =0 ; +u32 layer4_xor =0 ; +struct iphdr *iph; +struct ipv6hdr *ipv6h;if (skb->protocol ==htons (ETH_P_IP )) { + iph =ip_hdr (skb);if (!ip_is_fragment (iph) && - (iph->protocol ==IPPROTO_TCP || - iph->protocol ==IPPROTO_UDP )) { + (iph->protocol ==IPPROTO_TCP || + iph->protocol ==IPPROTO_UDP )) { +__be16 *layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); +if (iph->ihl *sizeof (u32) +sizeof (__be16) *2 > +skb_headlen (skb) -skb_network_offset (skb)) + goto short_header; layer4_xor =ntohs ((*layer4hdr ^ *(layer4hdr +1 ))); + }else if (skb_network_header_len (skb) <sizeof (struct iphdr )) { + goto short_header; } -return (layer4_xor ^ - ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count; - +return (layer4_xor ^ ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count; + }else if (skb->protocol ==htons (ETH_P_IPV6 )) { + ipv6h =ipv6_hdr (skb); +if (ipv6h->nexthdr ==IPPROTO_TCP || ipv6h->nexthdr ==IPPROTO_UDP ) { +__be16 *layer4hdrv6 = (__be16 *)((u8 *)ipv6h +sizeof (struct ipv6hdr )); +if (sizeof (struct ipv6hdr ) +sizeof (__be16) *2 > +skb_headlen (skb) -skb_network_offset (skb)) + goto short_header; + layer4_xor = (*layer4hdrv6 ^ *(layer4hdrv6 +1 )); + }else if (skb_network_header_len (skb) <sizeof (struct ipv6hdr )) { + goto short_header; + } + layer4_xor ^= + (ipv6h->saddr.s6_addr32[1 ] ^ ipv6h->daddr.s6_addr32[1 ]) ^ + (ipv6h->saddr.s6_addr32[2 ] ^ ipv6h->daddr.s6_addr32[2 ]) ^ + (ipv6h->saddr.s6_addr32[3 ] ^ ipv6h->daddr.s6_addr32[3 ]); +return ((layer4_xor >>16 ) ^ (layer4_xor >>8 ) ^ layer4_xor) % count; } -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; -} - -/* -* Hash for the output device based upon layer 2 data -*/ -static int bond_xmit_hash_policy_l2 (struct sk_buff *skb,int count) -{ -struct ethhdr *data = (struct ethhdr *)skb->data; - -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; +short_header: +return bond_xmit_hash_policy_l2 (skb, count); }/*-------------------------- Device entry points ----------------------------*/
Signature · 1 line
1.7.11
David Miller
maintainerRe: [PATCH v4 1/2] Add support for IPv6 and bounds checking to transmit hashing functions.
From: John Eaglesham <[email protected]>
Date: Sun, 1 Jul 2012 00:01:38 -0700
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3372
Quoted 3 lines
drivers/net/bonding/bond_main.c +2
-1-if (skb->protocol ==htons (ETH_P_IP )) { +if (skb->protocol ==htons (ETH_P_IP ) && +skb_network_header_len (skb) >=sizeof (struct iphdr )) {
This is not indented properly, the goal isn't to use only TAB characters to indent, the goal it to line things up right after the openning parenthesis on the previous line, so this should be:
if (skb->protocol == htons(ETH_P_IP) &&
skb_network_header_len(skb) >= sizeof(struct iphdr)) {drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3377
Quoted 2 lines
drivers/net/bonding/bond_main.c +2
-0+ }else if (skb->protocol ==htons (ETH_P_IPV6 ) && +skb_network_header_len (skb) >=sizeof (struct ipv6hdr )) {
Likewise, in this case you even under-indented it.
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3382
Quoted 4 lines
drivers/net/bonding/bond_main.c +4
-0+ v6hash = + (ipv6h->saddr.s6_addr32[1 ] ^ ipv6h->daddr.s6_addr32[1 ]) ^ + (ipv6h->saddr.s6_addr32[2 ] ^ ipv6h->daddr.s6_addr32[2 ]) ^ + (ipv6h->saddr.s6_addr32[3 ] ^ ipv6h->daddr.s6_addr32[3 ]);
This is rediculous, just put &ipv6h->saddr into a local pointer named 's' and then you won't have use such gymnastics to indent the code.
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3405
Quoted 5 lines
drivers/net/bonding/bond_main.c +2
-2if (!ip_is_fragment (iph) && - (iph->protocol ==IPPROTO_TCP || - iph->protocol ==IPPROTO_UDP )) { + (iph->protocol ==IPPROTO_TCP || + iph->protocol ==IPPROTO_UDP )) {
This is what _REALLY_ bothers me. You took an existing conditional which _WAS_ indented properly and you made erroneously re-indented.
In fact your only change here is to break the indentation.
Just remove these changes entirely.
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (iph->ihl * sizeof(u32) + sizeof(__be16) * 2 > + skb_headlen(skb) - skb_network_offset(skb))
Similarly, this is indented improperly.
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3413
Quoted 3 lines
drivers/net/bonding/bond_main.c +2
-0+ }else if (skb_network_header_len (skb) <sizeof (struct iphdr )) { + goto short_header; }
Single line basic blocks do not use openning and closing braces.
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3414
Quoted 4 lines
drivers/net/bonding/bond_main.c +1
-3-return (layer4_xor ^ - ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count; - +return (layer4_xor ^ ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count;
Don't reindent code unless you can do it properly, now this line is way over 80 columns. The previous code was perfectly fine, leave it alone.
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3417
Quoted 1 line
drivers/net/bonding/bond_main.c +1
-0+if (ipv6h->nexthdr ==IPPROTO_TCP || ipv6h->nexthdr ==IPPROTO_UDP ) {
Line is too long, write it as:
if (ipv6h->nexthdr == IPPROTO_TCP ||
ipv6h->nexthdr == IPPROTO_UDP) {drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3418
Quoted 1 line
drivers/net/bonding/bond_main.c +1
-0+__be16 *layer4hdrv6 = (__be16 *)((u8 *)ipv6h +sizeof (struct ipv6hdr ));
Likewise, line is too long. Just do the variable declaration seperate from the assignment:
__be16 *layer4hdrv6; layer4hdrv6 = BLAH BLAH BLAH;
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3420
Quoted 2 lines
drivers/net/bonding/bond_main.c +2
-0+if (sizeof (struct ipv6hdr ) +sizeof (__be16) *2 > +skb_headlen (skb) -skb_network_offset (skb))
Improperly indented, fix.
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3425
Quoted 3 lines
drivers/net/bonding/bond_main.c +3
-0+ }else if (skb_network_header_len (skb) <sizeof (struct ipv6hdr )) { + goto short_header; + }
Since line basic block, no braces.
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3429
Quoted 4 lines
drivers/net/bonding/bond_main.c +4
-0+ layer4_xor ^= + (ipv6h->saddr.s6_addr32[1 ] ^ ipv6h->daddr.s6_addr32[1 ]) ^ + (ipv6h->saddr.s6_addr32[2 ] ^ ipv6h->daddr.s6_addr32[2 ]) ^ + (ipv6h->saddr.s6_addr32[3 ] ^ ipv6h->daddr.s6_addr32[3 ]);
This is gross, do as I said above using a local pointer variable.
PATCH v4 2/2 Update bonding driver documentation to include IPv6 transmit hashing algorithm.
In reply to John Eaglesham--- Documentation/networking/bonding.txt | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-)
Documentation/networking/bonding.txt +26 -5
@@ -752,12 +752,22 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The formula is + generate the hash. The IPv4 formula is (((source IP XOR dest IP) AND 0xffff) XOR ( source MAC XOR destination MAC )) modulo slave count + The IPv6 forumla is + + iphash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + ((iphash >> 16) XOR (iphash >> 8) XOR iphash) + modulo slave count + This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, the formula is the same as for the layer2 transmit
@@ -778,19 +788,30 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented TCP and UDP packets is + The formula for unfragmented IPv4 TCP and UDP packets is ((source port XOR dest port) XOR ((source IP XOR dest IP) AND 0xffff) modulo slave count - For fragmented TCP or UDP packets and all other IP - protocol traffic, the source and destination port + The formula for unfragmented IPv6 TCP and UDP packets is + + iphash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + ((source port XOR dest port) XOR + (iphash >> 16) XOR (iphash >> 8) XOR iphash) + modulo slave count + + For fragmented TCP or UDP packets and all other IPv4 and + IPv6 protocol traffic, the source and destination port information is omitted. For non-IP traffic, the formula is the same as for the layer2 transmit hash policy. - This policy is intended to mimic the behavior of + The IPv4 policy is intended to mimic the behavior of certain switches, notably Cisco switches with PFC2 as well as some Foundry and IBM products.
Signature · 1 line
1.7.11
David Miller
maintainerRe: [PATCH v4 2/2] Update bonding driver documentation to include IPv6 transmit hashing algorithm.
I think you should combine this into the first patch, there is no reason to separate these two changes.
Re: [PATCH v4 2/2] Update bonding driver documentation to include IPv6 transmit hashing algorithm.
Quoted 8 lines · David Miller
I think you should combine this into the first patch, there is no reason to separate these two changes.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Thanks, I will do that.
John
PATCH v5 1/1 bonding support for IPv6 transmit hashing
In reply to John EagleshamCurrently the "bonding" driver does not support load balancing outgoing traffic in LACP mode for IPv6 traffic. IPv4 (and TCP or UDP over IPv4) are currently supported; this patch adds transmit hashing for IPv6 (and TCP or UDP over IPv6), bringing IPv6 up to par with IPv4 support in the bonding driver.
The algorithm chosen (xor'ing the bottom three quads and then xor'ing the bottom three bytes of that) was chosen after testing almost 400,000 unique IPv6 addresses harvested from server logs. This algorithm had the most even distribution for both big- and little-endian architectures while still using few instructions.
The IPv6 flow label was intentionally not included in the hash as it appears to be unset in the vast majority of IPv6 traffic sampled, and the current algorithm not using the flow label already offers a very even distribution.
Fragmented IPv6 packets are handled the same way as fragmented IPv4 packets, ie, they are not balanced based on layer 4 information. Additionally, IPv6 packets with intermediate headers are not balanced based on layer 4 information. In practice these intermediate headers are not common and this should not cause any problems, and the alternative (a packet-parsing loop and look-up table) seemed slow and complicated for little gain.
This is an update to a prior patch I submitted. This version includes a clarified description, thorough bounds checking, updates functions to call bond_xmit_hash_policy_l2 rather than re-implement the same logic, incorporates Jay's style suggestions, patches against net-next, and squashes the documentation and code patch into one. Patch has been tested and performs as expected.
John Eaglesham
--- Documentation/networking/bonding.txt | 31 ++++++++++-- drivers/net/bonding/bond_main.c | 91 +++++++++++++++++++++++++----------- 2 files changed, 90 insertions(+), 32 deletions(-)
Documentation/networking/bonding.txt +26 -5
@@ -752,12 +752,22 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The formula is + generate the hash. The IPv4 formula is (((source IP XOR dest IP) AND 0xffff) XOR ( source MAC XOR destination MAC )) modulo slave count + The IPv6 forumla is + + iphash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + ((iphash >> 16) XOR (iphash >> 8) XOR iphash) + modulo slave count + This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, the formula is the same as for the layer2 transmit
@@ -778,19 +788,30 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented TCP and UDP packets is + The formula for unfragmented IPv4 TCP and UDP packets is ((source port XOR dest port) XOR ((source IP XOR dest IP) AND 0xffff) modulo slave count - For fragmented TCP or UDP packets and all other IP - protocol traffic, the source and destination port + The formula for unfragmented IPv6 TCP and UDP packets is + + iphash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + ((source port XOR dest port) XOR + (iphash >> 16) XOR (iphash >> 8) XOR iphash) + modulo slave count + + For fragmented TCP or UDP packets and all other IPv4 and + IPv6 protocol traffic, the source and destination port information is omitted. For non-IP traffic, the formula is the same as for the layer2 transmit hash policy. - This policy is intended to mimic the behavior of + The IPv4 policy is intended to mimic the behavior of certain switches, notably Cisco switches with PFC2 as well as some Foundry and IBM products.
drivers/net/bonding/bond_main.c +64 -27
120 lines not on this page · raw message
Signature · 1 line
1.7.11
David Miller
maintainerFrom: John Eaglesham <[email protected]>
Date: Sun, 1 Jul 2012 01:07:43 -0700
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3382
Quoted 4 lines
drivers/net/bonding/bond_main.c +4
-0+ v6hash = + (ipv6h->saddr.s6_addr32[1 ] ^ ipv6h->daddr.s6_addr32[1 ]) ^ + (ipv6h->saddr.s6_addr32[2 ] ^ ipv6h->daddr.s6_addr32[2 ]) ^ + (ipv6h->saddr.s6_addr32[3 ] ^ ipv6h->daddr.s6_addr32[3 ]);
You completely ignored my feedback:
This is rediculous, just put &ipv6h->saddr into a local pointer named 's' and then you won't have use such gymnastics to indent the code.
Quoted 10 lines · David Miller
From: John Eaglesham <[email protected]>
Date: Sun, 1 Jul 2012 01:07:43 -0700drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3382
Quoted 4 lines
drivers/net/bonding/bond_main.c +4
-0+ v6hash = + (ipv6h->saddr.s6_addr32[1 ] ^ ipv6h->daddr.s6_addr32[1 ]) ^ + (ipv6h->saddr.s6_addr32[2 ] ^ ipv6h->daddr.s6_addr32[2 ]) ^ + (ipv6h->saddr.s6_addr32[3 ] ^ ipv6h->daddr.s6_addr32[3 ]);You completely ignored my feedback:
Quoted 8 lines
This is rediculous, just put
&ipv6h->saddrinto a local pointer named 's' and then you won't have use such gymnastics to indent the code.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
My apologies, I completely misunderstood your feedback. I will make the correction and re-submit.
Thanks.
John
PATCH v6 1/1 bonding support for IPv6 transmit hashing
In reply to John EagleshamCurrently the "bonding" driver does not support load balancing outgoing traffic in LACP mode for IPv6 traffic. IPv4 (and TCP or UDP over IPv4) are currently supported; this patch adds transmit hashing for IPv6 (and TCP or UDP over IPv6), bringing IPv6 up to par with IPv4 support in the bonding driver.
The algorithm chosen (xor'ing the bottom three quads of the source and destination addresses together, then xor'ing each byte of that result into the bottom byte, finally xor'ing with the last bytes of the MAC addresses) was selected after testing almost 400,000 unique IPv6 addresses harvested from server logs. This algorithm had the most even distribution for both big- and little-endian architectures while still using few instructions. Its behavior also attempts to closely match that of the IPv4 algorithm.
The IPv6 flow label was intentionally not included in the hash as it appears to be unset in the vast majority of IPv6 traffic sampled, and the current algorithm not using the flow label already offers a very even distribution.
Fragmented IPv6 packets are handled the same way as fragmented IPv4 packets, ie, they are not balanced based on layer 4 information. Additionally, IPv6 packets with intermediate headers are not balanced based on layer 4 information. In practice these intermediate headers are not common and this should not cause any problems, and the alternative (a packet-parsing loop and look-up table) seemed slow and complicated for little gain.
This is an update to prior patches I submitted. This version includes:
* Updated and clarified description
* IPv6 algorithm more closely matches that of IPv4
* Thorough bounds checking on all xmit functions
* Consolidate layer 2 hashing logic into one function
* Update style as per Jay Vosburgh and David Miller
* Patches against net-next as one patch
Patch has been tested and performs as expected.
John Eaglesham
--- Documentation/networking/bonding.txt | 32 +++++++++++-- drivers/net/bonding/bond_main.c | 91 +++++++++++++++++++++++++----------- 2 files changed, 92 insertions(+), 31 deletions(-)
Documentation/networking/bonding.txt +27 -5
@@ -752,12 +752,23 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The formula is + generate the hash. The IPv4 formula is (((source IP XOR dest IP) AND 0xffff) XOR ( source MAC XOR destination MAC )) modulo slave count + The IPv6 formula is + + hash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + (source MAC XOR destination MAC)) + modulo slave count + This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, the formula is the same as for the layer2 transmit
@@ -778,19 +789,30 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented TCP and UDP packets is + The formula for unfragmented IPv4 TCP and UDP packets is ((source port XOR dest port) XOR ((source IP XOR dest IP) AND 0xffff) modulo slave count - For fragmented TCP or UDP packets and all other IP - protocol traffic, the source and destination port + The formula for unfragmented IPv6 TCP and UDP packets is + + hash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + ((source port XOR dest port) XOR + (hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + modulo slave count + + For fragmented TCP or UDP packets and all other IPv4 and + IPv6 protocol traffic, the source and destination port information is omitted. For non-IP traffic, the formula is the same as for the layer2 transmit hash policy. - This policy is intended to mimic the behavior of + The IPv4 policy is intended to mimic the behavior of certain switches, notably Cisco switches with PFC2 as well as some Foundry and IBM products.
drivers/net/bonding/bond_main.c +65 -26
121 lines not on this page · raw message
Signature · 1 line
1.7.11
Quoted 57 lines · John Eaglesham
Currently the "bonding" driver does not support load balancing outgoing traffic in LACP mode for IPv6 traffic. IPv4 (and TCP or UDP over IPv4) are currently supported; this patch adds transmit hashing for IPv6 (and TCP or UDP over IPv6), bringing IPv6 up to par with IPv4 support in the bonding driver.
The algorithm chosen (xor'ing the bottom three quads of the source and destination addresses together, then xor'ing each byte of that result into the bottom byte, finally xor'ing with the last bytes of the MAC addresses) was selected after testing almost 400,000 unique IPv6 addresses harvested from server logs. This algorithm had the most even distribution for both big- and little-endian architectures while still using few instructions. Its behavior also attempts to closely match that of the IPv4 algorithm.
The IPv6 flow label was intentionally not included in the hash as it appears to be unset in the vast majority of IPv6 traffic sampled, and the current algorithm not using the flow label already offers a very even distribution.
Fragmented IPv6 packets are handled the same way as fragmented IPv4 packets, ie, they are not balanced based on layer 4 information. Additionally, IPv6 packets with intermediate headers are not balanced based on layer 4 information. In practice these intermediate headers are not common and this should not cause any problems, and the alternative (a packet-parsing loop and look-up table) seemed slow and complicated for little gain.
This is an update to prior patches I submitted. This version includes:
* Updated and clarified description
* IPv6 algorithm more closely matches that of IPv4
* Thorough bounds checking on all xmit functions
* Consolidate layer 2 hashing logic into one function
* Update style as per Jay Vosburgh and David Miller
* Patches against net-next as one patchPatch has been tested and performs as expected.
John Eaglesham
---
Documentation/networking/bonding.txt| 32 +++++++++++--drivers/net/bonding/bond_main.c| 91 +++++++++++++++++++++++++-----------2 files changed, 92 insertions(+), 31 deletions(-)Documentation/networking/bonding.txt +1
-1@@ -752,12 +752,23 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The formula is + generate the hash. The IPv4 formula is (((source IP XOR dest IP) AND 0xffff) XOR ( source MAC XOR destination MAC )) modulo slave count
Documentation/networking/bonding.txt xmit_hash_policy · line 770
Quoted 10 lines
Documentation/networking/bonding.txt +10
-0+ The IPv6 formula is + + hash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + (source MAC XOR destination MAC)) + modulo slave count
This seems to be missing an XOR, between the end of "XOR hash)"
and the start of "(source MAC".
Quoted 109 lines
Documentation/networking/bonding.txt +16
-4+ This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, the formula is the same as for the layer2 transmit@@ -778,19 +789,30 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented TCP and UDP packets is + The formula for unfragmented IPv4 TCP and UDP packets is ((source port XOR dest port) XOR ((source IP XOR dest IP) AND 0xffff) modulo slave count - For fragmented TCP or UDP packets and all other IP - protocol traffic, the source and destination port + The formula for unfragmented IPv6 TCP and UDP packets is + + hash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + ((source port XOR dest port) XOR + (hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + modulo slave count + + For fragmented TCP or UDP packets and all other IPv4 and + IPv6 protocol traffic, the source and destination port information is omitted. For non-IP traffic, the formula is the same as for the layer2 transmit hash policy. - This policy is intended to mimic the behavior of + The IPv4 policy is intended to mimic the behavior of certain switches, notably Cisco switches with PFC2 as well as some Foundry and IBM products.drivers/net/bonding/bond_main.c +35
-10@@ -3345,56 +3345,95 @@ static struct notifier_block bond_netdev_notifier = {/*---------------------------- Hashing Policies -----------------------------*/ /* +* Hash for the output device based upon layer 2 data +*/ +static int bond_xmit_hash_policy_l2 (struct sk_buff *skb,int count) +{ +struct ethhdr *data = (struct ethhdr *)skb->data; + +if (skb_headlen (skb) >= offsetof(struct ethhdr , h_proto)) +return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; + +return 0 ; +} + +/* * Hash for the output device based upon layer 2 and layer 3 data. If -* the packet is not IP mimic bond_xmit_hash_policy_l2() +* the packet is not IP, fall back on bond_xmit_hash_policy_l2() */ static int bond_xmit_hash_policy_l23 (struct sk_buff *skb,int count) {struct ethhdr *data = (struct ethhdr *)skb->data; -struct iphdr *iph =ip_hdr (skb); - -if (skb->protocol ==htons (ETH_P_IP )) { +struct iphdr *iph; +struct ipv6hdr *ipv6h; +u32 v6hash; +__be32 *s, *d; + +if (skb->protocol ==htons (ETH_P_IP ) && +skb_network_header_len (skb) >=sizeof (struct iphdr )) { + iph =ip_hdr (skb);return ((ntohl (iph->saddr ^ iph->daddr) &0xffff ) ^ (data->h_dest[5 ] ^ data->h_source[5 ])) % count; + }else if (skb->protocol ==htons (ETH_P_IPV6 ) && +skb_network_header_len (skb) >=sizeof (struct ipv6hdr )) { + ipv6h =ipv6_hdr (skb); + s = &ipv6h->saddr.s6_addr32[0 ]; + d = &ipv6h->daddr.s6_addr32[0 ]; + v6hash = (s[1 ] ^ d[1 ]) ^ (s[2 ] ^ d[2 ]) ^ (s[3 ] ^ d[3 ]); + v6hash ^= (v6hash >>24 ) ^ (v6hash >>16 ) ^ (v6hash >>8 ); +return (v6hash ^ data->h_dest[5 ] ^ data->h_source[5 ]) % count; } -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; +return bond_xmit_hash_policy_l2 (skb, count); }/* * Hash for the output device based upon layer 3 and layer 4 data. If * the packet is a frag or not TCP or UDP, just use layer 3 data. If it is -* altogether not IP, mimic bond_xmit_hash_policy_l2() +* altogether not IP, fall back on bond_xmit_hash_policy_l2() */ static int bond_xmit_hash_policy_l34 (struct sk_buff *skb,int count) { -struct ethhdr *data = (struct ethhdr *)skb->data; -struct iphdr *iph =ip_hdr (skb); -__be16 *layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); -int layer4_xor =0 ; +u32 layer4_xor =0 ; +struct iphdr *iph; +struct ipv6hdr *ipv6h;
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3407
Quoted 10 lines
drivers/net/bonding/bond_main.c +5
-2+__be32 *s, *d; +__be16 *layer4hdr;if (skb->protocol ==htons (ETH_P_IP )) { + iph =ip_hdr (skb);if (!ip_is_fragment (iph) && - (iph->protocol ==IPPROTO_TCP || - iph->protocol ==IPPROTO_UDP )) { + (iph->protocol ==IPPROTO_TCP || + iph->protocol ==IPPROTO_UDP )) {
Why did these two lines change?
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3416
Quoted 12 lines
drivers/net/bonding/bond_main.c +7
-3+ layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); +if (iph->ihl *sizeof (u32) +sizeof (__be16) *2 > +skb_headlen (skb) -skb_network_offset (skb)) + goto short_header; layer4_xor =ntohs ((*layer4hdr ^ *(layer4hdr +1 ))); + }else if (skb_network_header_len (skb) <sizeof (struct iphdr )) { + goto short_header; } -return (layer4_xor ^ - ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count; - +return (layer4_xor ^ ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count;
This line runs past 80 columns. There are a few more of these
further down.
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3420
Quoted 4 lines
drivers/net/bonding/bond_main.c +4
-0+ }else if (skb->protocol ==htons (ETH_P_IPV6 )) { + ipv6h =ipv6_hdr (skb); +if (ipv6h->nexthdr ==IPPROTO_TCP || ipv6h->nexthdr ==IPPROTO_UDP ) { + layer4hdr = (__be16 *)((u8 *)ipv6h +sizeof (struct ipv6hdr ));
Could this be written as layer4hdr = (__be16 *)(ipv6h + 1); instead? -J
Quoted 32 lines
drivers/net/bonding/bond_main.c +14
-11+if (sizeof (struct ipv6hdr ) +sizeof (__be16) *2 > +skb_headlen (skb) -skb_network_offset (skb)) + goto short_header; + layer4_xor = (*layer4hdr ^ *(layer4hdr +1 )); + }else if (skb_network_header_len (skb) <sizeof (struct ipv6hdr )) { + goto short_header; + } + s = &ipv6h->saddr.s6_addr32[0 ]; + d = &ipv6h->daddr.s6_addr32[0 ]; + layer4_xor ^= (s[1 ] ^ d[1 ]) ^ (s[2 ] ^ d[2 ]) ^ (s[3 ] ^ d[3 ]); + layer4_xor ^= (layer4_xor >>24 ) ^ (layer4_xor >>16 ) ^ (layer4_xor >>8 ); +return layer4_xor % count; } -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; -} - -/* -* Hash for the output device based upon layer 2 data -*/ -static int bond_xmit_hash_policy_l2 (struct sk_buff *skb,int count) -{ -struct ethhdr *data = (struct ethhdr *)skb->data; - -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; +short_header: +return bond_xmit_hash_policy_l2 (skb, count); }/*-------------------------- Device entry points ----------------------------*/ Signature · 1 line
1.7.11
---
-Jay Vosburgh, IBM Linux Technology Center, [email protected]
Quoted 8 lines · Jay Vosburgh
Documentation/networking/bonding.txt xmit_hash_policy · line 770
Quoted 4 lines
Documentation/networking/bonding.txt +4
-0+ + (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + (source MAC XOR destination MAC)) + modulo slave countThis seems to be missing an XOR, between the end of "XOR hash)"
and the start of "(source MAC".
You're correct.
Quoted 10 lines
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3407
Quoted 7 lines
if (skb->protocol == htons(ETH_P_IP)) {(quoted diff) +1
-0+ iph = ip_hdr(skb);if (!ip_is_fragment(iph) &&drivers/net/bonding/bond_main.c +2
-2- (iph->protocol == IPPROTO_TCP || - iph->protocol == IPPROTO_UDP)) { + (iph->protocol == IPPROTO_TCP || + iph->protocol == IPPROTO_UDP)) {Why did these two lines change?
I replaced the mixed tabs and spaces with all tabs when I updated that function, but in retrospect the tabs and spaces were likely intentional. I will revert.
Quoted 7 lines
Quoted 7 lines
drivers/net/bonding/bond_main.c +4
-0+ layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); +if (iph->ihl *sizeof (u32) +sizeof (__be16) *2 > +skb_headlen (skb) -skb_network_offset (skb)) + goto short_header;layer4_xor = ntohs((*layer4hdr ^ *(layer4hdr + 1)));(quoted diff) +2
-0+ } else if (skb_network_header_len(skb) < sizeof(struct iphdr)) { + goto short_header;
Quoted 9 lines
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3416
Quoted 5 lines
}drivers/net/bonding/bond_main.c +1
-3-return (layer4_xor ^ - ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count; - +return (layer4_xor ^ ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count;This line runs past 80 columns. There are a few more of these
further down.
I will double-check this.
Quoted 5 lines
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3420
Quoted 4 lines
drivers/net/bonding/bond_main.c +4
-0+ }else if (skb->protocol ==htons (ETH_P_IPV6 )) { + ipv6h =ipv6_hdr (skb); +if (ipv6h->nexthdr ==IPPROTO_TCP || ipv6h->nexthdr ==IPPROTO_UDP ) { + layer4hdr = (__be16 *)((u8 *)ipv6h +sizeof (struct ipv6hdr ));
Quoted 8 lines
Could this be written as layer4hdr = (__be16 *)(ipv6h + 1); instead? -J
Yes, I can make that change. Thanks.
John
David Miller
maintainerFrom: John Eaglesham <[email protected]>
Date: Mon, 02 Jul 2012 22:01:20 -0700
Quoted 3 lines
I replaced the mixed tabs and spaces with all tabs when I updated that function, but in retrospect the tabs and spaces were likely intentional. I will revert.
They were. Don't change existing coding style unless you are certain it is wrong.
Besides any such changes are completely outside of the scope of the changes you are making, so you should have left them out in any event.
Quoted 10 lines · David Miller
From: John Eaglesham <[email protected]>
Date: Mon, 02 Jul 2012 22:01:20 -0700Quoted 3 lines
I replaced the mixed tabs and spaces with all tabs when I updated that function, but in retrospect the tabs and spaces were likely intentional. I will revert.
They were. Don't change existing coding style unless you are certain it is wrong.
Quoted 8 lines
Besides any such changes are completely outside of the scope of the changes you are making, so you should have left them out in any event.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Should the indents in the code I am adding follow the style guide and use only tabs, or follow the style already present and mix spaces and tabs for indentation?
Thanks,
John
David Miller
maintainerFrom: John Eaglesham <[email protected]>
Date: Mon, 02 Jul 2012 22:38:05 -0700
Quoted 20 lines · David Miller
Quoted 10 lines
From: John Eaglesham <[email protected]>
Date: Mon, 02 Jul 2012 22:01:20 -0700Quoted 3 lines
I replaced the mixed tabs and spaces with all tabs when I updated that function, but in retrospect the tabs and spaces were likely intentional. I will revert.
They were. Don't change existing coding style unless you are certain it is wrong.
Quoted 8 lines
Besides any such changes are completely outside of the scope of the changes you are making, so you should have left them out in any event.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 3 lines
Should the indents in the code I am adding follow the style guide and use only tabs, or follow the style already present and mix spaces and tabs for indentation?
You should use a mix of tabs, as necessary, to get things to line up how I told you they need to line up.
Quoted 2 lines
You should use a mix of tabs, as necessary, to get things to line up how I told you they need to line up.
Unless I'm missing something, this change doesn't seem to have made it through to the kernel tip, but we could really use this bugfix. Is it in a repository I didn't notice, or not yet through the review? If it's not through the review, is any help needed to get it there?
Regards,
Jeremy Brookman
Quoted 7 lines · Jeremy Brookman
Quoted 2 lines
You should use a mix of tabs, as necessary, to get things to line up how I told you they need to line up.
Unless I'm missing something, this change doesn't seem to have made it through to the kernel tip, but we could really use this bugfix. Is it in a repository I didn't notice, or not yet through the review? If it's not through the review, is any help needed to get it there?
The submitter (John Eaglesham) never posted an updated version
that addressed the various comments, nor did his original patch submission include a Signed-off-by.
I went ahead and updated the patch to address the comments; I've
only compile tested this. Are you (Jeremy or John) able to test this to confirm that it will hash ipv6 traffic as expected (I can test it, but it won't be today)?
John, can you post a Signed-off-by for your patch (really, this
updated version of your patch)?
If John signs off and somebody tests this, I'll post a formal
submssion with the full commit message.
-J
Documentation/networking/bonding.txt +27 -5
@@ -752,12 +752,23 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The formula is + generate the hash. The IPv4 formula is (((source IP XOR dest IP) AND 0xffff) XOR ( source MAC XOR destination MAC )) modulo slave count + The IPv6 formula is + + hash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + XOR (source MAC XOR destination MAC)) + modulo slave count + This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, the formula is the same as for the layer2 transmit
@@ -778,19 +789,30 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented TCP and UDP packets is + The formula for unfragmented IPv4 TCP and UDP packets is ((source port XOR dest port) XOR ((source IP XOR dest IP) AND 0xffff) modulo slave count - For fragmented TCP or UDP packets and all other IP - protocol traffic, the source and destination port + The formula for unfragmented IPv6 TCP and UDP packets is + + hash = + (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + ((source port XOR dest port) XOR + (hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + modulo slave count + + For fragmented TCP or UDP packets and all other IPv4 and + IPv6 protocol traffic, the source and destination port information is omitted. For non-IP traffic, the formula is the same as for the layer2 transmit hash policy. - This policy is intended to mimic the behavior of + The IPv4 policy is intended to mimic the behavior of certain switches, notably Cisco switches with PFC2 as well as some Foundry and IBM products.
drivers/net/bonding/bond_main.c +65 -22
121 lines not on this page · raw message
---
-Jay Vosburgh, IBM Linux Technology Center, [email protected]
Quoted 19 lines · Jay Vosburgh
Quoted 7 lines · Jeremy Brookman
Quoted 2 lines
You should use a mix of tabs, as necessary, to get things to line up how I told you they need to line up.
Unless I'm missing something, this change doesn't seem to have made it through to the kernel tip, but we could really use this bugfix. Is it in a repository I didn't notice, or not yet through the review? If it's not through the review, is any help needed to get it there?
The submitter (John Eaglesham) never posted an updated version
that addressed the various comments, nor did his original patch submission include a Signed-off-by.I went ahead and updated the patch to address the comments; I've
only compile tested this. Are you (Jeremy or John) able to test this to confirm that it will hash ipv6 traffic as expected (I can test it, but it won't be today)?
Quoted 8 lines
John, can you post a Signed-off-by for your patch (really, this
updated version of your patch)?If John signs off and somebody tests this, I'll post a formal
submssion with the full commit message.-J
Since my last submission I ended up making some changes on my end to streamline the logic. I can fold together my patch with yours and test them later tonight. If everything looks good I'll post the changes back to the list.
Thanks!
John
Quoted 7 lines
Quoted 2 lines
If John signs off and somebody tests this, I'll post a formal
submssion with the full commit message.Since my last submission I ended up making some changes on my end to streamline the logic. I can fold together my patch with yours and test them later tonight. If everything looks good I'll post the changes back to the list.
Great - thanks for that Jay/John; will look forward to the latest patch later. I'm actually looking at the 2.6.32 branch so have tested a backport of Jay's patch (which only took a couple of very minor modifications); a quick test on an 8-port bond with layer3+4 hashing looked fine. (Will hold off until the final patch before doing more testing.)
Regards,
Jeremy
Quoted 26 lines · John Eaglesham
Quoted 25 lines · Jay Vosburgh
Quoted 8 lines · Jeremy Brookman
Quoted 2 lines
You should use a mix of tabs, as necessary, to get things to line up how I told you they need to line up.
Unless I'm missing something, this change doesn't seem to have made it through to the kernel tip, but we could really use this bugfix. Is it in a repository I didn't notice, or not yet through the review? If it's not through the review, is any help needed to get it there?
The submitter (John Eaglesham) never posted an updated version
that addressed the various comments, nor did his original patch submission include a Signed-off-by.I went ahead and updated the patch to address the comments; I've
only compile tested this. Are you (Jeremy or John) able to test this to confirm that it will hash ipv6 traffic as expected (I can test it, but it won't be today)?John, can you post a Signed-off-by for your patch (really, this
updated version of your patch)?
Hi,
A few questions on the actual patch inline now I've had a bit more time...
Quoted 1 line
static int
bond_xmit_hash_policy_l23(struct sk_buff *skb, int count)
...
Quoted 5 lines
drivers/net/bonding/bond_main.c +3
-0+if (skb->protocol ==htons (ETH_P_IP ) && +skb_network_header_len (skb) >=sizeof (struct iphdr )) { + iph =ip_hdr (skb); return ((ntohl(iph->saddr ^ iph->daddr) & 0xffff) ^
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3394
Quoted 10 lines
drivers/net/bonding/bond_main.c +8
-0(data->h_dest[5] ^ data->h_source[5])) % count; + }else if (skb->protocol ==htons (ETH_P_IPV6 ) && +skb_network_header_len (skb) >=sizeof (struct ipv6hdr )) { + ipv6h =ipv6_hdr (skb); + s = &ipv6h->saddr.s6_addr32[0 ]; + d = &ipv6h->daddr.s6_addr32[0 ]; + v6hash = (s[1 ] ^ d[1 ]) ^ (s[2 ] ^ d[2 ]) ^ (s[3 ] ^ d[3 ]); + v6hash ^= (v6hash >>24 ) ^ (v6hash >>16 ) ^ (v6hash >>8 ); +return (v6hash ^ data->h_dest[5 ] ^ data->h_source[5 ]) % count; }
If the IPv4 case needs an ntohl, does the IPv6 case (if we're interpreting the address as 4 32-bits)? If IPv4 hashing algorithm is consistent across different endiannesses, then maybe IPv6 should be too?
Quoted 6 lines
/*
* Hash for the output device based upon layer 3 and layer 4 data. If
* the packet is a frag or not TCP or UDP, just use layer 3 data. If it is
- * altogether not IP, mimicbond_xmit_hash_policy_l2()
+ * altogether not IP, fall back onbond_xmit_hash_policy_l2()*/
Looking at the code below, we only check the first value of next_hdr in the chain; however RFC 2460 lists the following possible extension headers, all of which will therefore cause fallback to L3 hashing:
Hop-by-Hop Options
Routing (Type 0)
Fragment
Destination Options
Authentication
Encapsulating Security Payload
Clearly with some (eg ESP and fragment) we do need to drop out of using L4 header info in the hash. And anyone using Routing (Type 0) probably deserves anything they get. But should we at least comment on the limitation that the existence of the other headers also causes fallback to L3 hashing only? (And possibly even include in documentation?) Or of course, fix?
drivers/net/bonding/bond_main.c static struct notifier_block bond_netdev_notifier = { · line 3424
Quoted 13 lines
if (skb->protocol == htons(ETH_P_IP)) {drivers/net/bonding/bond_main.c +7
-0+ iph =ip_hdr (skb);if (!ip_is_fragment (iph) && (iph->protocol ==IPPROTO_TCP || iph->protocol ==IPPROTO_UDP )) { +if (iph->ihl *sizeof (u32) +sizeof (__be16) *2 > +skb_headlen (skb) -skb_network_offset (skb)) + goto short_header; + layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); layer4_xor =ntohs ((*layer4hdr ^ *(layer4hdr +1 ))); + }else if (skb_network_header_len (skb) <sizeof (struct iphdr )) { + goto short_header; }
I don't know the assertions we can make about skb_network_header_len(skb), but it looks odd doing a length check against sizeof(iphdr) after iph->protocol has already been dereferenced. Is this really right? (The pattern recurs a few times.)
Regards,
Jeremy
PATCH v7 1/1 bonding: support for IPv6 transmit hashing
In reply to John EagleshamFrom: John Eaglesham <[email protected]>
Currently the "bonding" driver does not support load balancing outgoing traffic in LACP mode for IPv6 traffic. IPv4 (and TCP or UDP over IPv4) are currently supported; this patch adds transmit hashing for IPv6 (and TCP or UDP over IPv6), bringing IPv6 up to par with IPv4 support in the bonding driver. In addition, bounds checking has been added to all transmit hashing functions.
The algorithm chosen (xor'ing the bottom three quads of the source and destination addresses together, then xor'ing each byte of that result into the bottom byte, finally xor'ing with the last bytes of the MAC addresses) was selected after testing almost 400,000 unique IPv6 addresses harvested from server logs. This algorithm had the most even distribution for both big- and little-endian architectures while still using few instructions. Its behavior also attempts to closely match that of the IPv4 algorithm.
The IPv6 flow label was intentionally not included in the hash as it appears to be unset in the vast majority of IPv6 traffic sampled, and the current algorithm not using the flow label already offers a very even distribution.
Fragmented IPv6 packets are handled the same way as fragmented IPv4 packets, ie, they are not balanced based on layer 4 information. Additionally, IPv6 packets with intermediate headers are not balanced based on layer 4 information. In practice these intermediate headers are not common and this should not cause any problems, and the alternative (a packet-parsing loop and look-up table) seemed slow and complicated for little gain.
Changes:
v2)
* Clarify description
* Add bounds checking to more functions
* All functions call bond_xmit_hash_policy_l2 rather than re- implement the same logic.
v3)
* Patch against net-next.
* Style corrections.
v4)
* Correct indenting.
v5)
* Squash documentation and code patches into one.
v6)
* Modify IPv6 hash to behave more like the IPv4 hash, update documentation with modified algorithm.
* Clean up formatting.
* Move all variable declaration to the top of the function.
* Minor change to IPv6 layer 4 hash to match IPv4 hash behavior (mix all hashed address bits together rather than just the bottom 24 bits).
v7)
* Improve bounds checking code (handle truncated IPv6 header, removed goto, fewer if statements).
* Re-write pseudocode in documentation to match actual code more closely.
* Correct indenting, align parentheses, wrap code at <= 80 columns (based on Jay's changes).
Patch has been tested and performs as expected.
--- Documentation/networking/bonding.txt | 30 ++++++++++-- drivers/net/bonding/bond_main.c | 89 +++++++++++++++++++++++++----------- 2 files changed, 88 insertions(+), 31 deletions(-)
Documentation/networking/bonding.txt +25 -5
@@ -752,12 +752,22 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The formula is + generate the hash. The IPv4 formula is (((source IP XOR dest IP) AND 0xffff) XOR ( source MAC XOR destination MAC )) modulo slave count + The IPv6 formula is + + hash = (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + XOR (source MAC XOR destination MAC)) + modulo slave count + This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, the formula is the same as for the layer2 transmit
@@ -778,19 +788,29 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented TCP and UDP packets is + The formula for unfragmented IPv4 TCP and UDP packets is ((source port XOR dest port) XOR ((source IP XOR dest IP) AND 0xffff) modulo slave count - For fragmented TCP or UDP packets and all other IP - protocol traffic, the source and destination port + The formula for unfragmented IPv6 TCP and UDP packets is + + hash = (source port XOR dest port) XOR + ((source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4)) + + ((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + modulo slave count + + For fragmented TCP or UDP packets and all other IPv4 and + IPv6 protocol traffic, the source and destination port information is omitted. For non-IP traffic, the formula is the same as for the layer2 transmit hash policy. - This policy is intended to mimic the behavior of + The IPv4 policy is intended to mimic the behavior of certain switches, notably Cisco switches with PFC2 as well as some Foundry and IBM products.
drivers/net/bonding/bond_main.c +63 -26
119 lines not on this page · raw message
Signature · 1 line
1.7.11
David Miller
maintainerRe: [PATCH v7] bonding: support for IPv6 transmit hashing
From: John Eaglesham <[email protected]>
Date: Tue, 21 Aug 2012 22:12:33 -0700
Quoted 1 line
Signed-off-by John Eaglesham
This is not a proper signoff, you must put an email address on this line.
Also, the changelog between the versions doesn't belong in the commit log message itself. It gets placed in commentary after the "---" line.
PATCH v8 1/1 bonding: support for IPv6 transmit hashing
In reply to John EagleshamFrom: John Eaglesham <[email protected]>
Currently the "bonding" driver does not support load balancing outgoing traffic in LACP mode for IPv6 traffic. IPv4 (and TCP or UDP over IPv4) are currently supported; this patch adds transmit hashing for IPv6 (and TCP or UDP over IPv6), bringing IPv6 up to par with IPv4 support in the bonding driver. In addition, bounds checking has been added to all transmit hashing functions.
The algorithm chosen (xor'ing the bottom three quads of the source and destination addresses together, then xor'ing each byte of that result into the bottom byte, finally xor'ing with the last bytes of the MAC addresses) was selected after testing almost 400,000 unique IPv6 addresses harvested from server logs. This algorithm had the most even distribution for both big- and little-endian architectures while still using few instructions. Its behavior also attempts to closely match that of the IPv4 algorithm.
The IPv6 flow label was intentionally not included in the hash as it appears to be unset in the vast majority of IPv6 traffic sampled, and the current algorithm not using the flow label already offers a very even distribution.
Fragmented IPv6 packets are handled the same way as fragmented IPv4 packets, ie, they are not balanced based on layer 4 information. Additionally, IPv6 packets with intermediate headers are not balanced based on layer 4 information. In practice these intermediate headers are not common and this should not cause any problems, and the alternative (a packet-parsing loop and look-up table) seemed slow and complicated for little gain.
---
Changes:
v2)
* Clarify description
* Add bounds checking to more functions
* All functions call bond_xmit_hash_policy_l2 rather than re- implement the same logic.
v3)
* Patch against net-next.
* Style corrections.
v4)
* Correct indenting.
v5)
* Squash documentation and code patches into one.
v6)
* Modify IPv6 hash to behave more like the IPv4 hash, update documentation with modified algorithm.
* Clean up formatting.
* Move all variable declaration to the top of the function.
* Minor change to IPv6 layer 4 hash to match IPv4 hash behavior (mix all hashed address bits together rather than just the bottom 24 bits).
v7)
* Improve bounds checking code (handle truncated IPv6 header, removed goto, fewer if statements).
* Re-write pseudocode in documentation to match actual code more closely.
* Correct indenting, align parentheses, wrap code at <= 80 columns (based on Jay's changes).
v8)
* Correct patch submission format.
Documentation/networking/bonding.txt | 30 ++++++++++-- drivers/net/bonding/bond_main.c | 89 +++++++++++++++++++++++++----------- 2 files changed, 88 insertions(+), 31 deletions(-)
Documentation/networking/bonding.txt +25 -5
@@ -752,12 +752,22 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The formula is + generate the hash. The IPv4 formula is (((source IP XOR dest IP) AND 0xffff) XOR ( source MAC XOR destination MAC )) modulo slave count + The IPv6 formula is + + hash = (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + XOR (source MAC XOR destination MAC)) + modulo slave count + This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, the formula is the same as for the layer2 transmit
@@ -778,19 +788,29 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented TCP and UDP packets is + The formula for unfragmented IPv4 TCP and UDP packets is ((source port XOR dest port) XOR ((source IP XOR dest IP) AND 0xffff) modulo slave count - For fragmented TCP or UDP packets and all other IP - protocol traffic, the source and destination port + The formula for unfragmented IPv6 TCP and UDP packets is + + hash = (source port XOR dest port) XOR + ((source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4)) + + ((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + modulo slave count + + For fragmented TCP or UDP packets and all other IPv4 and + IPv6 protocol traffic, the source and destination port information is omitted. For non-IP traffic, the formula is the same as for the layer2 transmit hash policy. - This policy is intended to mimic the behavior of + The IPv4 policy is intended to mimic the behavior of certain switches, notably Cisco switches with PFC2 as well as some Foundry and IBM products.
drivers/net/bonding/bond_main.c +63 -26
119 lines not on this page · raw message
Signature · 1 line
1.7.11
Re: [PATCH v8] bonding: support for IPv6 transmit hashing
From: John Eaglesham <[email protected]>
Date: Tue, 21 Aug 2012 23:43:35 -0700
Quoted 28 lines
From: John Eaglesham <[email protected]>
Currently the "bonding" driver does not support load balancing outgoing traffic in LACP mode for IPv6 traffic. IPv4 (and TCP or UDP over IPv4) are currently supported; this patch adds transmit hashing for IPv6 (and TCP or UDP over IPv6), bringing IPv6 up to par with IPv4 support in the bonding driver. In addition, bounds checking has been added to all transmit hashing functions.
The algorithm chosen (xor'ing the bottom three quads of the source and destination addresses together, then xor'ing each byte of that result into the bottom byte, finally xor'ing with the last bytes of the MAC addresses) was selected after testing almost 400,000 unique IPv6 addresses harvested from server logs. This algorithm had the most even distribution for both big- and little-endian architectures while still using few instructions. Its behavior also attempts to closely match that of the IPv4 algorithm.
The IPv6 flow label was intentionally not included in the hash as it appears to be unset in the vast majority of IPv6 traffic sampled, and the current algorithm not using the flow label already offers a very even distribution.
Fragmented IPv6 packets are handled the same way as fragmented IPv4 packets, ie, they are not balanced based on layer 4 information. Additionally, IPv6 packets with intermediate headers are not balanced based on layer 4 information. In practice these intermediate headers are not common and this should not cause any problems, and the alternative (a packet-parsing loop and look-up table) seemed slow and complicated for little gain.
Quoted 2 lines
Tested-by John Eaglesham <[email protected]>Signed-off-by John Eaglesham <[email protected]>
Applied, thanks a lot.
Re: [PATCH v8] bonding: support for IPv6 transmit hashing
In reply to John EagleshamThanks for getting this in John. Apologies for my earlier reply, where I hadn't spotted this revision of the patch; it looks like the comments I made have been addressed, and all is well.
Thanks again,
Jeremy
Quoted 261 lines · John Eaglesham
From: John Eaglesham <[email protected]>
Currently the "bonding" driver does not support load balancing outgoing traffic in LACP mode for IPv6 traffic. IPv4 (and TCP or UDP over IPv4) are currently supported; this patch adds transmit hashing for IPv6 (and TCP or UDP over IPv6), bringing IPv6 up to par with IPv4 support in the bonding driver. In addition, bounds checking has been added to all transmit hashing functions.
The algorithm chosen (xor'ing the bottom three quads of the source and destination addresses together, then xor'ing each byte of that result into the bottom byte, finally xor'ing with the last bytes of the MAC addresses) was selected after testing almost 400,000 unique IPv6 addresses harvested from server logs. This algorithm had the most even distribution for both big- and little-endian architectures while still using few instructions. Its behavior also attempts to closely match that of the IPv4 algorithm.
The IPv6 flow label was intentionally not included in the hash as it appears to be unset in the vast majority of IPv6 traffic sampled, and the current algorithm not using the flow label already offers a very even distribution.
Fragmented IPv6 packets are handled the same way as fragmented IPv4 packets, ie, they are not balanced based on layer 4 information. Additionally, IPv6 packets with intermediate headers are not balanced based on layer 4 information. In practice these intermediate headers are not common and this should not cause any problems, and the alternative (a packet-parsing loop and look-up table) seemed slow and complicated for little gain.
Tested-by John Eaglesham <[email protected]>Signed-off-by John Eaglesham <[email protected]>---
Changes:
v2)
* Clarify description
* Add bounds checking to more functions
* All functions callbond_xmit_hash_policy_l2rather than re- implement the same logic.
v3)
* Patch against net-next.
* Style corrections.
v4)
* Correct indenting.
v5)
* Squash documentation and code patches into one.
v6)
* Modify IPv6 hash to behave more like the IPv4 hash, update documentation with modified algorithm.
* Clean up formatting.
* Move all variable declaration to the top of the function.
* Minor change to IPv6 layer 4 hash to match IPv4 hash behavior (mix all hashed address bits together rather than just the bottom 24 bits).
v7)
* Improve bounds checking code (handle truncated IPv6 header, removed goto, fewer if statements).
* Re-write pseudocode in documentation to match actual code more closely.
* Correct indenting, align parentheses, wrap code at <= 80 columns (based on Jay's changes).
v8)
* Correct patch submission format.Documentation/networking/bonding.txt | 30 ++++++++++-- drivers/net/bonding/bond_main.c | 89 +++++++++++++++++++++++++----------- 2 files changed, 88 insertions(+), 31 deletions(-)Documentation/networking/bonding.txt +25
-5@@ -752,12 +752,22 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The formula is + generate the hash. The IPv4 formula is (((source IP XOR dest IP) AND 0xffff) XOR ( source MAC XOR destination MAC )) modulo slave count + The IPv6 formula is + + hash = (source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4) + + (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + XOR (source MAC XOR destination MAC)) + modulo slave count + This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, the formula is the same as for the layer2 transmit@@ -778,19 +788,29 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented TCP and UDP packets is + The formula for unfragmented IPv4 TCP and UDP packets is ((source port XOR dest port) XOR ((source IP XOR dest IP) AND 0xffff) modulo slave count - For fragmented TCP or UDP packets and all other IP - protocol traffic, the source and destination port + The formula for unfragmented IPv6 TCP and UDP packets is + + hash = (source port XOR dest port) XOR + ((source ip quad 2 XOR dest IP quad 2) XOR + (source ip quad 3 XOR dest IP quad 3) XOR + (source ip quad 4 XOR dest IP quad 4)) + + ((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) + modulo slave count + + For fragmented TCP or UDP packets and all other IPv4 and + IPv6 protocol traffic, the source and destination port information is omitted. For non-IP traffic, the formula is the same as for the layer2 transmit hash policy. - This policy is intended to mimic the behavior of + The IPv4 policy is intended to mimic the behavior of certain switches, notably Cisco switches with PFC2 as well as some Foundry and IBM products.drivers/net/bonding/bond_main.c +63
-27@@ -3354,56 +3354,93 @@ static struct notifier_block bond_netdev_notifier = {/*---------------------------- Hashing Policies -----------------------------*/ /* +* Hash for the output device based upon layer 2 data +*/ +static int bond_xmit_hash_policy_l2 (struct sk_buff *skb,int count) +{ +struct ethhdr *data = (struct ethhdr *)skb->data; + +if (skb_headlen (skb) >= offsetof(struct ethhdr , h_proto)) +return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; + +return 0 ; +} + +/* * Hash for the output device based upon layer 2 and layer 3 data. If -* the packet is not IP mimic bond_xmit_hash_policy_l2() +* the packet is not IP, fall back on bond_xmit_hash_policy_l2() */ static int bond_xmit_hash_policy_l23 (struct sk_buff *skb,int count) {struct ethhdr *data = (struct ethhdr *)skb->data; -struct iphdr *iph =ip_hdr (skb); - -if (skb->protocol ==htons (ETH_P_IP )) { +struct iphdr *iph; +struct ipv6hdr *ipv6h; +u32 v6hash; +__be32 *s, *d; + +if (skb->protocol ==htons (ETH_P_IP ) && +skb_network_header_len (skb) >=sizeof (*iph)) { + iph =ip_hdr (skb);return ((ntohl (iph->saddr ^ iph->daddr) &0xffff ) ^ (data->h_dest[5 ] ^ data->h_source[5 ])) % count; + }else if (skb->protocol ==htons (ETH_P_IPV6 ) && +skb_network_header_len (skb) >=sizeof (*ipv6h)) { + ipv6h =ipv6_hdr (skb); + s = &ipv6h->saddr.s6_addr32[0 ]; + d = &ipv6h->daddr.s6_addr32[0 ]; + v6hash = (s[1 ] ^ d[1 ]) ^ (s[2 ] ^ d[2 ]) ^ (s[3 ] ^ d[3 ]); + v6hash ^= (v6hash >>24 ) ^ (v6hash >>16 ) ^ (v6hash >>8 ); +return (v6hash ^ data->h_dest[5 ] ^ data->h_source[5 ]) % count; } -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; +return bond_xmit_hash_policy_l2 (skb, count); }/* * Hash for the output device based upon layer 3 and layer 4 data. If * the packet is a frag or not TCP or UDP, just use layer 3 data. If it is -* altogether not IP, mimic bond_xmit_hash_policy_l2() +* altogether not IP, fall back on bond_xmit_hash_policy_l2() */ static int bond_xmit_hash_policy_l34 (struct sk_buff *skb,int count) { -struct ethhdr *data = (struct ethhdr *)skb->data; -struct iphdr *iph =ip_hdr (skb); -__be16 *layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); -int layer4_xor =0 ; - -if (skb->protocol ==htons (ETH_P_IP )) { +u32 layer4_xor =0 ; +struct iphdr *iph; +struct ipv6hdr *ipv6h; +__be32 *s, *d; +__be16 *layer4hdr; + +if (skb->protocol ==htons (ETH_P_IP ) && +skb_network_header_len (skb) >=sizeof (*iph)) { + iph =ip_hdr (skb);if (!ip_is_fragment (iph) && (iph->protocol ==IPPROTO_TCP || - iph->protocol ==IPPROTO_UDP )) { - layer4_xor =ntohs ((*layer4hdr ^ *(layer4hdr +1 ))); + iph->protocol ==IPPROTO_UDP ) && + (skb_headlen (skb) -skb_network_offset (skb) >= + iph->ihl *sizeof (u32) +sizeof (*layer4hdr) *2 )) { + layer4hdr = (__be16 *)((u32 *)iph + iph->ihl); + layer4_xor =ntohs (*layer4hdr ^ *(layer4hdr +1 )); }return (layer4_xor ^ ((ntohl (iph->saddr ^ iph->daddr)) &0xffff )) % count; - + }else if (skb->protocol ==htons (ETH_P_IPV6 ) && +skb_network_header_len (skb) >=sizeof (*ipv6h)) { + ipv6h =ipv6_hdr (skb); +if ((ipv6h->nexthdr ==IPPROTO_TCP || + ipv6h->nexthdr ==IPPROTO_UDP ) && + (skb_headlen (skb) -skb_network_offset (skb) >= +sizeof (*ipv6h) +sizeof (*layer4hdr) *2 )) { + layer4hdr = (__be16 *)(ipv6h +1 ); + layer4_xor =ntohs (*layer4hdr ^ *(layer4hdr +1 )); + } + s = &ipv6h->saddr.s6_addr32[0 ]; + d = &ipv6h->daddr.s6_addr32[0 ]; + layer4_xor ^= (s[1 ] ^ d[1 ]) ^ (s[2 ] ^ d[2 ]) ^ (s[3 ] ^ d[3 ]); + layer4_xor ^= (layer4_xor >>24 ) ^ (layer4_xor >>16 ) ^ + (layer4_xor >>8 ); +return layer4_xor % count; } -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; -} - -/* -* Hash for the output device based upon layer 2 data -*/ -static int bond_xmit_hash_policy_l2 (struct sk_buff *skb,int count) -{ -struct ethhdr *data = (struct ethhdr *)skb->data; - -return (data->h_dest[5 ] ^ data->h_source[5 ]) % count; +return bond_xmit_hash_policy_l2 (skb, count); }/*-------------------------- Device entry points ----------------------------*/ --1.7.11
--
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html