提交 8052ba29 编写于 作者: A Alex Gartrell 提交者: Simon Horman

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: NAlex Gartrell <agartrell@fb.com>
Acked-by: NJulian Anastasov <ja@ssi.bg>
Signed-off-by: NSimon Horman <horms@verge.net.au>
上级 c63e4de2
...@@ -488,7 +488,12 @@ static inline void ip_vs_bind_xmit(struct ip_vs_conn *cp) ...@@ -488,7 +488,12 @@ static inline void ip_vs_bind_xmit(struct ip_vs_conn *cp)
break; break;
case IP_VS_CONN_F_TUNNEL: case IP_VS_CONN_F_TUNNEL:
cp->packet_xmit = ip_vs_tunnel_xmit; #ifdef CONFIG_IP_VS_IPV6
if (cp->daf == AF_INET6)
cp->packet_xmit = ip_vs_tunnel_xmit_v6;
else
#endif
cp->packet_xmit = ip_vs_tunnel_xmit;
break; break;
case IP_VS_CONN_F_DROUTE: case IP_VS_CONN_F_DROUTE:
...@@ -514,7 +519,10 @@ static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp) ...@@ -514,7 +519,10 @@ static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp)
break; break;
case IP_VS_CONN_F_TUNNEL: case IP_VS_CONN_F_TUNNEL:
cp->packet_xmit = ip_vs_tunnel_xmit_v6; if (cp->daf == AF_INET6)
cp->packet_xmit = ip_vs_tunnel_xmit_v6;
else
cp->packet_xmit = ip_vs_tunnel_xmit;
break; break;
case IP_VS_CONN_F_DROUTE: case IP_VS_CONN_F_DROUTE:
......
...@@ -824,6 +824,81 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -824,6 +824,81 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
} }
#endif #endif
/* When forwarding a packet, we must ensure that we've got enough headroom
* for the encapsulation packet in the skb. This also gives us an
* opportunity to figure out what the payload_len, dsfield, ttl, and df
* values should be, so that we won't need to look at the old ip header
* again
*/
static struct sk_buff *
ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af,
unsigned int max_headroom, __u8 *next_protocol,
__u32 *payload_len, __u8 *dsfield, __u8 *ttl,
__be16 *df)
{
struct sk_buff *new_skb = NULL;
struct iphdr *old_iph = NULL;
#ifdef CONFIG_IP_VS_IPV6
struct ipv6hdr *old_ipv6h = NULL;
#endif
if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) {
new_skb = skb_realloc_headroom(skb, max_headroom);
if (!new_skb)
goto error;
consume_skb(skb);
skb = new_skb;
}
#ifdef CONFIG_IP_VS_IPV6
if (skb_af == AF_INET6) {
old_ipv6h = ipv6_hdr(skb);
*next_protocol = IPPROTO_IPV6;
if (payload_len)
*payload_len =
ntohs(old_ipv6h->payload_len) +
sizeof(*old_ipv6h);
*dsfield = ipv6_get_dsfield(old_ipv6h);
*ttl = old_ipv6h->hop_limit;
if (df)
*df = 0;
} else
#endif
{
old_iph = ip_hdr(skb);
/* Copy DF, reset fragment offset and MF */
if (df)
*df = (old_iph->frag_off & htons(IP_DF));
*next_protocol = IPPROTO_IPIP;
/* fix old IP header checksum */
ip_send_check(old_iph);
*dsfield = ipv4_get_dsfield(old_iph);
*ttl = old_iph->ttl;
if (payload_len)
*payload_len = ntohs(old_iph->tot_len);
}
return skb;
error:
kfree_skb(skb);
return ERR_PTR(-ENOMEM);
}
static inline int __tun_gso_type_mask(int encaps_af, int orig_af)
{
if (encaps_af == AF_INET) {
if (orig_af == AF_INET)
return SKB_GSO_IPIP;
return SKB_GSO_SIT;
}
/* GSO: we need to provide proper SKB_GSO_ value for IPv6:
* SKB_GSO_SIT/IPV6
*/
return 0;
}
/* /*
* IP Tunneling transmitter * IP Tunneling transmitter
...@@ -852,9 +927,11 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -852,9 +927,11 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct rtable *rt; /* Route to the other host */ struct rtable *rt; /* Route to the other host */
__be32 saddr; /* Source for tunnel */ __be32 saddr; /* Source for tunnel */
struct net_device *tdev; /* Device to other host */ struct net_device *tdev; /* Device to other host */
struct iphdr *old_iph = ip_hdr(skb); __u8 next_protocol = 0;
u8 tos = old_iph->tos; __u8 dsfield = 0;
__be16 df; __u8 ttl = 0;
__be16 df = 0;
__be16 *dfp = NULL;
struct iphdr *iph; /* Our new IP header */ struct iphdr *iph; /* Our new IP header */
unsigned int max_headroom; /* The extra header space needed */ unsigned int max_headroom; /* The extra header space needed */
int ret, local; int ret, local;
...@@ -877,29 +954,21 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -877,29 +954,21 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
rt = skb_rtable(skb); rt = skb_rtable(skb);
tdev = rt->dst.dev; tdev = rt->dst.dev;
/* Copy DF, reset fragment offset and MF */
df = sysctl_pmtu_disc(ipvs) ? old_iph->frag_off & htons(IP_DF) : 0;
/* /*
* Okay, now see if we can stuff it in the buffer as-is. * Okay, now see if we can stuff it in the buffer as-is.
*/ */
max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr); max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr);
if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) { /* We only care about the df field if sysctl_pmtu_disc(ipvs) is set */
struct sk_buff *new_skb = dfp = sysctl_pmtu_disc(ipvs) ? &df : NULL;
skb_realloc_headroom(skb, max_headroom); skb = ip_vs_prepare_tunneled_skb(skb, cp->af, max_headroom,
&next_protocol, NULL, &dsfield,
if (!new_skb) &ttl, dfp);
goto tx_error; if (IS_ERR(skb))
consume_skb(skb); goto tx_error;
skb = new_skb;
old_iph = ip_hdr(skb);
}
/* fix old IP header checksum */
ip_send_check(old_iph);
skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP); skb = iptunnel_handle_offloads(
skb, false, __tun_gso_type_mask(AF_INET, cp->af));
if (IS_ERR(skb)) if (IS_ERR(skb))
goto tx_error; goto tx_error;
...@@ -916,11 +985,11 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -916,11 +985,11 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
iph->version = 4; iph->version = 4;
iph->ihl = sizeof(struct iphdr)>>2; iph->ihl = sizeof(struct iphdr)>>2;
iph->frag_off = df; iph->frag_off = df;
iph->protocol = IPPROTO_IPIP; iph->protocol = next_protocol;
iph->tos = tos; iph->tos = dsfield;
iph->daddr = cp->daddr.ip; iph->daddr = cp->daddr.ip;
iph->saddr = saddr; iph->saddr = saddr;
iph->ttl = old_iph->ttl; iph->ttl = ttl;
ip_select_ident(skb, NULL); ip_select_ident(skb, NULL);
/* Another hack: avoid icmp_send in ip_fragment */ /* Another hack: avoid icmp_send in ip_fragment */
...@@ -953,7 +1022,10 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -953,7 +1022,10 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct rt6_info *rt; /* Route to the other host */ struct rt6_info *rt; /* Route to the other host */
struct in6_addr saddr; /* Source for tunnel */ struct in6_addr saddr; /* Source for tunnel */
struct net_device *tdev; /* Device to other host */ struct net_device *tdev; /* Device to other host */
struct ipv6hdr *old_iph = ipv6_hdr(skb); __u8 next_protocol = 0;
__u32 payload_len = 0;
__u8 dsfield = 0;
__u8 ttl = 0;
struct ipv6hdr *iph; /* Our new IP header */ struct ipv6hdr *iph; /* Our new IP header */
unsigned int max_headroom; /* The extra header space needed */ unsigned int max_headroom; /* The extra header space needed */
int ret, local; int ret, local;
...@@ -981,19 +1053,14 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -981,19 +1053,14 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/ */
max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr); max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr);
if (skb_headroom(skb) < max_headroom || skb_cloned(skb)) { skb = ip_vs_prepare_tunneled_skb(skb, cp->af, max_headroom,
struct sk_buff *new_skb = &next_protocol, &payload_len,
skb_realloc_headroom(skb, max_headroom); &dsfield, &ttl, NULL);
if (IS_ERR(skb))
if (!new_skb) goto tx_error;
goto tx_error;
consume_skb(skb);
skb = new_skb;
old_iph = ipv6_hdr(skb);
}
/* GSO: we need to provide proper SKB_GSO_ value for IPv6 */ skb = iptunnel_handle_offloads(
skb = iptunnel_handle_offloads(skb, false, 0); /* SKB_GSO_SIT/IPV6 */ skb, false, __tun_gso_type_mask(AF_INET6, cp->af));
if (IS_ERR(skb)) if (IS_ERR(skb))
goto tx_error; goto tx_error;
...@@ -1008,14 +1075,13 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1008,14 +1075,13 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/ */
iph = ipv6_hdr(skb); iph = ipv6_hdr(skb);
iph->version = 6; iph->version = 6;
iph->nexthdr = IPPROTO_IPV6; iph->nexthdr = next_protocol;
iph->payload_len = old_iph->payload_len; iph->payload_len = htons(payload_len);
be16_add_cpu(&iph->payload_len, sizeof(*old_iph));
memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl)); memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl));
ipv6_change_dsfield(iph, 0, ipv6_get_dsfield(old_iph)); ipv6_change_dsfield(iph, 0, dsfield);
iph->daddr = cp->daddr.in6; iph->daddr = cp->daddr.in6;
iph->saddr = saddr; iph->saddr = saddr;
iph->hop_limit = old_iph->hop_limit; iph->hop_limit = ttl;
/* Another hack: avoid icmp_send in ip_fragment */ /* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1; skb->ignore_df = 1;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册