IPVS: Add 'af' args to protocol handler functions

Add 'af' arguments to conn_schedule(), conn_in_get(), conn_out_get() and
csum_check() function pointers in struct ip_vs_protocol. Extend the
respective functions for TCP, UDP, AH and ESP and adjust the callers.

The changes in the callers need to be somewhat extensive, since they now
need to pass a filled out struct ip_vs_iphdr * to the modified functions
instead of a struct iphdr *.

Signed-off-by: Julius Volz <juliusv@google.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
This commit is contained in:
Julius Volz 2008-09-02 15:55:40 +02:00 committed by Simon Horman
parent b14198f6c1
commit 51ef348b14
5 changed files with 171 additions and 124 deletions

View File

@ -296,21 +296,23 @@ struct ip_vs_protocol {
void (*exit)(struct ip_vs_protocol *pp);
int (*conn_schedule)(struct sk_buff *skb,
int (*conn_schedule)(int af, struct sk_buff *skb,
struct ip_vs_protocol *pp,
int *verdict, struct ip_vs_conn **cpp);
struct ip_vs_conn *
(*conn_in_get)(const struct sk_buff *skb,
(*conn_in_get)(int af,
const struct sk_buff *skb,
struct ip_vs_protocol *pp,
const struct iphdr *iph,
const struct ip_vs_iphdr *iph,
unsigned int proto_off,
int inverse);
struct ip_vs_conn *
(*conn_out_get)(const struct sk_buff *skb,
(*conn_out_get)(int af,
const struct sk_buff *skb,
struct ip_vs_protocol *pp,
const struct iphdr *iph,
const struct ip_vs_iphdr *iph,
unsigned int proto_off,
int inverse);
@ -320,7 +322,8 @@ struct ip_vs_protocol {
int (*dnat_handler)(struct sk_buff *skb,
struct ip_vs_protocol *pp, struct ip_vs_conn *cp);
int (*csum_check)(struct sk_buff *skb, struct ip_vs_protocol *pp);
int (*csum_check)(int af, struct sk_buff *skb,
struct ip_vs_protocol *pp);
const char *(*state_name)(int state);

View File

@ -572,6 +572,7 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related)
struct iphdr *iph;
struct icmphdr _icmph, *ic;
struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */
struct ip_vs_iphdr ciph;
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
unsigned int offset, ihl, verdict;
@ -627,8 +628,9 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related)
offset += cih->ihl * 4;
ip_vs_fill_iphdr(AF_INET, cih, &ciph);
/* The embedded headers contain source and dest in reverse order */
cp = pp->conn_out_get(skb, pp, cih, offset, 1);
cp = pp->conn_out_get(AF_INET, skb, pp, &ciph, offset, 1);
if (!cp)
return NF_ACCEPT;
@ -686,43 +688,41 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct ip_vs_iphdr iph;
struct ip_vs_protocol *pp;
struct ip_vs_conn *cp;
int ihl;
EnterFunction(11);
if (skb->ipvs_property)
return NF_ACCEPT;
iph = ip_hdr(skb);
if (unlikely(iph->protocol == IPPROTO_ICMP)) {
ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
if (unlikely(iph.protocol == IPPROTO_ICMP)) {
int related, verdict = ip_vs_out_icmp(skb, &related);
if (related)
return verdict;
iph = ip_hdr(skb);
ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
}
pp = ip_vs_proto_get(iph->protocol);
pp = ip_vs_proto_get(iph.protocol);
if (unlikely(!pp))
return NF_ACCEPT;
/* reassemble IP fragments */
if (unlikely(iph->frag_off & htons(IP_MF|IP_OFFSET) &&
if (unlikely(ip_hdr(skb)->frag_off & htons(IP_MF|IP_OFFSET) &&
!pp->dont_defrag)) {
if (ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT))
return NF_STOLEN;
iph = ip_hdr(skb);
}
ihl = iph->ihl << 2;
ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
}
/*
* Check if the packet belongs to an existing entry
*/
cp = pp->conn_out_get(skb, pp, iph, ihl, 0);
cp = pp->conn_out_get(AF_INET, skb, pp, &iph, iph.len, 0);
if (unlikely(!cp)) {
if (sysctl_ip_vs_nat_icmp_send &&
@ -730,18 +730,18 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
pp->protocol == IPPROTO_UDP)) {
__be16 _ports[2], *pptr;
pptr = skb_header_pointer(skb, ihl,
pptr = skb_header_pointer(skb, iph.len,
sizeof(_ports), _ports);
if (pptr == NULL)
return NF_ACCEPT; /* Not for me */
if (ip_vs_lookup_real_service(iph->protocol,
iph->saddr, pptr[0])) {
if (ip_vs_lookup_real_service(iph.protocol,
iph.saddr.ip, pptr[0])) {
/*
* Notify the real server: there is no
* existing entry if it is not RST
* packet or not TCP packet.
*/
if (iph->protocol != IPPROTO_TCP
if (iph.protocol != IPPROTO_TCP
|| !is_tcp_reset(skb)) {
icmp_send(skb,ICMP_DEST_UNREACH,
ICMP_PORT_UNREACH, 0);
@ -756,7 +756,7 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb,
IP_VS_DBG_PKT(11, pp, skb, 0, "Outgoing packet");
if (!skb_make_writable(skb, ihl))
if (!skb_make_writable(skb, iph.len))
goto drop;
/* mangle the packet */
@ -804,6 +804,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
struct iphdr *iph;
struct icmphdr _icmph, *ic;
struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */
struct ip_vs_iphdr ciph;
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
unsigned int offset, ihl, verdict;
@ -860,8 +861,9 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
offset += cih->ihl * 4;
ip_vs_fill_iphdr(AF_INET, cih, &ciph);
/* The embedded headers contain source and dest in reverse order */
cp = pp->conn_in_get(skb, pp, cih, offset, 1);
cp = pp->conn_in_get(AF_INET, skb, pp, &ciph, offset, 1);
if (!cp)
return NF_ACCEPT;
@ -897,11 +899,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct ip_vs_iphdr iph;
struct ip_vs_protocol *pp;
struct ip_vs_conn *cp;
int ret, restart;
int ihl;
ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
/*
* Big tappo: only PACKET_HOST (neither loopback nor mcasts)
@ -909,38 +912,35 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb,
*/
if (unlikely(skb->pkt_type != PACKET_HOST
|| skb->dev->flags & IFF_LOOPBACK || skb->sk)) {
IP_VS_DBG(12, "packet type=%d proto=%d daddr=%d.%d.%d.%d ignored\n",
IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s ignored\n",
skb->pkt_type,
ip_hdr(skb)->protocol,
NIPQUAD(ip_hdr(skb)->daddr));
iph.protocol,
IP_VS_DBG_ADDR(AF_INET, &iph.daddr));
return NF_ACCEPT;
}
iph = ip_hdr(skb);
if (unlikely(iph->protocol == IPPROTO_ICMP)) {
if (unlikely(iph.protocol == IPPROTO_ICMP)) {
int related, verdict = ip_vs_in_icmp(skb, &related, hooknum);
if (related)
return verdict;
iph = ip_hdr(skb);
ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
}
/* Protocol supported? */
pp = ip_vs_proto_get(iph->protocol);
pp = ip_vs_proto_get(iph.protocol);
if (unlikely(!pp))
return NF_ACCEPT;
ihl = iph->ihl << 2;
/*
* Check if the packet belongs to an existing connection entry
*/
cp = pp->conn_in_get(skb, pp, iph, ihl, 0);
cp = pp->conn_in_get(AF_INET, skb, pp, &iph, iph.len, 0);
if (unlikely(!cp)) {
int v;
if (!pp->conn_schedule(skb, pp, &v, &cp))
if (!pp->conn_schedule(AF_INET, skb, pp, &v, &cp))
return v;
}

View File

@ -39,25 +39,23 @@ struct isakmp_hdr {
static struct ip_vs_conn *
ah_esp_conn_in_get(const struct sk_buff *skb,
struct ip_vs_protocol *pp,
const struct iphdr *iph,
unsigned int proto_off,
ah_esp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct ip_vs_iphdr *iph, unsigned int proto_off,
int inverse)
{
struct ip_vs_conn *cp;
if (likely(!inverse)) {
cp = ip_vs_conn_in_get(IPPROTO_UDP,
iph->saddr,
iph->saddr.ip,
htons(PORT_ISAKMP),
iph->daddr,
iph->daddr.ip,
htons(PORT_ISAKMP));
} else {
cp = ip_vs_conn_in_get(IPPROTO_UDP,
iph->daddr,
iph->daddr.ip,
htons(PORT_ISAKMP),
iph->saddr,
iph->saddr.ip,
htons(PORT_ISAKMP));
}
@ -66,12 +64,12 @@ ah_esp_conn_in_get(const struct sk_buff *skb,
* We are not sure if the packet is from our
* service, so our conn_schedule hook should return NF_ACCEPT
*/
IP_VS_DBG(12, "Unknown ISAKMP entry for outin packet "
"%s%s %u.%u.%u.%u->%u.%u.%u.%u\n",
IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for outin packet "
"%s%s %s->%s\n",
inverse ? "ICMP+" : "",
pp->name,
NIPQUAD(iph->saddr),
NIPQUAD(iph->daddr));
IP_VS_DBG_ADDR(af, &iph->saddr),
IP_VS_DBG_ADDR(af, &iph->daddr));
}
return cp;
@ -79,32 +77,35 @@ ah_esp_conn_in_get(const struct sk_buff *skb,
static struct ip_vs_conn *
ah_esp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct iphdr *iph, unsigned int proto_off, int inverse)
ah_esp_conn_out_get(int af, const struct sk_buff *skb,
struct ip_vs_protocol *pp,
const struct ip_vs_iphdr *iph,
unsigned int proto_off,
int inverse)
{
struct ip_vs_conn *cp;
if (likely(!inverse)) {
cp = ip_vs_conn_out_get(IPPROTO_UDP,
iph->saddr,
iph->saddr.ip,
htons(PORT_ISAKMP),
iph->daddr,
iph->daddr.ip,
htons(PORT_ISAKMP));
} else {
cp = ip_vs_conn_out_get(IPPROTO_UDP,
iph->daddr,
iph->daddr.ip,
htons(PORT_ISAKMP),
iph->saddr,
iph->saddr.ip,
htons(PORT_ISAKMP));
}
if (!cp) {
IP_VS_DBG(12, "Unknown ISAKMP entry for inout packet "
"%s%s %u.%u.%u.%u->%u.%u.%u.%u\n",
IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for inout packet "
"%s%s %s->%s\n",
inverse ? "ICMP+" : "",
pp->name,
NIPQUAD(iph->saddr),
NIPQUAD(iph->daddr));
IP_VS_DBG_ADDR(af, &iph->saddr),
IP_VS_DBG_ADDR(af, &iph->daddr));
}
return cp;
@ -112,8 +113,7 @@ ah_esp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
ah_esp_conn_schedule(struct sk_buff *skb,
struct ip_vs_protocol *pp,
ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
int *verdict, struct ip_vs_conn **cpp)
{
/*

View File

@ -25,8 +25,9 @@
static struct ip_vs_conn *
tcp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct iphdr *iph, unsigned int proto_off, int inverse)
tcp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct ip_vs_iphdr *iph, unsigned int proto_off,
int inverse)
{
__be16 _ports[2], *pptr;
@ -36,18 +37,19 @@ tcp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
if (likely(!inverse)) {
return ip_vs_conn_in_get(iph->protocol,
iph->saddr, pptr[0],
iph->daddr, pptr[1]);
iph->saddr.ip, pptr[0],
iph->daddr.ip, pptr[1]);
} else {
return ip_vs_conn_in_get(iph->protocol,
iph->daddr, pptr[1],
iph->saddr, pptr[0]);
iph->daddr.ip, pptr[1],
iph->saddr.ip, pptr[0]);
}
}
static struct ip_vs_conn *
tcp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct iphdr *iph, unsigned int proto_off, int inverse)
tcp_conn_out_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct ip_vs_iphdr *iph, unsigned int proto_off,
int inverse)
{
__be16 _ports[2], *pptr;
@ -57,26 +59,25 @@ tcp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
if (likely(!inverse)) {
return ip_vs_conn_out_get(iph->protocol,
iph->saddr, pptr[0],
iph->daddr, pptr[1]);
iph->saddr.ip, pptr[0],
iph->daddr.ip, pptr[1]);
} else {
return ip_vs_conn_out_get(iph->protocol,
iph->daddr, pptr[1],
iph->saddr, pptr[0]);
iph->daddr.ip, pptr[1],
iph->saddr.ip, pptr[0]);
}
}
static int
tcp_conn_schedule(struct sk_buff *skb,
struct ip_vs_protocol *pp,
tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
int *verdict, struct ip_vs_conn **cpp)
{
struct ip_vs_service *svc;
struct tcphdr _tcph, *th;
struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph);
if (th == NULL) {
@ -85,8 +86,8 @@ tcp_conn_schedule(struct sk_buff *skb,
}
if (th->syn &&
(svc = ip_vs_service_get(AF_INET, skb->mark, iph.protocol,
&iph.daddr, th->dest))) {
(svc = ip_vs_service_get(af, skb->mark, iph.protocol, &iph.daddr,
th->dest))) {
if (ip_vs_todrop()) {
/*
* It seems that we are very loaded.
@ -136,7 +137,7 @@ tcp_snat_handler(struct sk_buff *skb,
if (unlikely(cp->app != NULL)) {
/* Some checks before mangling */
if (pp->csum_check && !pp->csum_check(skb, pp))
if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp))
return 0;
/* Call application helper if needed */
@ -182,7 +183,7 @@ tcp_dnat_handler(struct sk_buff *skb,
if (unlikely(cp->app != NULL)) {
/* Some checks before mangling */
if (pp->csum_check && !pp->csum_check(skb, pp))
if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp))
return 0;
/*
@ -219,17 +220,39 @@ tcp_dnat_handler(struct sk_buff *skb,
static int
tcp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
{
const unsigned int tcphoff = ip_hdrlen(skb);
unsigned int tcphoff;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
tcphoff = sizeof(struct ipv6hdr);
else
#endif
tcphoff = ip_hdrlen(skb);
switch (skb->ip_summed) {
case CHECKSUM_NONE:
skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
case CHECKSUM_COMPLETE:
if (csum_tcpudp_magic(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
skb->len - tcphoff,
ip_hdr(skb)->protocol, skb->csum)) {
ipv6_hdr(skb)->nexthdr,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, pp, skb, 0,
"Failed checksum for");
return 0;
}
} else
#endif
if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr,
skb->len - tcphoff,
ip_hdr(skb)->protocol,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, pp, skb, 0,
"Failed checksum for");
return 0;

View File

@ -24,8 +24,9 @@
#include <net/ip.h>
static struct ip_vs_conn *
udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct iphdr *iph, unsigned int proto_off, int inverse)
udp_conn_in_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct ip_vs_iphdr *iph, unsigned int proto_off,
int inverse)
{
struct ip_vs_conn *cp;
__be16 _ports[2], *pptr;
@ -36,12 +37,12 @@ udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
if (likely(!inverse)) {
cp = ip_vs_conn_in_get(iph->protocol,
iph->saddr, pptr[0],
iph->daddr, pptr[1]);
iph->saddr.ip, pptr[0],
iph->daddr.ip, pptr[1]);
} else {
cp = ip_vs_conn_in_get(iph->protocol,
iph->daddr, pptr[1],
iph->saddr, pptr[0]);
iph->daddr.ip, pptr[1],
iph->saddr.ip, pptr[0]);
}
return cp;
@ -49,25 +50,25 @@ udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
static struct ip_vs_conn *
udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct iphdr *iph, unsigned int proto_off, int inverse)
udp_conn_out_get(int af, const struct sk_buff *skb, struct ip_vs_protocol *pp,
const struct ip_vs_iphdr *iph, unsigned int proto_off,
int inverse)
{
struct ip_vs_conn *cp;
__be16 _ports[2], *pptr;
pptr = skb_header_pointer(skb, ip_hdrlen(skb),
sizeof(_ports), _ports);
pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
if (pptr == NULL)
return NULL;
if (likely(!inverse)) {
cp = ip_vs_conn_out_get(iph->protocol,
iph->saddr, pptr[0],
iph->daddr, pptr[1]);
iph->saddr.ip, pptr[0],
iph->daddr.ip, pptr[1]);
} else {
cp = ip_vs_conn_out_get(iph->protocol,
iph->daddr, pptr[1],
iph->saddr, pptr[0]);
iph->daddr.ip, pptr[1],
iph->saddr.ip, pptr[0]);
}
return cp;
@ -75,14 +76,14 @@ udp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
int *verdict, struct ip_vs_conn **cpp)
{
struct ip_vs_service *svc;
struct udphdr _udph, *uh;
struct ip_vs_iphdr iph;
ip_vs_fill_iphdr(AF_INET, skb_network_header(skb), &iph);
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph);
if (uh == NULL) {
@ -90,7 +91,7 @@ udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
return 0;
}
svc = ip_vs_service_get(AF_INET, skb->mark, iph.protocol,
svc = ip_vs_service_get(af, skb->mark, iph.protocol,
&iph.daddr, uh->dest);
if (svc) {
if (ip_vs_todrop()) {
@ -143,7 +144,7 @@ udp_snat_handler(struct sk_buff *skb,
if (unlikely(cp->app != NULL)) {
/* Some checks before mangling */
if (pp->csum_check && !pp->csum_check(skb, pp))
if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp))
return 0;
/*
@ -195,7 +196,7 @@ udp_dnat_handler(struct sk_buff *skb,
if (unlikely(cp->app != NULL)) {
/* Some checks before mangling */
if (pp->csum_check && !pp->csum_check(skb, pp))
if (pp->csum_check && !pp->csum_check(AF_INET, skb, pp))
return 0;
/*
@ -234,10 +235,17 @@ udp_dnat_handler(struct sk_buff *skb,
static int
udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp)
{
struct udphdr _udph, *uh;
const unsigned int udphoff = ip_hdrlen(skb);
unsigned int udphoff;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
udphoff = sizeof(struct ipv6hdr);
else
#endif
udphoff = ip_hdrlen(skb);
uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
if (uh == NULL)
@ -249,6 +257,19 @@ udp_csum_check(struct sk_buff *skb, struct ip_vs_protocol *pp)
skb->csum = skb_checksum(skb, udphoff,
skb->len - udphoff, 0);
case CHECKSUM_COMPLETE:
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
skb->len - udphoff,
ipv6_hdr(skb)->nexthdr,
skb->csum)) {
IP_VS_DBG_RL_PKT(0, pp, skb, 0,
"Failed checksum for");
return 0;
}
} else
#endif
if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr,
skb->len - udphoff,