diff options
| author | Alex Gartrell <agartrell@fb.com> | 2014-09-09 19:40:28 -0400 |
|---|---|---|
| committer | Simon Horman <horms@verge.net.au> | 2014-09-15 20:03:37 -0400 |
| commit | 8052ba292559f907ea2ad4c827d83c195046dfe1 (patch) | |
| tree | 00c88663346c7b8c91eed54cf2af8fe5b7e9eb61 | |
| parent | c63e4de2be5e1d253adce16dbba57ed42868bc22 (diff) | |
ipvs: support ipv4 in ipv6 and ipv6 in ipv4 tunnel forwarding
Pull the common logic for preparing an skb to prepend the header into a
single function and then set fields such that they can be used in either
case (generalize tos and tclass to dscp, hop_limit and ttl to ttl, etc)
Signed-off-by: Alex Gartrell <agartrell@fb.com>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_conn.c | 12 | ||||
| -rw-r--r-- | net/netfilter/ipvs/ip_vs_xmit.c | 148 |
2 files changed, 117 insertions, 43 deletions
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index fdb4880a3a79..13e9cee02c81 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c | |||
| @@ -488,7 +488,12 @@ static inline void ip_vs_bind_xmit(struct ip_vs_conn *cp) | |||
| 488 | break; | 488 | break; |
| 489 | 489 | ||
| 490 | case IP_VS_CONN_F_TUNNEL: | 490 | case IP_VS_CONN_F_TUNNEL: |
| 491 | cp->packet_xmit = ip_vs_tunnel_xmit; | 491 | #ifdef CONFIG_IP_VS_IPV6 |
| 492 | if (cp->daf == AF_INET6) | ||
| 493 | cp->packet_xmit = ip_vs_tunnel_xmit_v6; | ||
| 494 | else | ||
| 495 | #endif | ||
| 496 | cp->packet_xmit = ip_vs_tunnel_xmit; | ||
| 492 | break; | 497 | break; |
| 493 | 498 | ||
| 494 | case IP_VS_CONN_F_DROUTE: | 499 | case IP_VS_CONN_F_DROUTE: |
| @@ -514,7 +519,10 @@ static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp) | |||
| 514 | break; | 519 | break; |
| 515 | 520 | ||
| 516 | case IP_VS_CONN_F_TUNNEL: | 521 | case IP_VS_CONN_F_TUNNEL: |
| 517 | cp->packet_xmit = ip_vs_tunnel_xmit_v6; | 522 | if (cp->daf == AF_INET6) |
| 523 | cp->packet_xmit = ip_vs_tunnel_xmit_v6; | ||
| 524 | else | ||
| 525 | cp->packet_xmit = ip_vs_tunnel_xmit; | ||
| 518 | break; | 526 | break; |
| 519 | 527 | ||
| 520 | case IP_VS_CONN_F_DROUTE: | 528 | case IP_VS_CONN_F_DROUTE: |
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index fa2fdd7421b7..91f17c1eb8a2 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c | |||
| @@ -824,6 +824,81 @@ tx_error: | |||
| 824 | } | 824 | } |
| 825 | #endif | 825 | #endif |
| 826 | 826 | ||
| 827 | /* When forwarding a packet, we must ensure that we've got enough headroom | ||
| 828 | * for the encapsulation packet in the skb. This also gives us an | ||
| 829 | * opportunity to figure out what the payload_len, dsfield, ttl, and df | ||
| 830 | * values should be, so that we won't need to look at the old ip header | ||
| 831 | * again | ||
| 832 | */ | ||
| 833 | static struct sk_buff * | ||
| 834 | ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af, | ||
| 835 | unsigned int max_headroom, __u8 *next_protocol, | ||
| 836 | __u32 *payload_len, __u8 *dsfield, __u8 *ttl, | ||
| 837 | __be16 *df) | ||
| 838 | { | ||
| 839 | struct sk_buff *new_skb = NULL; | ||
| 840 | struct iphdr *old_iph = NULL; | ||
| 841 | #ifdef CONFIG_IP_VS_IPV6 | ||
| 842 | struct ipv6hdr *old_ipv6h = NULL; | ||
| 843 | #endif | ||
| 844 | |||
| 845 | if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) { | ||
| 846 | new_skb = skb_realloc_headroom(skb, max_headroom); | ||
| 847 | if (!new_skb) | ||
| 848 | goto error; | ||
| 849 | consume_skb(skb); | ||
| 850 | skb = new_skb; | ||
| 851 | } | ||
| 852 | |||
| 853 | #ifdef CONFIG_IP_VS_IPV6 | ||
| 854 | if (skb_af == AF_INET6) { | ||
| 855 | old_ipv6h = ipv6_hdr(skb); | ||
| 856 | *next_protocol = IPPROTO_IPV6; | ||
| 857 | if (payload_len) | ||
| 858 | *payload_len = | ||
| 859 | ntohs(old_ipv6h->payload_len) + | ||
| 860 | sizeof(*old_ipv6h); | ||
| 861 | *dsfield = ipv6_get_dsfield(old_ipv6h); | ||
| 862 | *ttl = old_ipv6h->hop_limit; | ||
| 863 | if (df) | ||
| 864 | *df = 0; | ||
| 865 | } else | ||
| 866 | #endif | ||
| 867 | { | ||
| 868 | old_iph = ip_hdr(skb); | ||
| 869 | /* Copy DF, reset fragment offset and MF */ | ||
| 870 | if (df) | ||
| 871 | *df = (old_iph->frag_off & htons(IP_DF)); | ||
| 872 | *next_protocol = IPPROTO_IPIP; | ||
| 873 | |||
| 874 | /* fix old IP header checksum */ | ||
| 875 | ip_send_check(old_iph); | ||
| 876 | *dsfield = ipv4_get_dsfield(old_iph); | ||
| 877 | *ttl = old_iph->ttl; | ||
| 878 | if (payload_len) | ||
| 879 | *payload_len = ntohs(old_iph->tot_len); | ||
| 880 | } | ||
| 881 | |||
| 882 | return skb; | ||
| 883 | error: | ||
| 884 | kfree_skb(skb); | ||
| 885 | return ERR_PTR(-ENOMEM); | ||
| 886 | } | ||
| 887 | |||
| 888 | static inline int __tun_gso_type_mask(int encaps_af, int orig_af) | ||
| 889 | { | ||
| 890 | if (encaps_af == AF_INET) { | ||
| 891 | if (orig_af == AF_INET) | ||
| 892 | return SKB_GSO_IPIP; | ||
| 893 | |||
| 894 | return SKB_GSO_SIT; | ||
| 895 | } | ||
| 896 | |||
| 897 | /* GSO: we need to provide proper SKB_GSO_ value for IPv6: | ||
| 898 | * SKB_GSO_SIT/IPV6 | ||
| 899 | */ | ||
| 900 | return 0; | ||
| 901 | } | ||
| 827 | 902 | ||
| 828 | /* | 903 | /* |
| 829 | * IP Tunneling transmitter | 904 | * IP Tunneling transmitter |
| @@ -852,9 +927,11 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
| 852 | struct rtable *rt; /* Route to the other host */ | 927 | struct rtable *rt; /* Route to the other host */ |
| 853 | __be32 saddr; /* Source for tunnel */ | 928 | __be32 saddr; /* Source for tunnel */ |
| 854 | struct net_device *tdev; /* Device to other host */ | 929 | struct net_device *tdev; /* Device to other host */ |
| 855 | struct iphdr *old_iph = ip_hdr(skb); | 930 | __u8 next_protocol = 0; |
| 856 | u8 tos = old_iph->tos; | 931 | __u8 dsfield = 0; |
| 857 | __be16 df; | 932 | __u8 ttl = 0; |
| 933 | __be16 df = 0; | ||
| 934 | __be16 *dfp = NULL; | ||
| 858 | struct iphdr *iph; /* Our new IP header */ | 935 | struct iphdr *iph; /* Our new IP header */ |
| 859 | unsigned int max_headroom; /* The extra header space needed */ | 936 | unsigned int max_headroom; /* The extra header space needed */ |
| 860 | int ret, local; | 937 | int ret, local; |
| @@ -877,29 +954,21 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
| 877 | rt = skb_rtable(skb); | 954 | rt = skb_rtable(skb); |
| 878 | tdev = rt->dst.dev; | 955 | tdev = rt->dst.dev; |
| 879 | 956 | ||
| 880 | /* Copy DF, reset fragment offset and MF */ | ||
| 881 | df = sysctl_pmtu_disc(ipvs) ? old_iph->frag_off & htons(IP_DF) : 0; | ||
| 882 | |||
| 883 | /* | 957 | /* |
| 884 | * Okay, now see if we can stuff it in the buffer as-is. | 958 | * Okay, now see if we can stuff it in the buffer as-is. |
| 885 | */ | 959 | */ |
| 886 | max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr); | 960 | max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr); |
| 887 | 961 | ||
| 888 | if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) { | 962 | /* We only care about the df field if sysctl_pmtu_disc(ipvs) is set */ |
| 889 | struct sk_buff *new_skb = | 963 | dfp = sysctl_pmtu_disc(ipvs) ? &df : NULL; |
| 890 | skb_realloc_headroom(skb, max_headroom); | 964 | skb = ip_vs_prepare_tunneled_skb(skb, cp->af, max_headroom, |
| 891 | 965 | &next_protocol, NULL, &dsfield, | |
| 892 | if (!new_skb) | 966 | &ttl, dfp); |
| 893 | goto tx_error; | 967 | if (IS_ERR(skb)) |
| 894 | consume_skb(skb); | 968 | goto tx_error; |
| 895 | skb = new_skb; | ||
| 896 | old_iph = ip_hdr(skb); | ||
| 897 | } | ||
| 898 | |||
| 899 | /* fix old IP header checksum */ | ||
| 900 | ip_send_check(old_iph); | ||
| 901 | 969 | ||
| 902 | skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP); | 970 | skb = iptunnel_handle_offloads( |
| 971 | skb, false, __tun_gso_type_mask(AF_INET, cp->af)); | ||
| 903 | if (IS_ERR(skb)) | 972 | if (IS_ERR(skb)) |
| 904 | goto tx_error; | 973 | goto tx_error; |
| 905 | 974 | ||
| @@ -916,11 +985,11 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, | |||
| 916 | iph->version = 4; | 985 | iph->version = 4; |
| 917 | iph->ihl = sizeof(struct iphdr)>>2; | 986 | iph->ihl = sizeof(struct iphdr)>>2; |
| 918 | iph->frag_off = df; | 987 | iph->frag_off = df; |
| 919 | iph->protocol = IPPROTO_IPIP; | 988 | iph->protocol = next_protocol; |
| 920 | iph->tos = tos; | 989 | iph->tos = dsfield; |
| 921 | iph->daddr = cp->daddr.ip; | 990 | iph->daddr = cp->daddr.ip; |
| 922 | iph->saddr = saddr; | 991 | iph->saddr = saddr; |
| 923 | iph->ttl = | ||
