Commit f019948d authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://1984.lsi.us.es/nf-next

Pablo Neira Ayuso says:

====================
The following changeset contains updates for IPVS from Jesper Dangaard
Brouer that did not reach the previous merge window in time.

More specifically, updates to improve IPv6 support in IPVS. More
relevantly, some of the existing code performed wrong handling of the
extensions headers and better fragmentation handling.

Jesper promised more follow-up patches to refine this after this batch
hits net-next. Yet to come.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents dc95a2c0 54d83efa
This diff is collapsed.
...@@ -28,12 +28,11 @@ if IP_VS ...@@ -28,12 +28,11 @@ 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.
See http://www.mindbasket.com/ipvs for more information. Say Y if unsure.
Say N if unsure.
config IP_VS_DEBUG config IP_VS_DEBUG
bool "IP virtual server debugging" bool "IP virtual server debugging"
......
...@@ -308,13 +308,12 @@ struct ip_vs_conn *ip_vs_conn_in_get(const struct ip_vs_conn_param *p) ...@@ -308,13 +308,12 @@ struct ip_vs_conn *ip_vs_conn_in_get(const struct ip_vs_conn_param *p)
static int static int
ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb, ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb,
const struct ip_vs_iphdr *iph, const struct ip_vs_iphdr *iph,
unsigned int proto_off, int inverse, int inverse, struct ip_vs_conn_param *p)
struct ip_vs_conn_param *p)
{ {
__be16 _ports[2], *pptr; __be16 _ports[2], *pptr;
struct net *net = skb_net(skb); struct net *net = skb_net(skb);
pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports); pptr = frag_safe_skb_hp(skb, iph->len, sizeof(_ports), _ports, iph);
if (pptr == NULL) if (pptr == NULL)
return 1; return 1;
...@@ -329,12 +328,11 @@ ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb, ...@@ -329,12 +328,11 @@ ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb,
struct ip_vs_conn * struct ip_vs_conn *
ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb, ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb,
const struct ip_vs_iphdr *iph, const struct ip_vs_iphdr *iph, int inverse)
unsigned int proto_off, int inverse)
{ {
struct ip_vs_conn_param p; struct ip_vs_conn_param p;
if (ip_vs_conn_fill_param_proto(af, skb, iph, proto_off, inverse, &p)) if (ip_vs_conn_fill_param_proto(af, skb, iph, inverse, &p))
return NULL; return NULL;
return ip_vs_conn_in_get(&p); return ip_vs_conn_in_get(&p);
...@@ -432,12 +430,11 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p) ...@@ -432,12 +430,11 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
struct ip_vs_conn * struct ip_vs_conn *
ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb, ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb,
const struct ip_vs_iphdr *iph, const struct ip_vs_iphdr *iph, int inverse)
unsigned int proto_off, int inverse)
{ {
struct ip_vs_conn_param p; struct ip_vs_conn_param p;
if (ip_vs_conn_fill_param_proto(af, skb, iph, proto_off, inverse, &p)) if (ip_vs_conn_fill_param_proto(af, skb, iph, inverse, &p))
return NULL; return NULL;
return ip_vs_conn_out_get(&p); return ip_vs_conn_out_get(&p);
......
This diff is collapsed.
...@@ -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__);
......
...@@ -68,23 +68,31 @@ static int get_callid(const char *dptr, unsigned int dataoff, ...@@ -68,23 +68,31 @@ static int get_callid(const char *dptr, unsigned int dataoff,
static int static int
ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb) ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb)
{ {
struct sk_buff *reasm = skb_nfct_reasm(skb);
struct ip_vs_iphdr iph; struct ip_vs_iphdr iph;
unsigned int dataoff, datalen, matchoff, matchlen; unsigned int dataoff, datalen, matchoff, matchlen;
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)
return -EINVAL; return -EINVAL;
/* todo: IPv6 fragments:
* I think this only should be done for the first fragment. /HS
*/
if (reasm) {
skb = reasm;
dataoff = iph.thoff_reasm + sizeof(struct udphdr);
} else
dataoff = iph.len + sizeof(struct udphdr);
/* No Data ? */
dataoff = iph.len + sizeof(struct udphdr);
if (dataoff >= skb->len) if (dataoff >= skb->len)
return -EINVAL; return -EINVAL;
/* todo: Check if this will mess-up the reasm skb !!! /HS */
if ((retc=skb_linearize(skb)) < 0) retc = skb_linearize(skb);
if (retc < 0)
return retc; return retc;
dptr = skb->data + dataoff; dptr = skb->data + dataoff;
datalen = skb->len - dataoff; datalen = skb->len - dataoff;
......
...@@ -280,17 +280,17 @@ ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp, ...@@ -280,17 +280,17 @@ ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp,
if (ih == NULL) if (ih == NULL)
sprintf(buf, "TRUNCATED"); sprintf(buf, "TRUNCATED");
else if (ih->nexthdr == IPPROTO_FRAGMENT) else if (ih->nexthdr == IPPROTO_FRAGMENT)
sprintf(buf, "%pI6->%pI6 frag", &ih->saddr, &ih->daddr); sprintf(buf, "%pI6c->%pI6c frag", &ih->saddr, &ih->daddr);
else { else {
__be16 _ports[2], *pptr; __be16 _ports[2], *pptr;
pptr = skb_header_pointer(skb, offset + sizeof(struct ipv6hdr), pptr = skb_header_pointer(skb, offset + sizeof(struct ipv6hdr),
sizeof(_ports), _ports); sizeof(_ports), _ports);
if (pptr == NULL) if (pptr == NULL)
sprintf(buf, "TRUNCATED %pI6->%pI6", sprintf(buf, "TRUNCATED %pI6c->%pI6c",
&ih->saddr, &ih->daddr); &ih->saddr, &ih->daddr);
else else
sprintf(buf, "%pI6:%u->%pI6:%u", sprintf(buf, "%pI6c:%u->%pI6c:%u",
&ih->saddr, ntohs(pptr[0]), &ih->saddr, ntohs(pptr[0]),
&ih->daddr, ntohs(pptr[1])); &ih->daddr, ntohs(pptr[1]));
} }
......
...@@ -57,7 +57,7 @@ ah_esp_conn_fill_param_proto(struct net *net, int af, ...@@ -57,7 +57,7 @@ ah_esp_conn_fill_param_proto(struct net *net, int af,
static struct ip_vs_conn * static struct ip_vs_conn *
ah_esp_conn_in_get(int af, const struct sk_buff *skb, ah_esp_conn_in_get(int af, const struct sk_buff *skb,
const struct ip_vs_iphdr *iph, unsigned int proto_off, const struct ip_vs_iphdr *iph,
int inverse) int inverse)
{ {
struct ip_vs_conn *cp; struct ip_vs_conn *cp;
...@@ -85,9 +85,7 @@ ah_esp_conn_in_get(int af, const struct sk_buff *skb, ...@@ -85,9 +85,7 @@ ah_esp_conn_in_get(int af, const struct sk_buff *skb,
static struct ip_vs_conn * static struct ip_vs_conn *
ah_esp_conn_out_get(int af, const struct sk_buff *skb, ah_esp_conn_out_get(int af, const struct sk_buff *skb,
const struct ip_vs_iphdr *iph, const struct ip_vs_iphdr *iph, int inverse)
unsigned int proto_off,
int inverse)
{ {
struct ip_vs_conn *cp; struct ip_vs_conn *cp;
struct ip_vs_conn_param p; struct ip_vs_conn_param p;
...@@ -110,7 +108,8 @@ ah_esp_conn_out_get(int af, const struct sk_buff *skb, ...@@ -110,7 +108,8 @@ ah_esp_conn_out_get(int af, const struct sk_buff *skb,
static int static int
ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
int *verdict, struct ip_vs_conn **cpp) int *verdict, struct ip_vs_conn **cpp,
struct ip_vs_iphdr *iph)
{ {
/* /*
* AH/ESP is only related traffic. Pass the packet to IP stack. * AH/ESP is only related traffic. Pass the packet to IP stack.
......
...@@ -10,28 +10,26 @@ ...@@ -10,28 +10,26 @@
static int static int
sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
int *verdict, struct ip_vs_conn **cpp) int *verdict, struct ip_vs_conn **cpp,
struct ip_vs_iphdr *iph)
{ {
struct net *net; struct net *net;
struct ip_vs_service *svc; struct ip_vs_service *svc;
sctp_chunkhdr_t _schunkh, *sch; sctp_chunkhdr_t _schunkh, *sch;
sctp_sctphdr_t *sh, _sctph; sctp_sctphdr_t *sh, _sctph;
struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(af, skb_network_header(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)
return 0; return 0;
sch = skb_header_pointer(skb, iph.len + sizeof(sctp_sctphdr_t), sch = skb_header_pointer(skb, iph->len + sizeof(sctp_sctphdr_t),
sizeof(_schunkh), &_schunkh); sizeof(_schunkh), &_schunkh);
if (sch == NULL) if (sch == NULL)
return 0; return 0;
net = skb_net(skb); net = skb_net(skb);
if ((sch->type == SCTP_CID_INIT) && if ((sch->type == SCTP_CID_INIT) &&
(svc = ip_vs_service_get(net, af, skb->mark, iph.protocol, (svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
&iph.daddr, sh->dest))) { &iph->daddr, sh->dest))) {
int ignored; int ignored;
if (ip_vs_todrop(net_ipvs(net))) { if (ip_vs_todrop(net_ipvs(net))) {
...@@ -47,10 +45,10 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, ...@@ -47,10 +45,10 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
* Let the virtual server select a real server for the * Let the virtual server select a real server for the
* incoming connection, and create a connection entry. * incoming connection, and create a connection entry.
*/ */
*cpp = ip_vs_schedule(svc, skb, pd, &ignored); *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph);
if (!*cpp && ignored <= 0) { if (!*cpp && ignored <= 0) {
if (!ignored) if (!ignored)
*verdict = ip_vs_leave(svc, skb, pd); *verdict = ip_vs_leave(svc, skb, pd, iph);
else { else {
ip_vs_service_put(svc); ip_vs_service_put(svc);
*verdict = NF_DROP; *verdict = NF_DROP;
...@@ -64,20 +62,18 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, ...@@ -64,20 +62,18 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
} }
static int static int
sctp_snat_handler(struct sk_buff *skb, sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp) struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{ {
sctp_sctphdr_t *sctph; sctp_sctphdr_t *sctph;
unsigned int sctphoff; unsigned int sctphoff = iph->len;
struct sk_buff *iter; struct sk_buff *iter;
__be32 crc32; __be32 crc32;
#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)))
...@@ -108,20 +104,18 @@ sctp_snat_handler(struct sk_buff *skb, ...@@ -108,20 +104,18 @@ sctp_snat_handler(struct sk_buff *skb,
} }
static int static int
sctp_dnat_handler(struct sk_buff *skb, sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp) struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{ {
sctp_sctphdr_t *sctph; sctp_sctphdr_t *sctph;
unsigned int sctphoff; unsigned int sctphoff = iph->len;
struct sk_buff *iter; struct sk_buff *iter;
__be32 crc32; __be32 crc32;
#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)))
......
...@@ -33,16 +33,14 @@ ...@@ -33,16 +33,14 @@
static int static int
tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
int *verdict, struct ip_vs_conn **cpp) int *verdict, struct ip_vs_conn **cpp,
struct ip_vs_iphdr *iph)
{ {
struct net *net; struct net *net;
struct ip_vs_service *svc; struct ip_vs_service *svc;
struct tcphdr _tcph, *th; struct tcphdr _tcph, *th;
struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(af, skb_network_header(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) {
*verdict = NF_DROP; *verdict = NF_DROP;
return 0; return 0;
...@@ -50,8 +48,8 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, ...@@ -50,8 +48,8 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
net = skb_net(skb); net = skb_net(skb);
/* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */ /* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */
if (th->syn && if (th->syn &&
(svc = ip_vs_service_get(net, af, skb->mark, iph.protocol, (svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
&iph.daddr, th->dest))) { &iph->daddr, th->dest))) {
int ignored; int ignored;
if (ip_vs_todrop(net_ipvs(net))) { if (ip_vs_todrop(net_ipvs(net))) {
...@@ -68,10 +66,10 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, ...@@ -68,10 +66,10 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
* Let the virtual server select a real server for the * Let the virtual server select a real server for the
* incoming connection, and create a connection entry. * incoming connection, and create a connection entry.
*/ */
*cpp = ip_vs_schedule(svc, skb, pd, &ignored); *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph);
if (!*cpp && ignored <= 0) { if (!*cpp && ignored <= 0) {
if (!ignored) if (!ignored)
*verdict = ip_vs_leave(svc, skb, pd); *verdict = ip_vs_leave(svc, skb, pd, iph);
else { else {
ip_vs_service_put(svc); ip_vs_service_put(svc);
*verdict = NF_DROP; *verdict = NF_DROP;
...@@ -128,20 +126,18 @@ tcp_partial_csum_update(int af, struct tcphdr *tcph, ...@@ -128,20 +126,18 @@ tcp_partial_csum_update(int af, struct tcphdr *tcph,
static int static int
tcp_snat_handler(struct sk_buff *skb, tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp) struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{ {
struct tcphdr *tcph; struct tcphdr *tcph;
unsigned int tcphoff; unsigned int tcphoff = iph->len;
int oldlen; int oldlen;
int payload_csum = 0; int payload_csum = 0;
#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 */
...@@ -208,20 +204,18 @@ tcp_snat_handler(struct sk_buff *skb, ...@@ -208,20 +204,18 @@ tcp_snat_handler(struct sk_buff *skb,
static int static int
tcp_dnat_handler(struct sk_buff *skb, tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp) struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{ {
struct tcphdr *tcph; struct tcphdr *tcph;
unsigned int tcphoff; unsigned int tcphoff = iph->len;
int oldlen; int oldlen;
int payload_csum = 0; int payload_csum = 0;
#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 */
......
...@@ -30,23 +30,22 @@ ...@@ -30,23 +30,22 @@
static int static int
udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
int *verdict, struct ip_vs_conn **cpp) int *verdict, struct ip_vs_conn **cpp,
struct ip_vs_iphdr *iph)
{ {
struct net *net; struct net *net;
struct ip_vs_service *svc; struct ip_vs_service *svc;
struct udphdr _udph, *uh; struct udphdr _udph, *uh;
struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph); /* IPv6 fragments, only first fragment will hit this */
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) {
*verdict = NF_DROP; *verdict = NF_DROP;
return 0; return 0;
} }
net = skb_net(skb); net = skb_net(skb);
svc = ip_vs_service_get(net, af, skb->mark, iph.protocol, svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
&iph.daddr, uh->dest); &iph->daddr, uh->dest);
if (svc) { if (svc) {
int ignored; int ignored;
...@@ -64,10 +63,10 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, ...@@ -64,10 +63,10 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
* Let the virtual server select a real server for the * Let the virtual server select a real server for the
* incoming connection, and create a connection entry. * incoming connection, and create a connection entry.
*/ */
*cpp = ip_vs_schedule(svc, skb, pd, &ignored); *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph);
if (!*cpp && ignored <= 0) { if (!*cpp && ignored <= 0) {
if (!ignored) if (!ignored)
*verdict = ip_vs_leave(svc, skb, pd); *verdict = ip_vs_leave(svc, skb, pd, iph);
else { else {
ip_vs_service_put(svc); ip_vs_service_put(svc);
*verdict = NF_DROP; *verdict = NF_DROP;
...@@ -125,20 +124,18 @@ udp_partial_csum_update(int af, struct udphdr *uhdr, ...@@ -125,20 +124,18 @@ udp_partial_csum_update(int af, struct udphdr *uhdr,
static int static int
udp_snat_handler(struct sk_buff *skb, udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp) struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{ {
struct udphdr *udph; struct udphdr *udph;
unsigned int udphoff; unsigned int udphoff = iph->len;
int oldlen; int oldlen;
int payload_csum = 0; int payload_csum = 0;
#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 */
...@@ -210,20 +207,18 @@ udp_snat_handler(struct sk_buff *skb, ...@@ -210,20 +207,18 @@ udp_snat_handler(struct sk_buff *skb,
static int static int
udp_dnat_handler(struct sk_buff *skb, udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp) struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{ {
struct udphdr *udph; struct udphdr *udph;
unsigned int udphoff; unsigned int udphoff = iph->len;
int oldlen; int oldlen;
int payload_csum = 0; int payload_csum = 0;
#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 */
......
...@@ -159,7 +159,7 @@ void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg) ...@@ -159,7 +159,7 @@ void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg)
svc->fwmark, msg); svc->fwmark, msg);
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
} else if (svc->af == AF_INET6) { } else if (svc->af == AF_INET6) {
IP_VS_ERR_RL("%s: %s [%pI6]:%d - %s\n", IP_VS_ERR_RL("%s: %s [%pI6c]:%d - %s\n",
svc->scheduler->name, svc->scheduler->name,
ip_vs_proto_name(svc->protocol), ip_vs_proto_name(svc->protocol),
&svc->addr.in6, ntohs(svc->port), msg); &svc->addr.in6, ntohs(svc->port), msg);
......
...@@ -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");
......
...@@ -338,7 +338,7 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest, ...@@ -338,7 +338,7 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest,
local = __ip_vs_is_local_route6(rt); local = __ip_vs_is_local_route6(rt);
if (!((local ? IP_VS_RT_MODE_LOCAL : IP_VS_RT_MODE_NON_LOCAL) & if (!((local ? IP_VS_RT_MODE_LOCAL : IP_VS_RT_MODE_NON_LOCAL) &
rt_mode)) { rt_mode)) {
IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI6\n", IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI6c\n",
local ? "local":"non-local", daddr); local ? "local":"non-local", daddr);
dst_release(&rt->dst); dst_release(&rt->dst);
return NULL; return NULL;
...@@ -346,8 +346,8 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest, ...@@ -346,8 +346,8 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest,
if (local && !(rt_mode & IP_VS_RT_MODE_RDR) && if (local && !(rt_mode & IP_VS_RT_MODE_RDR) &&
!((ort = (struct rt6_info *) skb_dst(skb)) && !((ort = (struct rt6_info *) skb_dst(skb)) &&
__ip_vs_is_local_route6(ort))) { __ip_vs_is_local_route6(ort))) {
IP_VS_DBG_RL("Redirect from non-local address %pI6 to local " IP_VS_DBG_RL("Redirect from non-local address %pI6c to local "
"requires NAT method, dest: %pI6\n", "requires NAT method, dest: %pI6c\n",
&ipv6_hdr(skb)->daddr, daddr); &ipv6_hdr(skb)->daddr, daddr);
dst_release(&rt->dst); dst_release(&rt->dst);
return NULL; return NULL;
...@@ -355,8 +355,8 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest, ...@@ -355,8 +355,8 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest,
if (unlikely(!local && (!skb->dev || skb->dev->flags & IFF_LOOPBACK) && if (unlikely(!local && (!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
ipv6_addr_type(&ipv6_hdr(skb)->saddr) & ipv6_addr_type(&ipv6_hdr(skb)->saddr) &
IPV6_ADDR_LOOPBACK)) { IPV6_ADDR_LOOPBACK)) {
IP_VS_DBG_RL("Stopping traffic from loopback address %pI6 " IP_VS_DBG_RL("Stopping traffic from loopback address %pI6c "
"to non-local address, dest: %pI6\n", "to non-local address, dest: %pI6c\n",
&ipv6_hdr(skb)->saddr, daddr); &ipv6_hdr(skb)->saddr, daddr);
dst_release(&rt->dst); dst_release(&rt->dst);
return NULL; return NULL;
...@@ -427,7 +427,7 @@ do { \ ...@@ -427,7 +427,7 @@ do { \
*/ */
int int
ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{ {
/* we do not touch skb and do not need pskb ptr */ /* we do not touch skb and do not need pskb ptr */
IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 1); IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 1);
...@@ -441,7 +441,7 @@ ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -441,7 +441,7 @@ ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
*/ */
int int
ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{ {
struct rtable *rt; /* Route to the other host */ struct rtable *rt; /* Route to the other host */
struct iphdr *iph = ip_hdr(skb); struct iphdr *iph = ip_hdr(skb);
...@@ -496,16 +496,16 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -496,16 +496,16 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
int int
ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
{ {
struct rt6_info *rt; /* Route to the other host */ struct rt6_info *rt; /* Route to the other host */
struct ipv6hdr *iph = ipv6_hdr(skb);
int mtu; int mtu;
EnterFunction(10); EnterFunction(10);
if (!(rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph->daddr, NULL, 0, rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph->daddr.in6, NULL, 0,
IP_VS_RT_MODE_NON_LOCAL))) IP_VS_RT_MODE_NON_LOCAL);
if (!rt)
goto tx_error_icmp; goto tx_error_icmp;
/* MTU checking */ /* MTU checking */
...@@ -516,7 +516,9 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -516,7 +516,9 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev; skb->dev = net->loopback_dev;
} }
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); /* only send ICMP too big on first fragment */
if (!iph->fragoffs)
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
dst_release(&rt->dst); dst_release(&rt->dst);
IP_VS_DBG_RL("%s(): frag needed\n", __func__); IP_VS_DBG_RL("%s(): frag needed\n", __func__);
goto tx_error; goto tx_error;
...@@ -559,7 +561,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -559,7 +561,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/ */
int int
ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{ {
struct rtable *rt; /* Route to the other host */ struct rtable *rt; /* Route to the other host */
int mtu; int mtu;
...@@ -629,7 +631,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -629,7 +631,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
goto tx_error_put; goto tx_error_put;
/* mangle the packet */ /* mangle the packet */
if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp)) if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, ipvsh))
goto tx_error_put; goto tx_error_put;
ip_hdr(skb)->daddr = cp->daddr.ip; ip_hdr(skb)->daddr = cp->daddr.ip;
ip_send_check(ip_hdr(skb)); ip_send_check(ip_hdr(skb));
...@@ -677,7 +679,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -677,7 +679,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
int int
ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
{ {
struct rt6_info *rt; /* Route to the other host */ struct rt6_info *rt; /* Route to the other host */
int mtu; int mtu;
...@@ -686,10 +688,9 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -686,10 +688,9 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
EnterFunction(10); EnterFunction(10);
/* 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 && !iph->fragoffs)) {
__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);
...@@ -737,7 +738,9 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -737,7 +738,9 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev; skb->dev = net->loopback_dev;
} }
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); /* only send ICMP too big on first fragment */
if (!iph->fragoffs)
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
IP_VS_DBG_RL_PKT(0, AF_INET6, pp, skb, 0, IP_VS_DBG_RL_PKT(0, AF_INET6, pp, skb, 0,
"ip_vs_nat_xmit_v6(): frag needed for"); "ip_vs_nat_xmit_v6(): frag needed for");
goto tx_error_put; goto tx_error_put;
...@@ -751,7 +754,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -751,7 +754,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
goto tx_error_put; goto tx_error_put;
/* mangle the packet */ /* mangle the packet */
if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp)) if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, iph))
goto tx_error; goto tx_error;
ipv6_hdr(skb)->daddr = cp->daddr.in6; ipv6_hdr(skb)->daddr = cp->daddr.in6;
...@@ -812,7 +815,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -812,7 +815,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/ */
int int
ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{ {
struct netns_ipvs *ipvs = net_ipvs(skb_net(skb)); struct netns_ipvs *ipvs = net_ipvs(skb_net(skb));
struct rtable *rt; /* Route to the other host */ struct rtable *rt; /* Route to the other host */
...@@ -932,7 +935,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -932,7 +935,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
int int
ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{ {
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 */
...@@ -972,7 +975,9 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -972,7 +975,9 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev; skb->dev = net->loopback_dev;
} }
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); /* only send ICMP too big on first fragment */
if (!ipvsh->fragoffs)
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
IP_VS_DBG_RL("%s(): frag needed\n", __func__); IP_VS_DBG_RL("%s(): frag needed\n", __func__);
goto tx_error_put; goto tx_error_put;
} }
...@@ -1053,7 +1058,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1053,7 +1058,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/ */
int int
ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{ {
struct rtable *rt; /* Route to the other host */ struct rtable *rt; /* Route to the other host */
struct iphdr *iph = ip_hdr(skb); struct iphdr *iph = ip_hdr(skb);
...@@ -1115,7 +1120,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1115,7 +1120,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
int int
ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp) struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
{ {
struct rt6_info *rt; /* Route to the other host */ struct rt6_info *rt; /* Route to the other host */
int mtu; int mtu;
...@@ -1139,7 +1144,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1139,7 +1144,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev; skb->dev = net->loopback_dev;
} }
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); /* only send ICMP too big on first fragment */
if (!iph->fragoffs)
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
dst_release(&rt->dst); dst_release(&rt->dst);
IP_VS_DBG_RL("%s(): frag needed\n", __func__); IP_VS_DBG_RL("%s(): frag needed\n", __func__);
goto tx_error; goto tx_error;
...@@ -1183,7 +1190,8 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1183,7 +1190,8 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/ */
int int
ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, int offset, unsigned int hooknum) struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
struct ip_vs_iphdr *iph)
{ {
struct rtable *rt; /* Route to the other host */ struct rtable *rt; /* Route to the other host */
int mtu; int mtu;
...@@ -1198,7 +1206,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1198,7 +1206,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
translate address/port back */ translate address/port back */
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
if (cp->packet_xmit) if (cp->packet_xmit)
rc = cp->packet_xmit(skb, cp, pp); rc = cp->packet_xmit(skb, cp, pp, iph);
else else
rc = NF_ACCEPT; rc = NF_ACCEPT;
/* do not touch skb anymore */ /* do not touch skb anymore */
...@@ -1304,7 +1312,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1304,7 +1312,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
int int
ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, int offset, unsigned int hooknum) struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
struct ip_vs_iphdr *iph)
{ {
struct rt6_info *rt; /* Route to the other host */ struct rt6_info *rt; /* Route to the other host */
int mtu; int mtu;
...@@ -1319,7 +1328,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1319,7 +1328,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
translate address/port back */ translate address/port back */
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) { if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
if (cp->packet_xmit) if (cp->packet_xmit)
rc = cp->packet_xmit(skb, cp, pp); rc = cp->packet_xmit(skb, cp, pp, iph);
else else
rc = NF_ACCEPT; rc = NF_ACCEPT;
/* do not touch skb anymore */ /* do not touch skb anymore */
...@@ -1375,7 +1384,9 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ...@@ -1375,7 +1384,9 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev; skb->dev = net->loopback_dev;
} }
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); /* only send ICMP too big on first fragment */
if (!iph->fragoffs)
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
IP_VS_DBG_RL("%s(): frag needed\n", __func__); IP_VS_DBG_RL("%s(): frag needed\n", __func__);
goto tx_error_put; goto tx_error_put;
} }
......
...@@ -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) ^
...@@ -85,7 +85,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par) ...@@ -85,7 +85,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
/* /*
* Check if the packet belongs to an existing entry * Check if the packet belongs to an existing entry
*/ */
cp = pp->conn_out_get(family, skb, &iph, iph.len, 1 /* inverse */); cp = pp->conn_out_get(family, skb, &iph, 1 /* inverse */);
if (unlikely(cp == NULL)) { if (unlikely(cp == NULL)) {
match = false; match = false;
goto out; goto out;
......
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