aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Gartrell <agartrell@fb.com>2014-09-09 19:40:28 -0400
committerSimon Horman <horms@verge.net.au>2014-09-15 20:03:37 -0400
commit8052ba292559f907ea2ad4c827d83c195046dfe1 (patch)
tree00c88663346c7b8c91eed54cf2af8fe5b7e9eb61
parentc63e4de2be5e1d253adce16dbba57ed42868bc22 (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.c12
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c148
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 */
833static struct sk_buff *
834ip_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;
883error:
884 kfree_skb(skb);
885 return ERR_PTR(-ENOMEM);
886}
887
888static 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 =