Commit 63dca2c0 authored by Jesper Dangaard Brouer's avatar Jesper Dangaard Brouer Committed by Simon Horman
Browse files

ipvs: Fix faulty IPv6 extension header handling in IPVS

IPv6 packets can contain extension headers, thus its wrong to assume
that the transport/upper-layer header, starts right after (struct
ipv6hdr) the IPv6 header.  IPVS uses this false assumption, and will
write SNAT & DNAT modifications at a fixed pos which will corrupt the
message.

To fix this, proper header position must be found before modifying
packets.  Introducing ip_vs_fill_iph_skb(), which uses ipv6_find_hdr()
to skip the exthdrs. It finds (1) the transport header offset, (2) the
protocol, and (3) detects if the packet is a fragment.

Note, that fragments in IPv6 is represented via an exthdr.  Thus, this
is detected while skipping through the exthdrs.

This patch depends on commit 84018f55

:
 "netfilter: ip6_tables: add flags parameter to ipv6_find_hdr()"
This also adds a dependency to ip6_tables.

Originally based on patch from: Hans Schillstrom

kABI notes:
Changing struct ip_vs_iphdr is a potential minor kABI breaker,
because external modules can be compiled with another version of
this struct.  This should not matter, as they would most-likely
be using a compiled-in version of ip_vs_fill_iphdr().  When
recompiled, they will notice ip_vs_fill_iphdr() no longer exists,
and they have to used ip_vs_fill_iph_skb() instead.
Signed-off-by: default avatarJesper Dangaard Brouer <brouer@redhat.com>
Acked-by: default avatarJulian Anastasov <ja@ssi.bg>
Signed-off-by: default avatarSimon Horman <horms@verge.net.au>
parent a638e514
......@@ -22,6 +22,9 @@
#include <linux/ip.h>
#include <linux/ipv6.h> /* for struct ipv6hdr */
#include <net/ipv6.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <linux/netfilter_ipv6/ip6_tables.h>
#endif
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h>
#endif
......@@ -103,30 +106,79 @@ static inline struct net *seq_file_single_net(struct seq_file *seq)
/* Connections' size value needed by ip_vs_ctl.c */
extern int ip_vs_conn_tab_size;
struct ip_vs_iphdr {
int len;
__u8 protocol;
__u32 len; /* IPv4 simply where L4 starts
IPv6 where L4 Transport Header starts */
__u16 fragoffs; /* IPv6 fragment offset, 0 if first frag (or not frag)*/
__s16 protocol;
__s32 flags;
union nf_inet_addr saddr;
union nf_inet_addr daddr;
};
static inline void
ip_vs_fill_iphdr(int af, const void *nh, struct ip_vs_iphdr *iphdr)
ip_vs_fill_ip4hdr(const void *nh, struct ip_vs_iphdr *iphdr)
{
const struct iphdr *iph = nh;
iphdr->len = iph->ihl * 4;
iphdr->fragoffs = 0;
iphdr->protocol = iph->protocol;
iphdr->saddr.ip = iph->saddr;
iphdr->daddr.ip = iph->daddr;
}
/* This function handles filling *ip_vs_iphdr, both for IPv4 and IPv6.
* IPv6 requires some extra work, as finding proper header position,
* depend on the IPv6 extension headers.
*/
static inline void
ip_vs_fill_iph_skb(int af, const struct sk_buff *skb, struct ip_vs_iphdr *iphdr)
{
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
const struct ipv6hdr *iph = nh;
iphdr->len = sizeof(struct ipv6hdr);
iphdr->protocol = iph->nexthdr;
const struct ipv6hdr *iph =
(struct ipv6hdr *)skb_network_header(skb);
iphdr->saddr.in6 = iph->saddr;
iphdr->daddr.in6 = iph->daddr;
/* ipv6_find_hdr() updates len, flags */
iphdr->len = 0;
iphdr->flags = 0;
iphdr->protocol = ipv6_find_hdr(skb, &iphdr->len, -1,
&iphdr->fragoffs,
&iphdr->flags);
} else
#endif
{
const struct iphdr *iph = nh;
iphdr->len = iph->ihl * 4;
iphdr->protocol = iph->protocol;
const struct iphdr *iph =
(struct iphdr *)skb_network_header(skb);
iphdr->len = iph->ihl * 4;
iphdr->fragoffs = 0;
iphdr->protocol = iph->protocol;
iphdr->saddr.ip = iph->saddr;
iphdr->daddr.ip = iph->daddr;
}
}
/* This function is a faster version of ip_vs_fill_iph_skb().
* Where we only populate {s,d}addr (and avoid calling ipv6_find_hdr()).
* This is used by the some of the ip_vs_*_schedule() functions.
* (Mostly done to avoid ABI breakage of external schedulers)
*/
static inline void
ip_vs_fill_iph_addr_only(int af, const struct sk_buff *skb,
struct ip_vs_iphdr *iphdr)
{
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
const struct ipv6hdr *iph =
(struct ipv6hdr *)skb_network_header(skb);
iphdr->saddr.in6 = iph->saddr;
iphdr->daddr.in6 = iph->daddr;
} else {
#endif
const struct iphdr *iph =
(struct iphdr *)skb_network_header(skb);
iphdr->saddr.ip = iph->saddr;
iphdr->daddr.ip = iph->daddr;
}
......
......@@ -28,6 +28,7 @@ if IP_VS
config IP_VS_IPV6
bool "IPv6 support for IPVS"
depends on IPV6 = y || IP_VS = IPV6
select IP6_NF_IPTABLES
---help---
Add IPv6 support to IPVS. This is incomplete and might be dangerous.
......
......@@ -236,7 +236,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
union nf_inet_addr snet; /* source network of the client,
after masking */
ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(svc->af, skb, &iph);
/* Mask saddr with the netmask to adjust template granularity */
#ifdef CONFIG_IP_VS_IPV6
......@@ -402,7 +402,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
unsigned int flags;
*ignored = 1;
ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(svc->af, skb, &iph);
pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports);
if (pptr == NULL)
return NULL;
......@@ -506,7 +506,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
int unicast;
#endif
ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(svc->af, skb, &iph);
pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports);
if (pptr == NULL) {
......@@ -732,10 +732,19 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int inout)
{
struct ipv6hdr *iph = ipv6_hdr(skb);
unsigned int icmp_offset = sizeof(struct ipv6hdr);
struct icmp6hdr *icmph = (struct icmp6hdr *)(skb_network_header(skb) +
icmp_offset);
struct ipv6hdr *ciph = (struct ipv6hdr *)(icmph + 1);
unsigned int icmp_offset = 0;
unsigned int offs = 0; /* header offset*/
int protocol;
struct icmp6hdr *icmph;
struct ipv6hdr *ciph;
unsigned short fragoffs;
ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
offs = icmp_offset + sizeof(struct icmp6hdr);
ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
if (inout) {
iph->saddr = cp->vaddr.in6;
......@@ -746,10 +755,13 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
}
/* the TCP/UDP/SCTP port */
if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr ||
IPPROTO_SCTP == ciph->nexthdr) {
__be16 *ports = (void *)ciph + sizeof(struct ipv6hdr);
if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
IPPROTO_SCTP == protocol)) {
__be16 *ports = (void *)(skb_network_header(skb) + offs);
IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
ntohs(inout ? ports[1] : ports[0]),
ntohs(inout ? cp->vport : cp->dport));
if (inout)
ports[1] = cp->vport;
else
......@@ -898,9 +910,8 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
"Checking outgoing ICMP for");
offset += cih->ihl * 4;
ip_vs_fill_iphdr(AF_INET, cih, &ciph);
ip_vs_fill_ip4hdr(cih, &ciph);
ciph.len += offset;
/* The embedded headers contain source and dest in reverse order */
cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1);
if (!cp)
......@@ -908,41 +919,31 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
snet.ip = iph->saddr;
return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
pp, offset, ihl);
pp, ciph.len, ihl);
}
#ifdef CONFIG_IP_VS_IPV6
static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
unsigned int hooknum)
{
struct ipv6hdr *iph;
struct icmp6hdr _icmph, *ic;
struct ipv6hdr _ciph, *cih; /* The ip header contained
within the ICMP */
struct ip_vs_iphdr ciph;
struct ipv6hdr _ip6h, *ip6h; /* The ip header contained within ICMP */
struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
unsigned int offset;
union nf_inet_addr snet;
unsigned int writable;
*related = 1;
struct ip_vs_iphdr ipvsh_stack;
struct ip_vs_iphdr *ipvsh = &ipvsh_stack;
ip_vs_fill_iph_skb(AF_INET6, skb, ipvsh);
/* reassemble IP fragments */
if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) {
if (ip_vs_gather_frags_v6(skb, ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
}
*related = 1;
iph = ipv6_hdr(skb);
offset = sizeof(struct ipv6hdr);
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
ic = skb_header_pointer(skb, ipvsh->len, sizeof(_icmph), &_icmph);
if (ic == NULL)
return NF_DROP;
IP_VS_DBG(12, "Outgoing ICMPv6 (%d,%d) %pI6->%pI6\n",
ic->icmp6_type, ntohs(icmpv6_id(ic)),
&iph->saddr, &iph->daddr);
/*
* Work through seeing if this is for us.
* These checks are supposed to be in an order that means easy
......@@ -955,35 +956,35 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
return NF_ACCEPT;
}
IP_VS_DBG(8, "Outgoing ICMPv6 (%d,%d) %pI6c->%pI6c\n",
ic->icmp6_type, ntohs(icmpv6_id(ic)),
&ipvsh->saddr, &ipvsh->daddr);
/* Now find the contained IP header */
offset += sizeof(_icmph);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
if (cih == NULL)
ciph.len = ipvsh->len + sizeof(_icmph);
ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
if (ip6h == NULL)
return NF_ACCEPT; /* The packet looks wrong, ignore */
pp = ip_vs_proto_get(cih->nexthdr);
ciph.saddr.in6 = ip6h->saddr; /* conn_out_get() handles reverse order */
ciph.daddr.in6 = ip6h->daddr;
/* skip possible IPv6 exthdrs of contained IPv6 packet */
ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL);
if (ciph.protocol < 0)
return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */
pp = ip_vs_proto_get(ciph.protocol);
if (!pp)
return NF_ACCEPT;
/* Is the embedded protocol header present? */
/* TODO: we don't support fragmentation at the moment anyways */
if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag))
return NF_ACCEPT;
IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
"Checking outgoing ICMPv6 for");
offset += sizeof(struct ipv6hdr);
ip_vs_fill_iphdr(AF_INET6, cih, &ciph);
/* The embedded headers contain source and dest in reverse order */
cp = pp->conn_out_get(AF_INET6, skb, &ciph, offset, 1);
cp = pp->conn_out_get(AF_INET6, skb, &ciph, ciph.len, 1);
if (!cp)
return NF_ACCEPT;
snet.in6 = iph->saddr;
return handle_response_icmp(AF_INET6, skb, &snet, cih->nexthdr, cp,
pp, offset, sizeof(struct ipv6hdr));
snet.in6 = ciph.saddr.in6;
writable = ciph.len;
return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
pp, writable, sizeof(struct ipv6hdr));
}
#endif
......@@ -1113,7 +1114,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
if (!net_ipvs(net)->enable)
return NF_ACCEPT;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(af, skb, &iph);
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
if (unlikely(iph.protocol == IPPROTO_ICMPV6)) {
......@@ -1123,7 +1124,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
if (related)
return verdict;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(af, skb, &iph);
}
} else
#endif
......@@ -1133,7 +1134,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
if (related)
return verdict;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
ip_vs_fill_ip4hdr(skb_network_header(skb), &iph);
}
pd = ip_vs_proto_data_get(net, iph.protocol);
......@@ -1143,22 +1144,14 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
/* reassemble IP fragments */
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) {
if (ip_vs_gather_frags_v6(skb,
ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
}
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
} else
if (af == AF_INET)
#endif
if (unlikely(ip_is_fragment(ip_hdr(skb)) && !pp->dont_defrag)) {
if (ip_vs_gather_frags(skb,
ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
ip_vs_fill_ip4hdr(skb_network_header(skb), &iph);
}
/*
......@@ -1373,9 +1366,9 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
"Checking incoming ICMP for");
offset2 = offset;
offset += cih->ihl * 4;
ip_vs_fill_iphdr(AF_INET, cih, &ciph);
ip_vs_fill_ip4hdr(cih, &ciph);
ciph.len += offset;
offset = ciph.len;
/* The embedded headers contain source and dest in reverse order.
* For IPIP this is error for request, not for reply.
*/
......@@ -1461,34 +1454,24 @@ static int
ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
{
struct net *net = NULL;
struct ipv6hdr *iph;
struct ipv6hdr _ip6h, *ip6h;
struct icmp6hdr _icmph, *ic;
struct ipv6hdr _ciph, *cih; /* The ip header contained
within the ICMP */
struct ip_vs_iphdr ciph;
struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
struct ip_vs_proto_data *pd;
unsigned int offset, verdict;
unsigned int offs_ciph, writable, verdict;
*related = 1;
struct ip_vs_iphdr iph_stack;
struct ip_vs_iphdr *iph = &iph_stack;
ip_vs_fill_iph_skb(AF_INET6, skb, iph);
/* reassemble IP fragments */
if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) {
if (ip_vs_gather_frags_v6(skb, ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
}
*related = 1;
iph = ipv6_hdr(skb);
offset = sizeof(struct ipv6hdr);
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
ic = skb_header_pointer(skb, iph->len, sizeof(_icmph), &_icmph);
if (ic == NULL)
return NF_DROP;
IP_VS_DBG(12, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n",
ic->icmp6_type, ntohs(icmpv6_id(ic)),
&iph->saddr, &iph->daddr);
/*
* Work through seeing if this is for us.
* These checks are supposed to be in an order that means easy
......@@ -1501,40 +1484,51 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
return NF_ACCEPT;
}
IP_VS_DBG(8, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n",
ic->icmp6_type, ntohs(icmpv6_id(ic)),
&iph->saddr, &iph->daddr);
/* Now find the contained IP header */
offset += sizeof(_icmph);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
if (cih == NULL)
ciph.len = iph->len + sizeof(_icmph);
offs_ciph = ciph.len; /* Save ip header offset */
ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
if (ip6h == NULL)
return NF_ACCEPT; /* The packet looks wrong, ignore */
ciph.saddr.in6 = ip6h->saddr; /* conn_in_get() handles reverse order */
ciph.daddr.in6 = ip6h->daddr;
/* skip possible IPv6 exthdrs of contained IPv6 packet */
ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL);
if (ciph.protocol < 0)
return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */
net = skb_net(skb);
pd = ip_vs_proto_data_get(net, cih->nexthdr);
pd = ip_vs_proto_data_get(net, ciph.protocol);
if (!pd)
return NF_ACCEPT;
pp = pd->pp;
/* Is the embedded protocol header present? */
/* TODO: we don't support fragmentation at the moment anyways */
if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag))
/* Cannot handle fragmented embedded protocol */
if (ciph.fragoffs)
return NF_ACCEPT;
IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offs_ciph,
"Checking incoming ICMPv6 for");
offset += sizeof(struct ipv6hdr);
ip_vs_fill_iphdr(AF_INET6, cih, &ciph);
/* The embedded headers contain source and dest in reverse order */
cp = pp->conn_in_get(AF_INET6, skb, &ciph, offset, 1);
cp = pp->conn_in_get(AF_INET6, skb, &ciph, ciph.len, 1);
if (!cp)
return NF_ACCEPT;
/* do the statistics and put it back */
ip_vs_in_stats(cp, skb);
if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr ||
IPPROTO_SCTP == cih->nexthdr)
offset += 2 * sizeof(__u16);
verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum);
/* Need to mangle contained IPv6 header in ICMPv6 packet */
writable = ciph.len;
if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
IPPROTO_SCTP == ciph.protocol)
writable += 2 * sizeof(__u16); /* Also mangle ports */
verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, writable, hooknum);
__ip_vs_conn_put(cp);
......@@ -1570,7 +1564,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (unlikely((skb->pkt_type != PACKET_HOST &&
hooknum != NF_INET_LOCAL_OUT) ||
!skb_dst(skb))) {
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(af, skb, &iph);
IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s"
" ignored in hook %u\n",
skb->pkt_type, iph.protocol,
......@@ -1582,7 +1576,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (!net_ipvs(net)->enable)
return NF_ACCEPT;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(af, skb, &iph);
/* Bad... Do not break raw sockets */
if (unlikely(skb->sk != NULL && hooknum == NF_INET_LOCAL_OUT &&
......@@ -1602,7 +1596,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (related)
return verdict;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
}
} else
#endif
......@@ -1612,7 +1605,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (related)
return verdict;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
}
/* Protocol supported? */
......@@ -1622,10 +1614,11 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
pp = pd->pp;
/*
* Check if the packet belongs to an existing connection entry
* Only sched first IPv6 fragment.
*/
cp = pp->conn_in_get(af, skb, &iph, iph.len, 0);
if (unlikely(!cp)) {
if (unlikely(!cp) && !iph.fragoffs) {
int v;
if (!pp->conn_schedule(af, skb, pd, &v, &cp))
......@@ -1789,8 +1782,10 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb,
{
int r;
struct net *net;
struct ip_vs_iphdr iphdr;
if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6)
ip_vs_fill_iph_skb(AF_INET6, skb, &iphdr);
if (iphdr.protocol != IPPROTO_ICMPV6)
return NF_ACCEPT;
/* ipvs enabled in this netns ? */
......
......@@ -215,7 +215,7 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
struct ip_vs_dh_bucket *tbl;
struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
......
......@@ -479,7 +479,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
struct ip_vs_dest *dest = NULL;
struct ip_vs_lblc_entry *en;
ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
......
......@@ -649,7 +649,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
struct ip_vs_dest *dest = NULL;
struct ip_vs_lblcr_entry *en;
ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
......
......@@ -73,7 +73,7 @@ ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb)
const char *dptr;
int retc;
ip_vs_fill_iphdr(p->af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(p->af, skb, &iph);
/* Only useful with UDP */
if (iph.protocol != IPPROTO_UDP)
......
......@@ -18,7 +18,7 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
sctp_sctphdr_t *sh, _sctph;
struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
ip_vs_fill_iph_skb(af, skb, &iph);
sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph);
if (sh == NULL)
......@@ -72,12 +72,14 @@ sctp_snat_handler(struct sk_buff *skb,
struct sk_buff *iter;
__be32 crc32;
struct ip_vs_iphdr iph;
ip_vs_fill_iph_skb(cp->af, skb, &iph);
sctphoff = iph.len;
#ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6)
sctphoff = sizeof(struct ipv6hdr);
else
if (cp->af == AF_INET6 && iph.fragoffs)
return 1;
#endif
sctphoff = ip_hdrlen(skb);
/* csum_check requires unshared skb */
if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
......@@ -116,12 +118,14 @@ sctp_dnat_handler(struct sk_buff *skb,
struct sk_buff *iter;
__be32 crc32;
struct ip_vs_iphdr iph;
ip_vs_fill_iph_skb(cp->af, skb, &iph);
sctphoff = iph.len;
#ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6)
sctphoff = sizeof(struct ipv6hdr);
else
if (cp->af == AF_INET6 && iph.fragoffs)
return 1;
#endif
sctphoff = ip_hdrlen(skb);