|
@@ -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 ? */
|