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>
This commit is contained in:
Родитель
c63e4de2be
Коммит
8052ba2925
|
@ -488,6 +488,11 @@ 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:
|
||||||
|
#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;
|
cp->packet_xmit = ip_vs_tunnel_xmit;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
if (cp->daf == AF_INET6)
|
||||||
cp->packet_xmit = ip_vs_tunnel_xmit_v6;
|
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 @@ tx_error:
|
||||||
}
|
}
|
||||||
#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,
|
||||||
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,
|
||||||
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);
|
||||||
|
if (IS_ERR(skb))
|
||||||
goto tx_error;
|
goto tx_error;
|
||||||
consume_skb(skb);
|
|
||||||
skb = new_skb;
|
|
||||||
old_iph = ip_hdr(skb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fix old IP header checksum */
|
skb = iptunnel_handle_offloads(
|
||||||
ip_send_check(old_iph);
|
skb, false, __tun_gso_type_mask(AF_INET, cp->af));
|
||||||
|
|
||||||
skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP);
|
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
*/
|
*/
|
||||||
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,
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче