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

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 @@ ...@@ -22,6 +22,9 @@
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h> /* for struct ipv6hdr */ #include <linux/ipv6.h> /* for struct ipv6hdr */
#include <net/ipv6.h> #include <net/ipv6.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <linux/netfilter_ipv6/ip6_tables.h>
#endif
#if IS_ENABLED(CONFIG_NF_CONNTRACK) #if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#endif #endif
...@@ -103,30 +106,79 @@ static inline struct net *seq_file_single_net(struct seq_file *seq) ...@@ -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 */ /* Connections' size value needed by ip_vs_ctl.c */
extern int ip_vs_conn_tab_size; extern int ip_vs_conn_tab_size;
struct ip_vs_iphdr { struct ip_vs_iphdr {
int len; __u32 len; /* IPv4 simply where L4 starts
__u8 protocol; 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 saddr;
union nf_inet_addr daddr; union nf_inet_addr daddr;
}; };
static inline void 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 #ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) { if (af == AF_INET6) {
const struct ipv6hdr *iph = nh; const struct ipv6hdr *iph =
iphdr->len = sizeof(struct ipv6hdr); (struct ipv6hdr *)skb_network_header(skb);
iphdr->protocol = iph->nexthdr;
iphdr->saddr.in6 = iph->saddr; iphdr->saddr.in6 = iph->saddr;
iphdr->daddr.in6 = iph->daddr; 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 } else
#endif #endif
{ {
const struct iphdr *iph = nh; const struct iphdr *iph =
iphdr->len = iph->ihl * 4; (struct iphdr *)skb_network_header(skb);
iphdr->protocol = iph->protocol; 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->saddr.ip = iph->saddr;
iphdr->daddr.ip = iph->daddr; iphdr->daddr.ip = iph->daddr;
} }
......
...@@ -28,6 +28,7 @@ if IP_VS ...@@ -28,6 +28,7 @@ if IP_VS
config IP_VS_IPV6 config IP_VS_IPV6
bool "IPv6 support for IPVS" bool "IPv6 support for IPVS"
depends on IPV6 = y || IP_VS = IPV6 depends on IPV6 = y || IP_VS = IPV6
select IP6_NF_IPTABLES
---help--- ---help---
Add IPv6 support to IPVS. This is incomplete and might be dangerous. 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, ...@@ -236,7 +236,7 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
union nf_inet_addr snet; /* source network of the client, union nf_inet_addr snet; /* source network of the client,
after masking */ 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 */ /* Mask saddr with the netmask to adjust template granularity */
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
...@@ -402,7 +402,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, ...@@ -402,7 +402,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
unsigned int flags; unsigned int flags;
*ignored = 1; *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); pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports);
if (pptr == NULL) if (pptr == NULL)
return NULL; return NULL;
...@@ -506,7 +506,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, ...@@ -506,7 +506,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
int unicast; int unicast;
#endif #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); pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports);
if (pptr == NULL) { if (pptr == NULL) {
...@@ -732,10 +732,19 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, ...@@ -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 ip_vs_conn *cp, int inout)
{ {
struct ipv6hdr *iph = ipv6_hdr(skb); struct ipv6hdr *iph = ipv6_hdr(skb);
unsigned int icmp_offset = sizeof(struct ipv6hdr); unsigned int icmp_offset = 0;
struct icmp6hdr *icmph = (struct icmp6hdr *)(skb_network_header(skb) + unsigned int offs = 0; /* header offset*/
icmp_offset); int protocol;
struct ipv6hdr *ciph = (struct ipv6hdr *)(icmph + 1); 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) { if (inout) {
iph->saddr = cp->vaddr.in6; iph->saddr = cp->vaddr.in6;
...@@ -746,10 +755,13 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, ...@@ -746,10 +755,13 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
} }
/* the TCP/UDP/SCTP port */ /* the TCP/UDP/SCTP port */
if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr || if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
IPPROTO_SCTP == ciph->nexthdr) { IPPROTO_SCTP == protocol)) {
__be16 *ports = (void *)ciph + sizeof(struct ipv6hdr); __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) if (inout)
ports[1] = cp->vport; ports[1] = cp->vport;
else else
...@@ -898,9 +910,8 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related, ...@@ -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, IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
"Checking outgoing ICMP for"); "Checking outgoing ICMP for");
offset += cih->ihl * 4; ip_vs_fill_ip4hdr(cih, &ciph);
ciph.len += offset;
ip_vs_fill_iphdr(AF_INET, cih, &ciph);
/* The embedded headers contain source and dest in reverse order */ /* The embedded headers contain source and dest in reverse order */
cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1); cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1);
if (!cp) if (!cp)
...@@ -908,41 +919,31 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related, ...@@ -908,41 +919,31 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
snet.ip = iph->saddr; snet.ip = iph->saddr;
return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp, return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
pp, offset, ihl); pp, ciph.len, ihl);
} }
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
unsigned int hooknum) unsigned int hooknum)
{ {
struct ipv6hdr *iph;
struct icmp6hdr _icmph, *ic; struct icmp6hdr _icmph, *ic;
struct ipv6hdr _ciph, *cih; /* The ip header contained struct ipv6hdr _ip6h, *ip6h; /* The ip header contained within ICMP */
within the ICMP */ struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */
struct ip_vs_iphdr ciph;
struct ip_vs_conn *cp; struct ip_vs_conn *cp;
struct ip_vs_protocol *pp; struct ip_vs_protocol *pp;
unsigned int offset;
union nf_inet_addr snet; 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 */ *related = 1;
if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) {
if (ip_vs_gather_frags_v6(skb, ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
}
iph = ipv6_hdr(skb); ic = skb_header_pointer(skb, ipvsh->len, sizeof(_icmph), &_icmph);
offset = sizeof(struct ipv6hdr);
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
if (ic == NULL) if (ic == NULL)
return NF_DROP; 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. * Work through seeing if this is for us.
* These checks are supposed to be in an order that means easy * 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, ...@@ -955,35 +956,35 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
return NF_ACCEPT; 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 */ /* Now find the contained IP header */
offset += sizeof(_icmph); ciph.len = ipvsh->len + sizeof(_icmph);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
if (cih == NULL) if (ip6h == NULL)
return NF_ACCEPT; /* The packet looks wrong, ignore */ return NF_ACCEPT; /* The packet looks wrong, ignore */
ciph.saddr.in6 = ip6h->saddr; /* conn_out_get() handles reverse order */
pp = ip_vs_proto_get(cih->nexthdr); 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) if (!pp)
return NF_ACCEPT; 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 */ /* 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) if (!cp)
return NF_ACCEPT; return NF_ACCEPT;
snet.in6 = iph->saddr; snet.in6 = ciph.saddr.in6;
return handle_response_icmp(AF_INET6, skb, &snet, cih->nexthdr, cp, writable = ciph.len;
pp, offset, sizeof(struct ipv6hdr)); return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
pp, writable, sizeof(struct ipv6hdr));
} }
#endif #endif
...@@ -1113,7 +1114,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) ...@@ -1113,7 +1114,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
if (!net_ipvs(net)->enable) if (!net_ipvs(net)->enable)
return NF_ACCEPT; 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 #ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) { if (af == AF_INET6) {
if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { if (unlikely(iph.protocol == IPPROTO_ICMPV6)) {
...@@ -1123,7 +1124,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) ...@@ -1123,7 +1124,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
if (related) if (related)
return verdict; return verdict;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); ip_vs_fill_iph_skb(af, skb, &iph);
} }
} else } else
#endif #endif
...@@ -1133,7 +1134,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) ...@@ -1133,7 +1134,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
if (related) if (related)
return verdict; 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); 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) ...@@ -1143,22 +1144,14 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
/* reassemble IP fragments */ /* reassemble IP fragments */
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) { if (af == AF_INET)
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
#endif #endif
if (unlikely(ip_is_fragment(ip_hdr(skb)) && !pp->dont_defrag)) { if (unlikely(ip_is_fragment(ip_hdr(skb)) && !pp->dont_defrag)) {
if (ip_vs_gather_frags(skb, if (ip_vs_gather_frags(skb,
ip_vs_defrag_user(hooknum))) ip_vs_defrag_user(hooknum)))
return NF_STOLEN; 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) ...@@ -1373,9 +1366,9 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
"Checking incoming ICMP for"); "Checking incoming ICMP for");
offset2 = offset; offset2 = offset;
offset += cih->ihl * 4; ip_vs_fill_ip4hdr(cih, &ciph);
ciph.len += offset;
ip_vs_fill_iphdr(AF_INET, cih, &ciph); offset = ciph.len;
/* The embedded headers contain source and dest in reverse order. /* The embedded headers contain source and dest in reverse order.
* For IPIP this is error for request, not for reply. * For IPIP this is error for request, not for reply.
*/ */
...@@ -1461,34 +1454,24 @@ static int ...@@ -1461,34 +1454,24 @@ static int
ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
{ {
struct net *net = NULL; struct net *net = NULL;
struct ipv6hdr *iph; struct ipv6hdr _ip6h, *ip6h;
struct icmp6hdr _icmph, *ic; struct icmp6hdr _icmph, *ic;
struct ipv6hdr _ciph, *cih; /* The ip header contained struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */
within the ICMP */
struct ip_vs_iphdr ciph;
struct ip_vs_conn *cp; struct ip_vs_conn *cp;
struct ip_vs_protocol *pp; struct ip_vs_protocol *pp;
struct ip_vs_proto_data *pd; 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 */ *related = 1;
if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) {
if (ip_vs_gather_frags_v6(skb, ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
}
iph = ipv6_hdr(skb); ic = skb_header_pointer(skb, iph->len, sizeof(_icmph), &_icmph);
offset = sizeof(struct ipv6hdr);
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
if (ic == NULL) if (ic == NULL)
return NF_DROP; 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. * Work through seeing if this is for us.
* These checks are supposed to be in an order that means easy * 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) ...@@ -1501,40 +1484,51 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
return NF_ACCEPT; 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 */ /* Now find the contained IP header */
offset += sizeof(_icmph); ciph.len = iph->len + sizeof(_icmph);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); offs_ciph = ciph.len; /* Save ip header offset */
if (cih == NULL) ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
if (ip6h == NULL)
return NF_ACCEPT; /* The packet looks wrong, ignore */ 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); net = skb_net(skb);
pd = ip_vs_proto_data_get(net, cih->nexthdr); pd = ip_vs_proto_data_get(net, ciph.protocol);
if (!pd) if (!pd)
return NF_ACCEPT; return NF_ACCEPT;
pp = pd->pp; pp = pd->pp;
/* Is the embedded protocol header present? */ /* Cannot handle fragmented embedded protocol */
/* TODO: we don't support fragmentation at the moment anyways */ if (ciph.fragoffs)
if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag))
return NF_ACCEPT; 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"); "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 */ /* 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) if (!cp)
return NF_ACCEPT; return NF_ACCEPT;
/* do the statistics and put it back */ /* do the statistics and put it back */
ip_vs_in_stats(cp, skb); ip_vs_in_stats(cp, skb);
if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr ||
IPPROTO_SCTP == cih->nexthdr) /* Need to mangle contained IPv6 header in ICMPv6 packet */
offset += 2 * sizeof(__u16); writable = ciph.len;
verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum); 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); __ip_vs_conn_put(cp);
...@@ -1570,7 +1564,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) ...@@ -1570,7 +1564,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (unlikely((skb->pkt_type != PACKET_HOST && if (unlikely((skb->pkt_type != PACKET_HOST &&
hooknum != NF_INET_LOCAL_OUT) || hooknum != NF_INET_LOCAL_OUT) ||
!skb_dst(skb))) { !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" IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s"
" ignored in hook %u\n", " ignored in hook %u\n",
skb->pkt_type, iph.protocol, skb->pkt_type, iph.protocol,
...@@ -1582,7 +1576,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) ...@@ -1582,7 +1576,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (!net_ipvs(net)->enable) if (!net_ipvs(net)->enable)
return NF_ACCEPT; 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 */ /* Bad... Do not break raw sockets */
if (unlikely(skb->sk != NULL && hooknum == NF_INET_LOCAL_OUT && 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) ...@@ -1602,7 +1596,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (related) if (related)
return verdict; return verdict;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
} }
} else } else
#endif #endif
...@@ -1612,7 +1605,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) ...@@ -1612,7 +1605,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (related) if (related)
return verdict; return verdict;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
} }
/* Protocol supported? */ /* Protocol supported? */
...@@ -1622,10 +1614,11 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) ...@@ -1622,10 +1614,11 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
pp = pd->pp; pp = pd->pp;
/* /*
* Check if the packet belongs to an existing connection entry * 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); cp = pp->conn_in_get(af, skb, &iph, iph.len, 0);
if (unlikely(!cp)) { if (unlikely(!cp) && !iph.fragoffs) {
int v; int v;
if (!pp->conn_schedule(af, skb, pd, &v, &cp)) 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, ...@@ -1789,8 +1782,10 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb,
{ {
int r; int r;
struct net *net; 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; return NF_ACCEPT;
/* ipvs enabled in this netns ? */ /* ipvs enabled in this netns ? */
......
...@@ -215,7 +215,7 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) ...@@ -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_dh_bucket *tbl;
struct ip_vs_iphdr iph; 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__); 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) ...@@ -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_dest *dest = NULL;
struct ip_vs_lblc_entry *en; 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__); 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) ...@@ -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_dest *dest = NULL;
struct ip_vs_lblcr_entry *en; 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__); 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) ...@@ -73,7 +73,7 @@ ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb)
const char *dptr; const char *dptr;
int retc; 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 */ /* Only useful with UDP */
if (iph.protocol != IPPROTO_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, ...@@ -18,7 +18,7 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
sctp_sctphdr_t *sh, _sctph; sctp_sctphdr_t *sh, _sctph;
struct ip_vs_iphdr iph; 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); sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph);
if (sh == NULL) if (sh == NULL)
...@@ -72,12 +72,14 @@ sctp_snat_handler(struct sk_buff *skb, ...@@ -72,12 +72,14 @@ sctp_snat_handler(struct sk_buff *skb,
struct sk_buff *iter; struct sk_buff *iter;
__be32 crc32; __be32 crc32;
struct ip_vs_iphdr iph;
ip_vs_fill_iph_skb(cp->af, skb, &iph);
sctphoff = iph.len;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6 && iph.fragoffs)
sctphoff = sizeof(struct ipv6hdr); return 1;
else
#endif #endif
sctphoff = ip_hdrlen(skb);
/* csum_check requires unshared skb */ /* csum_check requires unshared skb */
if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
...@@ -116,12 +118,14 @@ sctp_dnat_handler(struct sk_buff *skb, ...@@ -116,12 +118,14 @@ sctp_dnat_handler(struct sk_buff *skb,
struct sk_buff *iter; struct sk_buff *iter;
__be32 crc32; __be32 crc32;
struct ip_vs_iphdr iph;
ip_vs_fill_iph_skb(cp->af, skb, &iph);
sctphoff = iph.len;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6 && iph.fragoffs)
sctphoff = sizeof(struct ipv6hdr); return 1;
else
#endif #endif
sctphoff = ip_hdrlen(skb);
/* csum_check requires unshared skb */ /* csum_check requires unshared skb */
if (!skb_make_writable(skb, sctphoff + sizeof(*sctph))) if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
......
...@@ -40,7 +40,7 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, ...@@ -40,7 +40,7 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
struct tcphdr _tcph, *th; struct tcphdr _tcph, *th;
struct ip_vs_iphdr iph; struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); ip_vs_fill_iph_skb(af, skb, &iph);
th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph); th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph);
if (th == NULL) { if (th == NULL) {
...@@ -136,12 +136,14 @@ tcp_snat_handler(struct sk_buff *skb, ...@@ -136,12 +136,14 @@ tcp_snat_handler(struct sk_buff *skb,
int oldlen; int oldlen;
int payload_csum = 0; int payload_csum = 0;
struct ip_vs_iphdr iph;
ip_vs_fill_iph_skb(cp->af, skb, &iph);
tcphoff = iph.len;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6 && iph.fragoffs)
tcphoff = sizeof(struct ipv6hdr); return 1;
else
#endif #endif
tcphoff = ip_hdrlen(skb);
oldlen = skb->len - tcphoff; oldlen = skb->len - tcphoff;
/* csum_check requires unshared skb */ /* csum_check requires unshared skb */
...@@ -216,12 +218,14 @@ tcp_dnat_handler(struct sk_buff *skb, ...@@ -216,12 +218,14 @@ tcp_dnat_handler(struct sk_buff *skb,
int oldlen; int oldlen;
int payload_csum = 0; int payload_csum = 0;
struct ip_vs_iphdr iph;
ip_vs_fill_iph_skb(cp->af, skb, &iph);
tcphoff = iph.len;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6 && iph.fragoffs)
tcphoff = sizeof(struct ipv6hdr); return 1;
else
#endif #endif
tcphoff = ip_hdrlen(skb);
oldlen = skb->len - tcphoff; oldlen = skb->len - tcphoff;
/* csum_check requires unshared skb */ /* csum_check requires unshared skb */
......
...@@ -37,7 +37,7 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, ...@@ -37,7 +37,7 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
struct udphdr _udph, *uh; struct udphdr _udph, *uh;
struct ip_vs_iphdr iph; struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); ip_vs_fill_iph_skb(af, skb, &iph);
uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph); uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph);
if (uh == NULL) { if (uh == NULL) {
...@@ -133,12 +133,14 @@ udp_snat_handler(struct sk_buff *skb, ...@@ -133,12 +133,14 @@ udp_snat_handler(struct sk_buff *skb,
int oldlen; int oldlen;
int payload_csum = 0; int payload_csum = 0;
struct ip_vs_iphdr iph;
ip_vs_fill_iph_skb(cp->af, skb, &iph);
udphoff = iph.len;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6 && iph.fragoffs)
udphoff = sizeof(struct ipv6hdr); return 1;
else
#endif #endif
udphoff = ip_hdrlen(skb);
oldlen = skb->len - udphoff; oldlen = skb->len - udphoff;
/* csum_check requires unshared skb */ /* csum_check requires unshared skb */
...@@ -218,12 +220,14 @@ udp_dnat_handler(struct sk_buff *skb, ...@@ -218,12 +220,14 @@ udp_dnat_handler(struct sk_buff *skb,
int oldlen; int oldlen;
int payload_csum = 0; int payload_csum = 0;
struct ip_vs_iphdr iph;
ip_vs_fill_iph_skb(cp->af, skb, &iph);
udphoff = iph.len;
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (cp->af == AF_INET6) if (cp->af == AF_INET6 && iph.fragoffs)
udphoff = sizeof(struct ipv6hdr); return 1;
else
#endif #endif
udphoff = ip_hdrlen(skb);
oldlen = skb->len - udphoff; oldlen = skb->len - udphoff;
/* csum_check requires unshared skb */ /* csum_check requires unshared skb */
......
...@@ -228,7 +228,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) ...@@ -228,7 +228,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
struct ip_vs_sh_bucket *tbl; struct ip_vs_sh_bucket *tbl;
struct ip_vs_iphdr iph; 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, "ip_vs_sh_schedule(): Scheduling...\n"); IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n");
......
...@@ -679,14 +679,15 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -679,14 +679,15 @@ ip_vs_nat_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 */
int mtu; int mtu;
int local; int local;
struct ip_vs_iphdr iph;
EnterFunction(10); EnterFunction(10);
ip_vs_fill_iph_skb(cp->af, skb, &iph);
/* check if it is a connection of no-client-port */ /* check if it is a connection of no-client-port */
if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) { if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) {
__be16 _pt, *p; __be16 _pt, *p;
p = skb_header_pointer(skb, sizeof(struct ipv6hdr), p = skb_header_pointer(skb, iph.len, sizeof(_pt), &_pt);
sizeof(_pt), &_pt);
if (p == NULL) if (p == NULL)
goto tx_error; goto tx_error;
ip_vs_conn_fill_cport(cp, *p); ip_vs_conn_fill_cport(cp, *p);
......
...@@ -67,7 +67,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par) ...@@ -67,7 +67,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
goto out; goto out;
} }
ip_vs_fill_iphdr(family, skb_network_header(skb), &iph); ip_vs_fill_iph_skb(family, skb, &iph);
if (data->bitmask & XT_IPVS_PROTO) if (data->bitmask & XT_IPVS_PROTO)
if ((iph.protocol == data->l4proto) ^ if ((iph.protocol == data->l4proto) ^
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment