Merge branch 'lvs-next-2.6' of git://git.kernel.org/pub/scm/linux/kernel/git/horms/lvs-2.6
This commit is contained in:
commit
dacc62dbf5
|
@ -21,11 +21,103 @@
|
|||
#include <linux/timer.h>
|
||||
|
||||
#include <net/checksum.h>
|
||||
#include <linux/netfilter.h> /* for union nf_inet_addr */
|
||||
#include <linux/ipv6.h> /* for struct ipv6hdr */
|
||||
#include <net/ipv6.h> /* for ipv6_addr_copy */
|
||||
|
||||
struct ip_vs_iphdr {
|
||||
int len;
|
||||
__u8 protocol;
|
||||
union nf_inet_addr saddr;
|
||||
union nf_inet_addr daddr;
|
||||
};
|
||||
|
||||
static inline void
|
||||
ip_vs_fill_iphdr(int af, const void *nh, struct ip_vs_iphdr *iphdr)
|
||||
{
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6) {
|
||||
const struct ipv6hdr *iph = nh;
|
||||
iphdr->len = sizeof(struct ipv6hdr);
|
||||
iphdr->protocol = iph->nexthdr;
|
||||
ipv6_addr_copy(&iphdr->saddr.in6, &iph->saddr);
|
||||
ipv6_addr_copy(&iphdr->daddr.in6, &iph->daddr);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
const struct iphdr *iph = nh;
|
||||
iphdr->len = iph->ihl * 4;
|
||||
iphdr->protocol = iph->protocol;
|
||||
iphdr->saddr.ip = iph->saddr;
|
||||
iphdr->daddr.ip = iph->daddr;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ip_vs_addr_copy(int af, union nf_inet_addr *dst,
|
||||
const union nf_inet_addr *src)
|
||||
{
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
ipv6_addr_copy(&dst->in6, &src->in6);
|
||||
else
|
||||
#endif
|
||||
dst->ip = src->ip;
|
||||
}
|
||||
|
||||
static inline int ip_vs_addr_equal(int af, const union nf_inet_addr *a,
|
||||
const union nf_inet_addr *b)
|
||||
{
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
return ipv6_addr_equal(&a->in6, &b->in6);
|
||||
#endif
|
||||
return a->ip == b->ip;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_DEBUG
|
||||
#include <linux/net.h>
|
||||
|
||||
extern int ip_vs_get_debug_level(void);
|
||||
|
||||
static inline const char *ip_vs_dbg_addr(int af, char *buf, size_t buf_len,
|
||||
const union nf_inet_addr *addr,
|
||||
int *idx)
|
||||
{
|
||||
int len;
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
len = snprintf(&buf[*idx], buf_len - *idx, "[" NIP6_FMT "]",
|
||||
NIP6(addr->in6)) + 1;
|
||||
else
|
||||
#endif
|
||||
len = snprintf(&buf[*idx], buf_len - *idx, NIPQUAD_FMT,
|
||||
NIPQUAD(addr->ip)) + 1;
|
||||
|
||||
*idx += len;
|
||||
BUG_ON(*idx > buf_len + 1);
|
||||
return &buf[*idx - len];
|
||||
}
|
||||
|
||||
#define IP_VS_DBG_BUF(level, msg...) \
|
||||
do { \
|
||||
char ip_vs_dbg_buf[160]; \
|
||||
int ip_vs_dbg_idx = 0; \
|
||||
if (level <= ip_vs_get_debug_level()) \
|
||||
printk(KERN_DEBUG "IPVS: " msg); \
|
||||
} while (0)
|
||||
#define IP_VS_ERR_BUF(msg...) \
|
||||
do { \
|
||||
char ip_vs_dbg_buf[160]; \
|
||||
int ip_vs_dbg_idx = 0; \
|
||||
printk(KERN_ERR "IPVS: " msg); \
|
||||
} while (0)
|
||||
|
||||
/* Only use from within IP_VS_DBG_BUF() or IP_VS_ERR_BUF macros */
|
||||
#define IP_VS_DBG_ADDR(af, addr) \
|
||||
ip_vs_dbg_addr(af, ip_vs_dbg_buf, \
|
||||
sizeof(ip_vs_dbg_buf), addr, \
|
||||
&ip_vs_dbg_idx)
|
||||
|
||||
#define IP_VS_DBG(level, msg...) \
|
||||
do { \
|
||||
if (level <= ip_vs_get_debug_level()) \
|
||||
|
@ -48,6 +140,8 @@ extern int ip_vs_get_debug_level(void);
|
|||
pp->debug_packet(pp, skb, ofs, msg); \
|
||||
} while (0)
|
||||
#else /* NO DEBUGGING at ALL */
|
||||
#define IP_VS_DBG_BUF(level, msg...) do {} while (0)
|
||||
#define IP_VS_ERR_BUF(msg...) do {} while (0)
|
||||
#define IP_VS_DBG(level, msg...) do {} while (0)
|
||||
#define IP_VS_DBG_RL(msg...) do {} while (0)
|
||||
#define IP_VS_DBG_PKT(level, pp, skb, ofs, msg) do {} while (0)
|
||||
|
@ -160,27 +254,10 @@ struct ip_vs_estimator {
|
|||
|
||||
struct ip_vs_stats
|
||||
{
|
||||
__u32 conns; /* connections scheduled */
|
||||
__u32 inpkts; /* incoming packets */
|
||||
__u32 outpkts; /* outgoing packets */
|
||||
__u64 inbytes; /* incoming bytes */
|
||||
__u64 outbytes; /* outgoing bytes */
|
||||
|
||||
__u32 cps; /* current connection rate */
|
||||
__u32 inpps; /* current in packet rate */
|
||||
__u32 outpps; /* current out packet rate */
|
||||
__u32 inbps; /* current in byte rate */
|
||||
__u32 outbps; /* current out byte rate */
|
||||
|
||||
/*
|
||||
* Don't add anything before the lock, because we use memcpy() to copy
|
||||
* the members before the lock to struct ip_vs_stats_user in
|
||||
* ip_vs_ctl.c.
|
||||
*/
|
||||
struct ip_vs_stats_user ustats; /* statistics */
|
||||
struct ip_vs_estimator est; /* estimator */
|
||||
|
||||
spinlock_t lock; /* spin lock */
|
||||
|
||||
struct ip_vs_estimator est; /* estimator */
|
||||
};
|
||||
|
||||
struct dst_entry;
|
||||
|
@ -202,21 +279,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);
|
||||
|
||||
|
@ -226,7 +305,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);
|
||||
|
||||
|
@ -259,9 +339,10 @@ struct ip_vs_conn {
|
|||
struct list_head c_list; /* hashed list heads */
|
||||
|
||||
/* Protocol, addresses and port numbers */
|
||||
__be32 caddr; /* client address */
|
||||
__be32 vaddr; /* virtual address */
|
||||
__be32 daddr; /* destination address */
|
||||
u16 af; /* address family */
|
||||
union nf_inet_addr caddr; /* client address */
|
||||
union nf_inet_addr vaddr; /* virtual address */
|
||||
union nf_inet_addr daddr; /* destination address */
|
||||
__be16 cport;
|
||||
__be16 vport;
|
||||
__be16 dport;
|
||||
|
@ -304,6 +385,45 @@ struct ip_vs_conn {
|
|||
};
|
||||
|
||||
|
||||
/*
|
||||
* Extended internal versions of struct ip_vs_service_user and
|
||||
* ip_vs_dest_user for IPv6 support.
|
||||
*
|
||||
* We need these to conveniently pass around service and destination
|
||||
* options, but unfortunately, we also need to keep the old definitions to
|
||||
* maintain userspace backwards compatibility for the setsockopt interface.
|
||||
*/
|
||||
struct ip_vs_service_user_kern {
|
||||
/* virtual service addresses */
|
||||
u16 af;
|
||||
u16 protocol;
|
||||
union nf_inet_addr addr; /* virtual ip address */
|
||||
u16 port;
|
||||
u32 fwmark; /* firwall mark of service */
|
||||
|
||||
/* virtual service options */
|
||||
char *sched_name;
|
||||
unsigned flags; /* virtual service flags */
|
||||
unsigned timeout; /* persistent timeout in sec */
|
||||
u32 netmask; /* persistent netmask */
|
||||
};
|
||||
|
||||
|
||||
struct ip_vs_dest_user_kern {
|
||||
/* destination server address */
|
||||
union nf_inet_addr addr;
|
||||
u16 port;
|
||||
|
||||
/* real server options */
|
||||
unsigned conn_flags; /* connection flags */
|
||||
int weight; /* destination weight */
|
||||
|
||||
/* thresholds for active connections */
|
||||
u32 u_threshold; /* upper threshold */
|
||||
u32 l_threshold; /* lower threshold */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The information about the virtual service offered to the net
|
||||
* and the forwarding entries
|
||||
|
@ -314,8 +434,9 @@ struct ip_vs_service {
|
|||
atomic_t refcnt; /* reference counter */
|
||||
atomic_t usecnt; /* use counter */
|
||||
|
||||
u16 af; /* address family */
|
||||
__u16 protocol; /* which protocol (TCP/UDP) */
|
||||
__be32 addr; /* IP address for virtual service */
|
||||
union nf_inet_addr addr; /* IP address for virtual service */
|
||||
__be16 port; /* port number for the service */
|
||||
__u32 fwmark; /* firewall mark of the service */
|
||||
unsigned flags; /* service status flags */
|
||||
|
@ -342,7 +463,8 @@ struct ip_vs_dest {
|
|||
struct list_head n_list; /* for the dests in the service */
|
||||
struct list_head d_list; /* for table with all the dests */
|
||||
|
||||
__be32 addr; /* IP address of the server */
|
||||
u16 af; /* address family */
|
||||
union nf_inet_addr addr; /* IP address of the server */
|
||||
__be16 port; /* port number of the server */
|
||||
volatile unsigned flags; /* dest status flags */
|
||||
atomic_t conn_flags; /* flags to copy to conn */
|
||||
|
@ -366,7 +488,7 @@ struct ip_vs_dest {
|
|||
/* for virtual service */
|
||||
struct ip_vs_service *svc; /* service it belongs to */
|
||||
__u16 protocol; /* which protocol (TCP/UDP) */
|
||||
__be32 vaddr; /* virtual IP address */
|
||||
union nf_inet_addr vaddr; /* virtual IP address */
|
||||
__be16 vport; /* virtual port number */
|
||||
__u32 vfwmark; /* firewall mark of service */
|
||||
};
|
||||
|
@ -380,6 +502,9 @@ struct ip_vs_scheduler {
|
|||
char *name; /* scheduler name */
|
||||
atomic_t refcnt; /* reference counter */
|
||||
struct module *module; /* THIS_MODULE/NULL */
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
int supports_ipv6; /* scheduler has IPv6 support */
|
||||
#endif
|
||||
|
||||
/* scheduler initializing service */
|
||||
int (*init_service)(struct ip_vs_service *svc);
|
||||
|
@ -479,16 +604,8 @@ extern void ip_vs_init_hash_table(struct list_head *table, int rows);
|
|||
#ifndef CONFIG_IP_VS_TAB_BITS
|
||||
#define CONFIG_IP_VS_TAB_BITS 12
|
||||
#endif
|
||||
/* make sure that IP_VS_CONN_TAB_BITS is located in [8, 20] */
|
||||
#if CONFIG_IP_VS_TAB_BITS < 8
|
||||
#define IP_VS_CONN_TAB_BITS 8
|
||||
#endif
|
||||
#if CONFIG_IP_VS_TAB_BITS > 20
|
||||
#define IP_VS_CONN_TAB_BITS 20
|
||||
#endif
|
||||
#if 8 <= CONFIG_IP_VS_TAB_BITS && CONFIG_IP_VS_TAB_BITS <= 20
|
||||
|
||||
#define IP_VS_CONN_TAB_BITS CONFIG_IP_VS_TAB_BITS
|
||||
#endif
|
||||
#define IP_VS_CONN_TAB_SIZE (1 << IP_VS_CONN_TAB_BITS)
|
||||
#define IP_VS_CONN_TAB_MASK (IP_VS_CONN_TAB_SIZE - 1)
|
||||
|
||||
|
@ -500,11 +617,16 @@ enum {
|
|||
};
|
||||
|
||||
extern struct ip_vs_conn *ip_vs_conn_in_get
|
||||
(int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port);
|
||||
(int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port,
|
||||
const union nf_inet_addr *d_addr, __be16 d_port);
|
||||
|
||||
extern struct ip_vs_conn *ip_vs_ct_in_get
|
||||
(int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port);
|
||||
(int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port,
|
||||
const union nf_inet_addr *d_addr, __be16 d_port);
|
||||
|
||||
extern struct ip_vs_conn *ip_vs_conn_out_get
|
||||
(int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port);
|
||||
(int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port,
|
||||
const union nf_inet_addr *d_addr, __be16 d_port);
|
||||
|
||||
/* put back the conn without restarting its timer */
|
||||
static inline void __ip_vs_conn_put(struct ip_vs_conn *cp)
|
||||
|
@ -515,8 +637,9 @@ extern void ip_vs_conn_put(struct ip_vs_conn *cp);
|
|||
extern void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport);
|
||||
|
||||
extern struct ip_vs_conn *
|
||||
ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport,
|
||||
__be32 daddr, __be16 dport, unsigned flags,
|
||||
ip_vs_conn_new(int af, int proto, const union nf_inet_addr *caddr, __be16 cport,
|
||||
const union nf_inet_addr *vaddr, __be16 vport,
|
||||
const union nf_inet_addr *daddr, __be16 dport, unsigned flags,
|
||||
struct ip_vs_dest *dest);
|
||||
extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp);
|
||||
|
||||
|
@ -532,24 +655,32 @@ static inline void ip_vs_control_del(struct ip_vs_conn *cp)
|
|||
{
|
||||
struct ip_vs_conn *ctl_cp = cp->control;
|
||||
if (!ctl_cp) {
|
||||
IP_VS_ERR("request control DEL for uncontrolled: "
|
||||
"%d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n",
|
||||
NIPQUAD(cp->caddr),ntohs(cp->cport),
|
||||
NIPQUAD(cp->vaddr),ntohs(cp->vport));
|
||||
IP_VS_ERR_BUF("request control DEL for uncontrolled: "
|
||||
"%s:%d to %s:%d\n",
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr),
|
||||
ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->vaddr),
|
||||
ntohs(cp->vport));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
IP_VS_DBG(7, "DELeting control for: "
|
||||
"cp.dst=%d.%d.%d.%d:%d ctl_cp.dst=%d.%d.%d.%d:%d\n",
|
||||
NIPQUAD(cp->caddr),ntohs(cp->cport),
|
||||
NIPQUAD(ctl_cp->caddr),ntohs(ctl_cp->cport));
|
||||
IP_VS_DBG_BUF(7, "DELeting control for: "
|
||||
"cp.dst=%s:%d ctl_cp.dst=%s:%d\n",
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr),
|
||||
ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &ctl_cp->caddr),
|
||||
ntohs(ctl_cp->cport));
|
||||
|
||||
cp->control = NULL;
|
||||
if (atomic_read(&ctl_cp->n_control) == 0) {
|
||||
IP_VS_ERR("BUG control DEL with n=0 : "
|
||||
"%d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n",
|
||||
NIPQUAD(cp->caddr),ntohs(cp->cport),
|
||||
NIPQUAD(cp->vaddr),ntohs(cp->vport));
|
||||
IP_VS_ERR_BUF("BUG control DEL with n=0 : "
|
||||
"%s:%d to %s:%d\n",
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr),
|
||||
ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->vaddr),
|
||||
ntohs(cp->vport));
|
||||
|
||||
return;
|
||||
}
|
||||
atomic_dec(&ctl_cp->n_control);
|
||||
|
@ -559,17 +690,22 @@ static inline void
|
|||
ip_vs_control_add(struct ip_vs_conn *cp, struct ip_vs_conn *ctl_cp)
|
||||
{
|
||||
if (cp->control) {
|
||||
IP_VS_ERR("request control ADD for already controlled: "
|
||||
"%d.%d.%d.%d:%d to %d.%d.%d.%d:%d\n",
|
||||
NIPQUAD(cp->caddr),ntohs(cp->cport),
|
||||
NIPQUAD(cp->vaddr),ntohs(cp->vport));
|
||||
IP_VS_ERR_BUF("request control ADD for already controlled: "
|
||||
"%s:%d to %s:%d\n",
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr),
|
||||
ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->vaddr),
|
||||
ntohs(cp->vport));
|
||||
|
||||
ip_vs_control_del(cp);
|
||||
}
|
||||
|
||||
IP_VS_DBG(7, "ADDing control for: "
|
||||
"cp.dst=%d.%d.%d.%d:%d ctl_cp.dst=%d.%d.%d.%d:%d\n",
|
||||
NIPQUAD(cp->caddr),ntohs(cp->cport),
|
||||
NIPQUAD(ctl_cp->caddr),ntohs(ctl_cp->cport));
|
||||
IP_VS_DBG_BUF(7, "ADDing control for: "
|
||||
"cp.dst=%s:%d ctl_cp.dst=%s:%d\n",
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr),
|
||||
ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &ctl_cp->caddr),
|
||||
ntohs(ctl_cp->cport));
|
||||
|
||||
cp->control = ctl_cp;
|
||||
atomic_inc(&ctl_cp->n_control);
|
||||
|
@ -647,7 +783,8 @@ extern struct ip_vs_stats ip_vs_stats;
|
|||
extern const struct ctl_path net_vs_ctl_path[];
|
||||
|
||||
extern struct ip_vs_service *
|
||||
ip_vs_service_get(__u32 fwmark, __u16 protocol, __be32 vaddr, __be16 vport);
|
||||
ip_vs_service_get(int af, __u32 fwmark, __u16 protocol,
|
||||
const union nf_inet_addr *vaddr, __be16 vport);
|
||||
|
||||
static inline void ip_vs_service_put(struct ip_vs_service *svc)
|
||||
{
|
||||
|
@ -655,14 +792,16 @@ static inline void ip_vs_service_put(struct ip_vs_service *svc)
|
|||
}
|
||||
|
||||
extern struct ip_vs_dest *
|
||||
ip_vs_lookup_real_service(__u16 protocol, __be32 daddr, __be16 dport);
|
||||
ip_vs_lookup_real_service(int af, __u16 protocol,
|
||||
const union nf_inet_addr *daddr, __be16 dport);
|
||||
|
||||
extern int ip_vs_use_count_inc(void);
|
||||
extern void ip_vs_use_count_dec(void);
|
||||
extern int ip_vs_control_init(void);
|
||||
extern void ip_vs_control_cleanup(void);
|
||||
extern struct ip_vs_dest *
|
||||
ip_vs_find_dest(__be32 daddr, __be16 dport,
|
||||
__be32 vaddr, __be16 vport, __u16 protocol);
|
||||
ip_vs_find_dest(int af, const union nf_inet_addr *daddr, __be16 dport,
|
||||
const union nf_inet_addr *vaddr, __be16 vport, __u16 protocol);
|
||||
extern struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp);
|
||||
|
||||
|
||||
|
@ -706,6 +845,19 @@ extern int ip_vs_icmp_xmit
|
|||
(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp, int offset);
|
||||
extern void ip_vs_dst_reset(struct ip_vs_dest *dest);
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
extern int ip_vs_bypass_xmit_v6
|
||||
(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp);
|
||||
extern int ip_vs_nat_xmit_v6
|
||||
(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp);
|
||||
extern int ip_vs_tunnel_xmit_v6
|
||||
(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp);
|
||||
extern int ip_vs_dr_xmit_v6
|
||||
(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp);
|
||||
extern int ip_vs_icmp_xmit_v6
|
||||
(struct sk_buff *skb, struct ip_vs_conn *cp, struct ip_vs_protocol *pp,
|
||||
int offset);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is a simple mechanism to ignore packets when
|
||||
|
@ -750,7 +902,12 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp)
|
|||
}
|
||||
|
||||
extern void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
|
||||
struct ip_vs_conn *cp, int dir);
|
||||
struct ip_vs_conn *cp, int dir);
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
extern void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
|
||||
struct ip_vs_conn *cp, int dir);
|
||||
#endif
|
||||
|
||||
extern __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
|
||||
|
||||
|
@ -761,6 +918,17 @@ static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
|
|||
return csum_partial((char *) diff, sizeof(diff), oldsum);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
static inline __wsum ip_vs_check_diff16(const __be32 *old, const __be32 *new,
|
||||
__wsum oldsum)
|
||||
{
|
||||
__be32 diff[8] = { ~old[3], ~old[2], ~old[1], ~old[0],
|
||||
new[3], new[2], new[1], new[0] };
|
||||
|
||||
return csum_partial((char *) diff, sizeof(diff), oldsum);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
|
||||
{
|
||||
__be16 diff[2] = { ~old, new };
|
||||
|
|
|
@ -24,6 +24,14 @@ menuconfig IP_VS
|
|||
|
||||
if IP_VS
|
||||
|
||||
config IP_VS_IPV6
|
||||
bool "IPv6 support for IPVS (DANGEROUS)"
|
||||
depends on EXPERIMENTAL && (IPV6 = y || IP_VS = IPV6)
|
||||
---help---
|
||||
Add IPv6 support to IPVS. This is incomplete and might be dangerous.
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config IP_VS_DEBUG
|
||||
bool "IP virtual server debugging"
|
||||
---help---
|
||||
|
@ -33,7 +41,8 @@ config IP_VS_DEBUG
|
|||
|
||||
config IP_VS_TAB_BITS
|
||||
int "IPVS connection table size (the Nth power of 2)"
|
||||
default "12"
|
||||
range 8 20
|
||||
default 12
|
||||
---help---
|
||||
The IPVS connection hash table uses the chaining scheme to handle
|
||||
hash collisions. Using a big IPVS connection hash table will greatly
|
||||
|
|
|
@ -114,9 +114,18 @@ static inline void ct_write_unlock_bh(unsigned key)
|
|||
/*
|
||||
* Returns hash value for IPVS connection entry
|
||||
*/
|
||||
static unsigned int ip_vs_conn_hashkey(unsigned proto, __be32 addr, __be16 port)
|
||||
static unsigned int ip_vs_conn_hashkey(int af, unsigned proto,
|
||||
const union nf_inet_addr *addr,
|
||||
__be16 port)
|
||||
{
|
||||
return jhash_3words((__force u32)addr, (__force u32)port, proto, ip_vs_conn_rnd)
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
return jhash_3words(jhash(addr, 16, ip_vs_conn_rnd),
|
||||
(__force u32)port, proto, ip_vs_conn_rnd)
|
||||
& IP_VS_CONN_TAB_MASK;
|
||||
#endif
|
||||
return jhash_3words((__force u32)addr->ip, (__force u32)port, proto,
|
||||
ip_vs_conn_rnd)
|
||||
& IP_VS_CONN_TAB_MASK;
|
||||
}
|
||||
|
||||
|
@ -131,7 +140,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
|
|||
int ret;
|
||||
|
||||
/* Hash by protocol, client address and port */
|
||||
hash = ip_vs_conn_hashkey(cp->protocol, cp->caddr, cp->cport);
|
||||
hash = ip_vs_conn_hashkey(cp->af, cp->protocol, &cp->caddr, cp->cport);
|
||||
|
||||
ct_write_lock(hash);
|
||||
|
||||
|
@ -162,7 +171,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
|
|||
int ret;
|
||||
|
||||
/* unhash it and decrease its reference counter */
|
||||
hash = ip_vs_conn_hashkey(cp->protocol, cp->caddr, cp->cport);
|
||||
hash = ip_vs_conn_hashkey(cp->af, cp->protocol, &cp->caddr, cp->cport);
|
||||
|
||||
ct_write_lock(hash);
|
||||
|
||||
|
@ -187,20 +196,23 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
|
|||
* d_addr, d_port: pkt dest address (load balancer)
|
||||
*/
|
||||
static inline struct ip_vs_conn *__ip_vs_conn_in_get
|
||||
(int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port)
|
||||
(int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port,
|
||||
const union nf_inet_addr *d_addr, __be16 d_port)
|
||||
{
|
||||
unsigned hash;
|
||||
struct ip_vs_conn *cp;
|
||||
|
||||
hash = ip_vs_conn_hashkey(protocol, s_addr, s_port);
|
||||
hash = ip_vs_conn_hashkey(af, protocol, s_addr, s_port);
|
||||
|
||||
ct_read_lock(hash);
|
||||
|
||||
list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
|
||||
if (s_addr==cp->caddr && s_port==cp->cport &&
|
||||
d_port==cp->vport && d_addr==cp->vaddr &&
|
||||
if (cp->af == af &&
|
||||
ip_vs_addr_equal(af, s_addr, &cp->caddr) &&
|
||||
ip_vs_addr_equal(af, d_addr, &cp->vaddr) &&
|
||||
s_port == cp->cport && d_port == cp->vport &&
|
||||
((!s_port) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) &&
|
||||
protocol==cp->protocol) {
|
||||
protocol == cp->protocol) {
|
||||
/* HIT */
|
||||
atomic_inc(&cp->refcnt);
|
||||
ct_read_unlock(hash);
|
||||
|
@ -214,39 +226,44 @@ static inline struct ip_vs_conn *__ip_vs_conn_in_get
|
|||
}
|
||||
|
||||
struct ip_vs_conn *ip_vs_conn_in_get
|
||||
(int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port)
|
||||
(int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port,
|
||||
const union nf_inet_addr *d_addr, __be16 d_port)
|
||||
{
|
||||
struct ip_vs_conn *cp;
|
||||
|
||||
cp = __ip_vs_conn_in_get(protocol, s_addr, s_port, d_addr, d_port);
|
||||
cp = __ip_vs_conn_in_get(af, protocol, s_addr, s_port, d_addr, d_port);
|
||||
if (!cp && atomic_read(&ip_vs_conn_no_cport_cnt))
|
||||
cp = __ip_vs_conn_in_get(protocol, s_addr, 0, d_addr, d_port);
|
||||
cp = __ip_vs_conn_in_get(af, protocol, s_addr, 0, d_addr,
|
||||
d_port);
|
||||
|
||||
IP_VS_DBG(9, "lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
|
||||
ip_vs_proto_name(protocol),
|
||||
NIPQUAD(s_addr), ntohs(s_port),
|
||||
NIPQUAD(d_addr), ntohs(d_port),
|
||||
cp?"hit":"not hit");
|
||||
IP_VS_DBG_BUF(9, "lookup/in %s %s:%d->%s:%d %s\n",
|
||||
ip_vs_proto_name(protocol),
|
||||
IP_VS_DBG_ADDR(af, s_addr), ntohs(s_port),
|
||||
IP_VS_DBG_ADDR(af, d_addr), ntohs(d_port),
|
||||
cp ? "hit" : "not hit");
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
/* Get reference to connection template */
|
||||
struct ip_vs_conn *ip_vs_ct_in_get
|
||||
(int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port)
|
||||
(int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port,
|
||||
const union nf_inet_addr *d_addr, __be16 d_port)
|
||||
{
|
||||
unsigned hash;
|
||||
struct ip_vs_conn *cp;
|
||||
|
||||
hash = ip_vs_conn_hashkey(protocol, s_addr, s_port);
|
||||
hash = ip_vs_conn_hashkey(af, protocol, s_addr, s_port);
|
||||
|
||||
ct_read_lock(hash);
|
||||
|
||||
list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
|
||||
if (s_addr==cp->caddr && s_port==cp->cport &&
|
||||
d_port==cp->vport && d_addr==cp->vaddr &&
|
||||
if (cp->af == af &&
|
||||
ip_vs_addr_equal(af, s_addr, &cp->caddr) &&
|
||||
ip_vs_addr_equal(af, d_addr, &cp->vaddr) &&
|
||||
s_port == cp->cport && d_port == cp->vport &&
|
||||
cp->flags & IP_VS_CONN_F_TEMPLATE &&
|
||||
protocol==cp->protocol) {
|
||||
protocol == cp->protocol) {
|
||||
/* HIT */
|
||||
atomic_inc(&cp->refcnt);
|
||||
goto out;
|
||||
|
@ -257,11 +274,11 @@ struct ip_vs_conn *ip_vs_ct_in_get
|
|||
out:
|
||||
ct_read_unlock(hash);
|
||||
|
||||
IP_VS_DBG(9, "template lookup/in %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
|
||||
ip_vs_proto_name(protocol),
|
||||
NIPQUAD(s_addr), ntohs(s_port),
|
||||
NIPQUAD(d_addr), ntohs(d_port),
|
||||
cp?"hit":"not hit");
|
||||
IP_VS_DBG_BUF(9, "template lookup/in %s %s:%d->%s:%d %s\n",
|
||||
ip_vs_proto_name(protocol),
|
||||
IP_VS_DBG_ADDR(af, s_addr), ntohs(s_port),
|
||||
IP_VS_DBG_ADDR(af, d_addr), ntohs(d_port),
|
||||
cp ? "hit" : "not hit");
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
@ -273,7 +290,8 @@ struct ip_vs_conn *ip_vs_ct_in_get
|
|||
* d_addr, d_port: pkt dest address (foreign host)
|
||||
*/
|
||||
struct ip_vs_conn *ip_vs_conn_out_get
|
||||
(int protocol, __be32 s_addr, __be16 s_port, __be32 d_addr, __be16 d_port)
|
||||
(int af, int protocol, const union nf_inet_addr *s_addr, __be16 s_port,
|
||||
const union nf_inet_addr *d_addr, __be16 d_port)
|
||||
{
|
||||
unsigned hash;
|
||||
struct ip_vs_conn *cp, *ret=NULL;
|
||||
|
@ -281,13 +299,15 @@ struct ip_vs_conn *ip_vs_conn_out_get
|
|||
/*
|
||||
* Check for "full" addressed entries
|
||||
*/
|
||||
hash = ip_vs_conn_hashkey(protocol, d_addr, d_port);
|
||||
hash = ip_vs_conn_hashkey(af, protocol, d_addr, d_port);
|
||||
|
||||
ct_read_lock(hash);
|
||||
|
||||
list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) {
|
||||
if (d_addr == cp->caddr && d_port == cp->cport &&
|
||||
s_port == cp->dport && s_addr == cp->daddr &&
|
||||
if (cp->af == af &&
|
||||
ip_vs_addr_equal(af, d_addr, &cp->caddr) &&
|
||||
ip_vs_addr_equal(af, s_addr, &cp->daddr) &&
|
||||
d_port == cp->cport && s_port == cp->dport &&
|
||||
protocol == cp->protocol) {
|
||||
/* HIT */
|
||||
atomic_inc(&cp->refcnt);
|
||||
|
@ -298,11 +318,11 @@ struct ip_vs_conn *ip_vs_conn_out_get
|
|||
|
||||
ct_read_unlock(hash);
|
||||
|
||||
IP_VS_DBG(9, "lookup/out %s %u.%u.%u.%u:%d->%u.%u.%u.%u:%d %s\n",
|
||||
ip_vs_proto_name(protocol),
|
||||
NIPQUAD(s_addr), ntohs(s_port),
|
||||
NIPQUAD(d_addr), ntohs(d_port),
|
||||
ret?"hit":"not hit");
|
||||
IP_VS_DBG_BUF(9, "lookup/out %s %s:%d->%s:%d %s\n",
|
||||
ip_vs_proto_name(protocol),
|
||||
IP_VS_DBG_ADDR(af, s_addr), ntohs(s_port),
|
||||
IP_VS_DBG_ADDR(af, d_addr), ntohs(d_port),
|
||||
ret ? "hit" : "not hit");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -369,6 +389,33 @@ static inline void ip_vs_bind_xmit(struct ip_vs_conn *cp)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
static inline void ip_vs_bind_xmit_v6(struct ip_vs_conn *cp)
|
||||
{
|
||||
switch (IP_VS_FWD_METHOD(cp)) {
|
||||
case IP_VS_CONN_F_MASQ:
|
||||
cp->packet_xmit = ip_vs_nat_xmit_v6;
|
||||
break;
|
||||
|
||||
case IP_VS_CONN_F_TUNNEL:
|
||||
cp->packet_xmit = ip_vs_tunnel_xmit_v6;
|
||||
break;
|
||||
|
||||
case IP_VS_CONN_F_DROUTE:
|
||||
cp->packet_xmit = ip_vs_dr_xmit_v6;
|
||||
break;
|
||||
|
||||
case IP_VS_CONN_F_LOCALNODE:
|
||||
cp->packet_xmit = ip_vs_null_xmit;
|
||||
break;
|
||||
|
||||
case IP_VS_CONN_F_BYPASS:
|
||||
cp->packet_xmit = ip_vs_bypass_xmit_v6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static inline int ip_vs_dest_totalconns(struct ip_vs_dest *dest)
|
||||
{
|
||||
|
@ -402,16 +449,16 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
|
|||
cp->flags |= atomic_read(&dest->conn_flags);
|
||||
cp->dest = dest;
|
||||
|
||||
IP_VS_DBG(7, "Bind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "
|
||||
"d:%u.%u.%u.%u:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d "
|
||||
"dest->refcnt:%d\n",
|
||||
ip_vs_proto_name(cp->protocol),
|
||||
NIPQUAD(cp->caddr), ntohs(cp->cport),
|
||||
NIPQUAD(cp->vaddr), ntohs(cp->vport),
|
||||
NIPQUAD(cp->daddr), ntohs(cp->dport),
|
||||
ip_vs_fwd_tag(cp), cp->state,
|
||||
cp->flags, atomic_read(&cp->refcnt),
|
||||
atomic_read(&dest->refcnt));
|
||||
IP_VS_DBG_BUF(7, "Bind-dest %s c:%s:%d v:%s:%d "
|
||||
"d:%s:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d "
|
||||
"dest->refcnt:%d\n",
|
||||
ip_vs_proto_name(cp->protocol),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->daddr), ntohs(cp->dport),
|
||||
ip_vs_fwd_tag(cp), cp->state,
|
||||
cp->flags, atomic_read(&cp->refcnt),
|
||||
atomic_read(&dest->refcnt));
|
||||
|
||||
/* Update the connection counters */
|
||||
if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
|
||||
|
@ -444,8 +491,9 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
|
|||
struct ip_vs_dest *dest;
|
||||
|
||||
if ((cp) && (!cp->dest)) {
|
||||
dest = ip_vs_find_dest(cp->daddr, cp->dport,
|
||||
cp->vaddr, cp->vport, cp->protocol);
|
||||
dest = ip_vs_find_dest(cp->af, &cp->daddr, cp->dport,
|
||||
&cp->vaddr, cp->vport,
|
||||
cp->protocol);
|
||||
ip_vs_bind_dest(cp, dest);
|
||||
return dest;
|
||||
} else
|
||||
|
@ -464,16 +512,16 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
|
|||
if (!dest)
|
||||
return;
|
||||
|
||||
IP_VS_DBG(7, "Unbind-dest %s c:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "
|
||||
"d:%u.%u.%u.%u:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d "
|
||||
"dest->refcnt:%d\n",
|
||||
ip_vs_proto_name(cp->protocol),
|
||||
NIPQUAD(cp->caddr), ntohs(cp->cport),
|
||||
NIPQUAD(cp->vaddr), ntohs(cp->vport),
|
||||
NIPQUAD(cp->daddr), ntohs(cp->dport),
|
||||
ip_vs_fwd_tag(cp), cp->state,
|
||||
cp->flags, atomic_read(&cp->refcnt),
|
||||
atomic_read(&dest->refcnt));
|
||||
IP_VS_DBG_BUF(7, "Unbind-dest %s c:%s:%d v:%s:%d "
|
||||
"d:%s:%d fwd:%c s:%u conn->flags:%X conn->refcnt:%d "
|
||||
"dest->refcnt:%d\n",
|
||||
ip_vs_proto_name(cp->protocol),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->daddr), ntohs(cp->dport),
|
||||
ip_vs_fwd_tag(cp), cp->state,
|
||||
cp->flags, atomic_read(&cp->refcnt),
|
||||
atomic_read(&dest->refcnt));
|
||||
|
||||
/* Update the connection counters */
|
||||
if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) {
|
||||
|
@ -526,13 +574,16 @@ int ip_vs_check_template(struct ip_vs_conn *ct)
|
|||
!(dest->flags & IP_VS_DEST_F_AVAILABLE) ||
|
||||
(sysctl_ip_vs_expire_quiescent_template &&
|
||||
(atomic_read(&dest->weight) == 0))) {
|
||||
IP_VS_DBG(9, "check_template: dest not available for "
|
||||
"protocol %s s:%u.%u.%u.%u:%d v:%u.%u.%u.%u:%d "
|
||||
"-> d:%u.%u.%u.%u:%d\n",
|
||||
ip_vs_proto_name(ct->protocol),
|
||||
NIPQUAD(ct->caddr), ntohs(ct->cport),
|
||||
NIPQUAD(ct->vaddr), ntohs(ct->vport),
|
||||
NIPQUAD(ct->daddr), ntohs(ct->dport));
|
||||
IP_VS_DBG_BUF(9, "check_template: dest not available for "
|
||||
"protocol %s s:%s:%d v:%s:%d "
|
||||
"-> d:%s:%d\n",
|
||||
ip_vs_proto_name(ct->protocol),
|
||||
IP_VS_DBG_ADDR(ct->af, &ct->caddr),
|
||||
ntohs(ct->cport),
|
||||
IP_VS_DBG_ADDR(ct->af, &ct->vaddr),
|
||||
ntohs(ct->vport),
|
||||
IP_VS_DBG_ADDR(ct->af, &ct->daddr),
|
||||
ntohs(ct->dport));
|
||||
|
||||
/*
|
||||
* Invalidate the connection template
|
||||
|
@ -625,8 +676,9 @@ void ip_vs_conn_expire_now(struct ip_vs_conn *cp)
|
|||
* Create a new connection entry and hash it into the ip_vs_conn_tab
|
||||
*/
|
||||
struct ip_vs_conn *
|
||||
ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport,
|
||||
__be32 daddr, __be16 dport, unsigned flags,
|
||||
ip_vs_conn_new(int af, int proto, const union nf_inet_addr *caddr, __be16 cport,
|
||||
const union nf_inet_addr *vaddr, __be16 vport,
|
||||
const union nf_inet_addr *daddr, __be16 dport, unsigned flags,
|
||||
struct ip_vs_dest *dest)
|
||||
{
|
||||
struct ip_vs_conn *cp;
|
||||
|
@ -640,12 +692,13 @@ ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport
|
|||
|
||||
INIT_LIST_HEAD(&cp->c_list);
|
||||
setup_timer(&cp->timer, ip_vs_conn_expire, (unsigned long)cp);
|
||||
cp->af = af;
|
||||
cp->protocol = proto;
|
||||
cp->caddr = caddr;
|
||||
ip_vs_addr_copy(af, &cp->caddr, caddr);
|
||||
cp->cport = cport;
|
||||
cp->vaddr = vaddr;
|
||||
ip_vs_addr_copy(af, &cp->vaddr, vaddr);
|
||||
cp->vport = vport;
|
||||
cp->daddr = daddr;
|
||||
ip_vs_addr_copy(af, &cp->daddr, daddr);
|
||||
cp->dport = dport;
|
||||
cp->flags = flags;
|
||||
spin_lock_init(&cp->lock);
|
||||
|
@ -672,7 +725,12 @@ ip_vs_conn_new(int proto, __be32 caddr, __be16 cport, __be32 vaddr, __be16 vport
|
|||
cp->timeout = 3*HZ;
|
||||
|
||||
/* Bind its packet transmitter */
|
||||
ip_vs_bind_xmit(cp);
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
ip_vs_bind_xmit_v6(cp);
|
||||
else
|
||||
#endif
|
||||
ip_vs_bind_xmit(cp);
|
||||
|
||||
if (unlikely(pp && atomic_read(&pp->appcnt)))
|
||||
ip_vs_bind_app(cp, pp);
|
||||
|
@ -760,12 +818,26 @@ static int ip_vs_conn_seq_show(struct seq_file *seq, void *v)
|
|||
else {
|
||||
const struct ip_vs_conn *cp = v;
|
||||
|
||||
seq_printf(seq,
|
||||
"%-3s %08X %04X %08X %04X %08X %04X %-11s %7lu\n",
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
seq_printf(seq,
|
||||
"%-3s " NIP6_FMT " %04X " NIP6_FMT
|
||||
" %04X " NIP6_FMT " %04X %-11s %7lu\n",
|
||||
ip_vs_proto_name(cp->protocol),
|
||||
ntohl(cp->caddr), ntohs(cp->cport),
|
||||
ntohl(cp->vaddr), ntohs(cp->vport),
|
||||
ntohl(cp->daddr), ntohs(cp->dport),
|
||||
NIP6(cp->caddr.in6), ntohs(cp->cport),
|
||||
NIP6(cp->vaddr.in6), ntohs(cp->vport),
|
||||
NIP6(cp->daddr.in6), ntohs(cp->dport),
|
||||
ip_vs_state_name(cp->protocol, cp->state),
|
||||
(cp->timer.expires-jiffies)/HZ);
|
||||
else
|
||||
#endif
|
||||
seq_printf(seq,
|
||||
"%-3s %08X %04X %08X %04X"
|
||||
" %08X %04X %-11s %7lu\n",
|
||||
ip_vs_proto_name(cp->protocol),
|
||||
ntohl(cp->caddr.ip), ntohs(cp->cport),
|
||||
ntohl(cp->vaddr.ip), ntohs(cp->vport),
|
||||
ntohl(cp->daddr.ip), ntohs(cp->dport),
|
||||
ip_vs_state_name(cp->protocol, cp->state),
|
||||
(cp->timer.expires-jiffies)/HZ);
|
||||
}
|
||||
|
@ -809,12 +881,27 @@ static int ip_vs_conn_sync_seq_show(struct seq_file *seq, void *v)
|
|||
else {
|
||||
const struct ip_vs_conn *cp = v;
|
||||
|
||||
seq_printf(seq,
|
||||
"%-3s %08X %04X %08X %04X %08X %04X %-11s %-6s %7lu\n",
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
seq_printf(seq,
|
||||
"%-3s " NIP6_FMT " %04X " NIP6_FMT
|
||||
" %04X " NIP6_FMT " %04X %-11s %-6s %7lu\n",
|
||||
ip_vs_proto_name(cp->protocol),
|
||||
ntohl(cp->caddr), ntohs(cp->cport),
|
||||
ntohl(cp->vaddr), ntohs(cp->vport),
|
||||
ntohl(cp->daddr), ntohs(cp->dport),
|
||||
NIP6(cp->caddr.in6), ntohs(cp->cport),
|
||||
NIP6(cp->vaddr.in6), ntohs(cp->vport),
|
||||
NIP6(cp->daddr.in6), ntohs(cp->dport),
|
||||
ip_vs_state_name(cp->protocol, cp->state),
|
||||
ip_vs_origin_name(cp->flags),
|
||||
(cp->timer.expires-jiffies)/HZ);
|
||||
else
|
||||
#endif
|
||||
seq_printf(seq,
|
||||
"%-3s %08X %04X %08X %04X "
|
||||
"%08X %04X %-11s %-6s %7lu\n",
|
||||
ip_vs_proto_name(cp->protocol),
|
||||
ntohl(cp->caddr.ip), ntohs(cp->cport),
|
||||
ntohl(cp->vaddr.ip), ntohs(cp->vport),
|
||||
ntohl(cp->daddr.ip), ntohs(cp->dport),
|
||||
ip_vs_state_name(cp->protocol, cp->state),
|
||||
ip_vs_origin_name(cp->flags),
|
||||
(cp->timer.expires-jiffies)/HZ);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -218,7 +218,7 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|||
IP_VS_DBG(6, "DH: destination IP address %u.%u.%u.%u "
|
||||
"--> server %u.%u.%u.%u:%d\n",
|
||||
NIPQUAD(iph->daddr),
|
||||
NIPQUAD(dest->addr),
|
||||
NIPQUAD(dest->addr.ip),
|
||||
ntohs(dest->port));
|
||||
|
||||
return dest;
|
||||
|
@ -234,6 +234,9 @@ static struct ip_vs_scheduler ip_vs_dh_scheduler =
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_dh_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 0,
|
||||
#endif
|
||||
.init_service = ip_vs_dh_init_svc,
|
||||
.done_service = ip_vs_dh_done_svc,
|
||||
.update_service = ip_vs_dh_update_svc,
|
||||
|
|
|
@ -65,37 +65,37 @@ static void estimation_timer(unsigned long arg)
|
|||
s = container_of(e, struct ip_vs_stats, est);
|
||||
|
||||
spin_lock(&s->lock);
|
||||
n_conns = s->conns;
|
||||
n_inpkts = s->inpkts;
|
||||
n_outpkts = s->outpkts;
|
||||
n_inbytes = s->inbytes;
|
||||
n_outbytes = s->outbytes;
|
||||
n_conns = s->ustats.conns;
|
||||
n_inpkts = s->ustats.inpkts;
|
||||
n_outpkts = s->ustats.outpkts;
|
||||
n_inbytes = s->ustats.inbytes;
|
||||
n_outbytes = s->ustats.outbytes;
|
||||
|
||||
/* scaled by 2^10, but divided 2 seconds */
|
||||
rate = (n_conns - e->last_conns)<<9;
|
||||
e->last_conns = n_conns;
|
||||
e->cps += ((long)rate - (long)e->cps)>>2;
|
||||
s->cps = (e->cps+0x1FF)>>10;
|
||||
s->ustats.cps = (e->cps+0x1FF)>>10;
|
||||
|
||||
rate = (n_inpkts - e->last_inpkts)<<9;
|
||||
e->last_inpkts = n_inpkts;
|
||||
e->inpps += ((long)rate - (long)e->inpps)>>2;
|
||||
s->inpps = (e->inpps+0x1FF)>>10;
|
||||
s->ustats.inpps = (e->inpps+0x1FF)>>10;
|
||||
|
||||
rate = (n_outpkts - e->last_outpkts)<<9;
|
||||
e->last_outpkts = n_outpkts;
|
||||
e->outpps += ((long)rate - (long)e->outpps)>>2;
|
||||
s->outpps = (e->outpps+0x1FF)>>10;
|
||||
s->ustats.outpps = (e->outpps+0x1FF)>>10;
|
||||
|
||||
rate = (n_inbytes - e->last_inbytes)<<4;
|
||||
e->last_inbytes = n_inbytes;
|
||||
e->inbps += ((long)rate - (long)e->inbps)>>2;
|
||||
s->inbps = (e->inbps+0xF)>>5;
|
||||
s->ustats.inbps = (e->inbps+0xF)>>5;
|
||||
|
||||
rate = (n_outbytes - e->last_outbytes)<<4;
|
||||
e->last_outbytes = n_outbytes;
|
||||
e->outbps += ((long)rate - (long)e->outbps)>>2;
|
||||
s->outbps = (e->outbps+0xF)>>5;
|
||||
s->ustats.outbps = (e->outbps+0xF)>>5;
|
||||
spin_unlock(&s->lock);
|
||||
}
|
||||
spin_unlock(&est_lock);
|
||||
|
@ -108,20 +108,20 @@ void ip_vs_new_estimator(struct ip_vs_stats *stats)
|
|||
|
||||
INIT_LIST_HEAD(&est->list);
|
||||
|
||||
est->last_conns = stats->conns;
|
||||
est->cps = stats->cps<<10;
|
||||
est->last_conns = stats->ustats.conns;
|
||||
est->cps = stats->ustats.cps<<10;
|
||||
|
||||
est->last_inpkts = stats->inpkts;
|
||||
est->inpps = stats->inpps<<10;
|
||||
est->last_inpkts = stats->ustats.inpkts;
|
||||
est->inpps = stats->ustats.inpps<<10;
|
||||
|
||||
est->last_outpkts = stats->outpkts;
|
||||
est->outpps = stats->outpps<<10;
|
||||
est->last_outpkts = stats->ustats.outpkts;
|
||||
est->outpps = stats->ustats.outpps<<10;
|
||||
|
||||
est->last_inbytes = stats->inbytes;
|
||||
est->inbps = stats->inbps<<5;
|
||||
est->last_inbytes = stats->ustats.inbytes;
|
||||
est->inbps = stats->ustats.inbps<<5;
|
||||
|
||||
est->last_outbytes = stats->outbytes;
|
||||
est->outbps = stats->outbps<<5;
|
||||
est->last_outbytes = stats->ustats.outbytes;
|
||||
est->outbps = stats->ustats.outbps<<5;
|
||||
|
||||
spin_lock_bh(&est_lock);
|
||||
list_add(&est->list, &est_list);
|
||||
|
|
|
@ -140,13 +140,21 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
|
|||
struct tcphdr *th;
|
||||
char *data, *data_limit;
|
||||
char *start, *end;
|
||||
__be32 from;
|
||||
union nf_inet_addr from;
|
||||
__be16 port;
|
||||
struct ip_vs_conn *n_cp;
|
||||
char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */
|
||||
unsigned buf_len;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
/* This application helper doesn't work with IPv6 yet,
|
||||
* so turn this into a no-op for IPv6 packets
|
||||
*/
|
||||
if (cp->af == AF_INET6)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
*diff = 0;
|
||||
|
||||
/* Only useful for established sessions */
|
||||
|
@ -166,24 +174,25 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
|
|||
if (ip_vs_ftp_get_addrport(data, data_limit,
|
||||
SERVER_STRING,
|
||||
sizeof(SERVER_STRING)-1, ')',
|
||||
&from, &port,
|
||||
&from.ip, &port,
|
||||
&start, &end) != 1)
|
||||
return 1;
|
||||
|
||||
IP_VS_DBG(7, "PASV response (%u.%u.%u.%u:%d) -> "
|
||||
"%u.%u.%u.%u:%d detected\n",
|
||||
NIPQUAD(from), ntohs(port), NIPQUAD(cp->caddr), 0);
|
||||
NIPQUAD(from.ip), ntohs(port),
|
||||
NIPQUAD(cp->caddr.ip), 0);
|
||||
|
||||
/*
|
||||
* Now update or create an connection entry for it
|
||||
*/
|
||||
n_cp = ip_vs_conn_out_get(iph->protocol, from, port,
|
||||
cp->caddr, 0);
|
||||
n_cp = ip_vs_conn_out_get(AF_INET, iph->protocol, &from, port,
|
||||
&cp->caddr, 0);
|
||||
if (!n_cp) {
|
||||
n_cp = ip_vs_conn_new(IPPROTO_TCP,
|
||||
cp->caddr, 0,
|
||||
cp->vaddr, port,
|
||||
from, port,
|
||||
n_cp = ip_vs_conn_new(AF_INET, IPPROTO_TCP,
|
||||
&cp->caddr, 0,
|
||||
&cp->vaddr, port,
|
||||
&from, port,
|
||||
IP_VS_CONN_F_NO_CPORT,
|
||||
cp->dest);
|
||||
if (!n_cp)
|
||||
|
@ -196,9 +205,9 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
|
|||
/*
|
||||
* Replace the old passive address with the new one
|
||||
*/
|
||||
from = n_cp->vaddr;
|
||||
from.ip = n_cp->vaddr.ip;
|
||||
port = n_cp->vport;
|
||||
sprintf(buf,"%d,%d,%d,%d,%d,%d", NIPQUAD(from),
|
||||
sprintf(buf, "%d,%d,%d,%d,%d,%d", NIPQUAD(from.ip),
|
||||
(ntohs(port)>>8)&255, ntohs(port)&255);
|
||||
buf_len = strlen(buf);
|
||||
|
||||
|
@ -243,10 +252,18 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
|
|||
struct tcphdr *th;
|
||||
char *data, *data_start, *data_limit;
|
||||
char *start, *end;
|
||||
__be32 to;
|
||||
union nf_inet_addr to;
|
||||
__be16 port;
|
||||
struct ip_vs_conn *n_cp;
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
/* This application helper doesn't work with IPv6 yet,
|
||||
* so turn this into a no-op for IPv6 packets
|
||||
*/
|
||||
if (cp->af == AF_INET6)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
/* no diff required for incoming packets */
|
||||
*diff = 0;
|
||||
|
||||
|
@ -291,12 +308,12 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
|
|||
*/
|
||||
if (ip_vs_ftp_get_addrport(data_start, data_limit,
|
||||
CLIENT_STRING, sizeof(CLIENT_STRING)-1,
|
||||
'\r', &to, &port,
|
||||
'\r', &to.ip, &port,
|
||||
&start, &end) != 1)
|
||||
return 1;
|
||||
|
||||
IP_VS_DBG(7, "PORT %u.%u.%u.%u:%d detected\n",
|
||||
NIPQUAD(to), ntohs(port));
|
||||
NIPQUAD(to.ip), ntohs(port));
|
||||
|
||||
/* Passive mode off */
|
||||
cp->app_data = NULL;
|
||||
|
@ -306,16 +323,16 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
|
|||
*/
|
||||
IP_VS_DBG(7, "protocol %s %u.%u.%u.%u:%d %u.%u.%u.%u:%d\n",
|
||||
ip_vs_proto_name(iph->protocol),
|
||||
NIPQUAD(to), ntohs(port), NIPQUAD(cp->vaddr), 0);
|
||||
NIPQUAD(to.ip), ntohs(port), NIPQUAD(cp->vaddr.ip), 0);
|
||||
|
||||
n_cp = ip_vs_conn_in_get(iph->protocol,
|
||||
to, port,
|
||||
cp->vaddr, htons(ntohs(cp->vport)-1));
|
||||
n_cp = ip_vs_conn_in_get(AF_INET, iph->protocol,
|
||||
&to, port,
|
||||
&cp->vaddr, htons(ntohs(cp->vport)-1));
|
||||
if (!n_cp) {
|
||||
n_cp = ip_vs_conn_new(IPPROTO_TCP,
|
||||
to, port,
|
||||
cp->vaddr, htons(ntohs(cp->vport)-1),
|
||||
cp->daddr, htons(ntohs(cp->dport)-1),
|
||||
n_cp = ip_vs_conn_new(AF_INET, IPPROTO_TCP,
|
||||
&to, port,
|
||||
&cp->vaddr, htons(ntohs(cp->vport)-1),
|
||||
&cp->daddr, htons(ntohs(cp->dport)-1),
|
||||
0,
|
||||
cp->dest);
|
||||
if (!n_cp)
|
||||
|
|
|
@ -422,7 +422,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc, struct iphdr *iph)
|
|||
|
||||
IP_VS_DBG(6, "LBLC: server %d.%d.%d.%d:%d "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
NIPQUAD(least->addr), ntohs(least->port),
|
||||
NIPQUAD(least->addr.ip), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
|
@ -506,7 +506,7 @@ out:
|
|||
IP_VS_DBG(6, "LBLC: destination IP address %u.%u.%u.%u "
|
||||
"--> server %u.%u.%u.%u:%d\n",
|
||||
NIPQUAD(iph->daddr),
|
||||
NIPQUAD(dest->addr),
|
||||
NIPQUAD(dest->addr.ip),
|
||||
ntohs(dest->port));
|
||||
|
||||
return dest;
|
||||
|
@ -522,6 +522,9 @@ static struct ip_vs_scheduler ip_vs_lblc_scheduler =
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_lblc_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 0,
|
||||
#endif
|
||||
.init_service = ip_vs_lblc_init_svc,
|
||||
.done_service = ip_vs_lblc_done_svc,
|
||||
.schedule = ip_vs_lblc_schedule,
|
||||
|
|
|
@ -204,7 +204,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
|
|||
|
||||
IP_VS_DBG(6, "ip_vs_dest_set_min: server %d.%d.%d.%d:%d "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
NIPQUAD(least->addr), ntohs(least->port),
|
||||
NIPQUAD(least->addr.ip), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
|
@ -250,7 +250,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set)
|
|||
|
||||
IP_VS_DBG(6, "ip_vs_dest_set_max: server %d.%d.%d.%d:%d "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
NIPQUAD(most->addr), ntohs(most->port),
|
||||
NIPQUAD(most->addr.ip), ntohs(most->port),
|
||||
atomic_read(&most->activeconns),
|
||||
atomic_read(&most->refcnt),
|
||||
atomic_read(&most->weight), moh);
|
||||
|
@ -598,7 +598,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc, struct iphdr *iph)
|
|||
|
||||
IP_VS_DBG(6, "LBLCR: server %d.%d.%d.%d:%d "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
NIPQUAD(least->addr), ntohs(least->port),
|
||||
NIPQUAD(least->addr.ip), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
|
@ -706,7 +706,7 @@ out:
|
|||
IP_VS_DBG(6, "LBLCR: destination IP address %u.%u.%u.%u "
|
||||
"--> server %u.%u.%u.%u:%d\n",
|
||||
NIPQUAD(iph->daddr),
|
||||
NIPQUAD(dest->addr),
|
||||
NIPQUAD(dest->addr.ip),
|
||||
ntohs(dest->port));
|
||||
|
||||
return dest;
|
||||
|
@ -722,6 +722,9 @@ static struct ip_vs_scheduler ip_vs_lblcr_scheduler =
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_lblcr_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 0,
|
||||
#endif
|
||||
.init_service = ip_vs_lblcr_init_svc,
|
||||
.done_service = ip_vs_lblcr_done_svc,
|
||||
.schedule = ip_vs_lblcr_schedule,
|
||||
|
|
|
@ -67,10 +67,10 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|||
}
|
||||
|
||||
if (least)
|
||||
IP_VS_DBG(6, "LC: server %u.%u.%u.%u:%u activeconns %d inactconns %d\n",
|
||||
NIPQUAD(least->addr), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->inactconns));
|
||||
IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d inactconns %d\n",
|
||||
IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->inactconns));
|
||||
|
||||
return least;
|
||||
}
|
||||
|
@ -81,6 +81,9 @@ static struct ip_vs_scheduler ip_vs_lc_scheduler = {
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_lc_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 1,
|
||||
#endif
|
||||
.schedule = ip_vs_lc_schedule,
|
||||
};
|
||||
|
||||
|
|
|
@ -99,12 +99,12 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|||
return NULL;
|
||||
|
||||
out:
|
||||
IP_VS_DBG(6, "NQ: server %u.%u.%u.%u:%u "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
NIPQUAD(least->addr), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
IP_VS_DBG_BUF(6, "NQ: server %s:%u "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
|
||||
return least;
|
||||
}
|
||||
|
@ -116,6 +116,9 @@ static struct ip_vs_scheduler ip_vs_nq_scheduler =
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_nq_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 1,
|
||||
#endif
|
||||
.schedule = ip_vs_nq_schedule,
|
||||
};
|
||||
|
||||
|
|
|
@ -151,11 +151,11 @@ const char * ip_vs_state_name(__u16 proto, int state)
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
ip_vs_tcpudp_debug_packet(struct ip_vs_protocol *pp,
|
||||
const struct sk_buff *skb,
|
||||
int offset,
|
||||
const char *msg)
|
||||
static void
|
||||
ip_vs_tcpudp_debug_packet_v4(struct ip_vs_protocol *pp,
|
||||
const struct sk_buff *skb,
|
||||
int offset,
|
||||
const char *msg)
|
||||
{
|
||||
char buf[128];
|
||||
struct iphdr _iph, *ih;
|
||||
|
@ -189,6 +189,61 @@ ip_vs_tcpudp_debug_packet(struct ip_vs_protocol *pp,
|
|||
printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
static void
|
||||
ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp,
|
||||
const struct sk_buff *skb,
|
||||
int offset,
|
||||
const char *msg)
|
||||
{
|
||||
char buf[192];
|
||||
struct ipv6hdr _iph, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
|
||||
if (ih == NULL)
|
||||
sprintf(buf, "%s TRUNCATED", pp->name);
|
||||
else if (ih->nexthdr == IPPROTO_FRAGMENT)
|
||||
sprintf(buf, "%s " NIP6_FMT "->" NIP6_FMT " frag",
|
||||
pp->name, NIP6(ih->saddr),
|
||||
NIP6(ih->daddr));
|
||||
else {
|
||||
__be16 _ports[2], *pptr;
|
||||
|
||||
pptr = skb_header_pointer(skb, offset + sizeof(struct ipv6hdr),
|
||||
sizeof(_ports), _ports);
|
||||
if (pptr == NULL)
|
||||
sprintf(buf, "%s TRUNCATED " NIP6_FMT "->" NIP6_FMT,
|
||||
pp->name,
|
||||
NIP6(ih->saddr),
|
||||
NIP6(ih->daddr));
|
||||
else
|
||||
sprintf(buf, "%s " NIP6_FMT ":%u->" NIP6_FMT ":%u",
|
||||
pp->name,
|
||||
NIP6(ih->saddr),
|
||||
ntohs(pptr[0]),
|
||||
NIP6(ih->daddr),
|
||||
ntohs(pptr[1]));
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
ip_vs_tcpudp_debug_packet(struct ip_vs_protocol *pp,
|
||||
const struct sk_buff *skb,
|
||||
int offset,
|
||||
const char *msg)
|
||||
{
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (skb->protocol == __constant_htons(ETH_P_IPV6))
|
||||
ip_vs_tcpudp_debug_packet_v6(pp, skb, offset, msg);
|
||||
else
|
||||
#endif
|
||||
ip_vs_tcpudp_debug_packet_v4(pp, skb, offset, msg);
|
||||
}
|
||||
|
||||
|
||||
int __init ip_vs_protocol_init(void)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
cp = ip_vs_conn_in_get(af, IPPROTO_UDP,
|
||||
&iph->saddr,
|
||||
htons(PORT_ISAKMP),
|
||||
iph->daddr,
|
||||
&iph->daddr,
|
||||
htons(PORT_ISAKMP));
|
||||
} else {
|
||||
cp = ip_vs_conn_in_get(IPPROTO_UDP,
|
||||
iph->daddr,
|
||||
cp = ip_vs_conn_in_get(af, IPPROTO_UDP,
|
||||
&iph->daddr,
|
||||
htons(PORT_ISAKMP),
|
||||
iph->saddr,
|
||||
&iph->saddr,
|
||||
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",
|
||||
inverse ? "ICMP+" : "",
|
||||
pp->name,
|
||||
NIPQUAD(iph->saddr),
|
||||
NIPQUAD(iph->daddr));
|
||||
IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for outin packet "
|
||||
"%s%s %s->%s\n",
|
||||
inverse ? "ICMP+" : "",
|
||||
pp->name,
|
||||
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,
|
||||
cp = ip_vs_conn_out_get(af, IPPROTO_UDP,
|
||||
&iph->saddr,
|
||||
htons(PORT_ISAKMP),
|
||||
iph->daddr,
|
||||
&iph->daddr,
|
||||
htons(PORT_ISAKMP));
|
||||
} else {
|
||||
cp = ip_vs_conn_out_get(IPPROTO_UDP,
|
||||
iph->daddr,
|
||||
cp = ip_vs_conn_out_get(af, IPPROTO_UDP,
|
||||
&iph->daddr,
|
||||
htons(PORT_ISAKMP),
|
||||
iph->saddr,
|
||||
&iph->saddr,
|
||||
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",
|
||||
inverse ? "ICMP+" : "",
|
||||
pp->name,
|
||||
NIPQUAD(iph->saddr),
|
||||
NIPQUAD(iph->daddr));
|
||||
IP_VS_DBG_BUF(12, "Unknown ISAKMP entry for inout packet "
|
||||
"%s%s %s->%s\n",
|
||||
inverse ? "ICMP+" : "",
|
||||
pp->name,
|
||||
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)
|
||||
{
|
||||
/*
|
||||
|
@ -125,8 +125,8 @@ ah_esp_conn_schedule(struct sk_buff *skb,
|
|||
|
||||
|
||||
static void
|
||||
ah_esp_debug_packet(struct ip_vs_protocol *pp, const struct sk_buff *skb,
|
||||
int offset, const char *msg)
|
||||
ah_esp_debug_packet_v4(struct ip_vs_protocol *pp, const struct sk_buff *skb,
|
||||
int offset, const char *msg)
|
||||
{
|
||||
char buf[256];
|
||||
struct iphdr _iph, *ih;
|
||||
|
@ -142,6 +142,38 @@ ah_esp_debug_packet(struct ip_vs_protocol *pp, const struct sk_buff *skb,
|
|||
printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
static void
|
||||
ah_esp_debug_packet_v6(struct ip_vs_protocol *pp, const struct sk_buff *skb,
|
||||
int offset, const char *msg)
|
||||
{
|
||||
char buf[256];
|
||||
struct ipv6hdr _iph, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
|
||||
if (ih == NULL)
|
||||
sprintf(buf, "%s TRUNCATED", pp->name);
|
||||
else
|
||||
sprintf(buf, "%s " NIP6_FMT "->" NIP6_FMT,
|
||||
pp->name, NIP6(ih->saddr),
|
||||
NIP6(ih->daddr));
|
||||
|
||||
printk(KERN_DEBUG "IPVS: %s: %s\n", msg, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
ah_esp_debug_packet(struct ip_vs_protocol *pp, const struct sk_buff *skb,
|
||||
int offset, const char *msg)
|
||||
{
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (skb->protocol == __constant_htons(ETH_P_IPV6))
|
||||
ah_esp_debug_packet_v6(pp, skb, offset, msg);
|
||||
else
|
||||
#endif
|
||||
ah_esp_debug_packet_v4(pp, skb, offset, msg);
|
||||
}
|
||||
|
||||
|
||||
static void ah_esp_init(struct ip_vs_protocol *pp)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -35,19 +36,20 @@ tcp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
|
|||
return NULL;
|
||||
|
||||
if (likely(!inverse)) {
|
||||
return ip_vs_conn_in_get(iph->protocol,
|
||||
iph->saddr, pptr[0],
|
||||
iph->daddr, pptr[1]);
|
||||
return ip_vs_conn_in_get(af, iph->protocol,
|
||||
&iph->saddr, pptr[0],
|
||||
&iph->daddr, pptr[1]);
|
||||
} else {
|
||||
return ip_vs_conn_in_get(iph->protocol,
|
||||
iph->daddr, pptr[1],
|
||||
iph->saddr, pptr[0]);
|
||||
return ip_vs_conn_in_get(af, iph->protocol,
|
||||
&iph->daddr, pptr[1],
|
||||
&iph->saddr, 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;
|
||||
|
||||
|
@ -56,34 +58,36 @@ tcp_conn_out_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
|
|||
return NULL;
|
||||
|
||||
if (likely(!inverse)) {
|
||||
return ip_vs_conn_out_get(iph->protocol,
|
||||
iph->saddr, pptr[0],
|
||||
iph->daddr, pptr[1]);
|
||||
return ip_vs_conn_out_get(af, iph->protocol,
|
||||
&iph->saddr, pptr[0],
|
||||
&iph->daddr, pptr[1]);
|
||||
} else {
|
||||
return ip_vs_conn_out_get(iph->protocol,
|
||||
iph->daddr, pptr[1],
|
||||
iph->saddr, pptr[0]);
|
||||
return ip_vs_conn_out_get(af, iph->protocol,
|
||||
&iph->daddr, pptr[1],
|
||||
&iph->saddr, 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;
|
||||
|
||||
th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
|
||||
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
|
||||
|
||||
th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL) {
|
||||
*verdict = NF_DROP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (th->syn &&
|
||||
(svc = ip_vs_service_get(skb->mark, ip_hdr(skb)->protocol,
|
||||
ip_hdr(skb)->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.
|
||||
|
@ -110,22 +114,62 @@ tcp_conn_schedule(struct sk_buff *skb,
|
|||
|
||||
|
||||
static inline void
|
||||
tcp_fast_csum_update(struct tcphdr *tcph, __be32 oldip, __be32 newip,
|
||||
tcp_fast_csum_update(int af, struct tcphdr *tcph,
|
||||
const union nf_inet_addr *oldip,
|
||||
const union nf_inet_addr *newip,
|
||||
__be16 oldport, __be16 newport)
|
||||
{
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
tcph->check =
|
||||
csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6,
|
||||
ip_vs_check_diff2(oldport, newport,
|
||||
~csum_unfold(tcph->check))));
|
||||
else
|
||||
#endif
|
||||
tcph->check =
|
||||
csum_fold(ip_vs_check_diff4(oldip, newip,
|
||||
csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip,
|
||||
ip_vs_check_diff2(oldport, newport,
|
||||
~csum_unfold(tcph->check))));
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
tcp_partial_csum_update(int af, struct tcphdr *tcph,
|
||||
const union nf_inet_addr *oldip,
|
||||
const union nf_inet_addr *newip,
|
||||
__be16 oldlen, __be16 newlen)
|
||||
{
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
tcph->check =
|
||||
csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6,
|
||||
ip_vs_check_diff2(oldlen, newlen,
|
||||
~csum_unfold(tcph->check))));
|
||||
else
|
||||
#endif
|
||||
tcph->check =
|
||||
csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip,
|
||||
ip_vs_check_diff2(oldlen, newlen,
|
||||
~csum_unfold(tcph->check))));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
tcp_snat_handler(struct sk_buff *skb,
|
||||
struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
|
||||
{
|
||||
struct tcphdr *tcph;
|
||||
const unsigned int tcphoff = ip_hdrlen(skb);
|
||||
unsigned int tcphoff;
|
||||
int oldlen;
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
tcphoff = sizeof(struct ipv6hdr);
|
||||
else
|
||||
#endif
|
||||
tcphoff = ip_hdrlen(skb);
|
||||
oldlen = skb->len - tcphoff;
|
||||
|
||||
/* csum_check requires unshared skb */
|
||||
if (!skb_make_writable(skb, tcphoff+sizeof(*tcph)))
|
||||
|
@ -133,7 +177,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(cp->af, skb, pp))
|
||||
return 0;
|
||||
|
||||
/* Call application helper if needed */
|
||||
|
@ -141,13 +185,17 @@ tcp_snat_handler(struct sk_buff *skb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
tcph = (void *)ip_hdr(skb) + tcphoff;
|
||||
tcph = (void *)skb_network_header(skb) + tcphoff;
|
||||
tcph->source = cp->vport;
|
||||
|
||||
/* Adjust TCP checksums */
|
||||
if (!cp->app) {
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
|
||||
htonl(oldlen),
|
||||
htonl(skb->len - tcphoff));
|
||||
} else if (!cp->app) {
|
||||
/* Only port and addr are changed, do fast csum update */
|
||||
tcp_fast_csum_update(tcph, cp->daddr, cp->vaddr,
|
||||
tcp_fast_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
|
||||
cp->dport, cp->vport);
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
@ -155,9 +203,20 @@ tcp_snat_handler(struct sk_buff *skb,
|
|||
/* full checksum calculation */
|
||||
tcph->check = 0;
|
||||
skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
|
||||
tcph->check = csum_tcpudp_magic(cp->vaddr, cp->caddr,
|
||||
skb->len - tcphoff,
|
||||
cp->protocol, skb->csum);
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
tcph->check = csum_ipv6_magic(&cp->vaddr.in6,
|
||||
&cp->caddr.in6,
|
||||
skb->len - tcphoff,
|
||||
cp->protocol, skb->csum);
|
||||
else
|
||||
#endif
|
||||
tcph->check = csum_tcpudp_magic(cp->vaddr.ip,
|
||||
cp->caddr.ip,
|
||||
skb->len - tcphoff,
|
||||
cp->protocol,
|
||||
skb->csum);
|
||||
|
||||
IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
|
||||
pp->name, tcph->check,
|
||||
(char*)&(tcph->check) - (char*)tcph);
|
||||
|
@ -171,7 +230,16 @@ tcp_dnat_handler(struct sk_buff *skb,
|
|||
struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
|
||||
{
|
||||
struct tcphdr *tcph;
|
||||
const unsigned int tcphoff = ip_hdrlen(skb);
|
||||
unsigned int tcphoff;
|
||||
int oldlen;
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
tcphoff = sizeof(struct ipv6hdr);
|
||||
else
|
||||
#endif
|
||||
tcphoff = ip_hdrlen(skb);
|
||||
oldlen = skb->len - tcphoff;
|
||||
|
||||
/* csum_check requires unshared skb */
|
||||
if (!skb_make_writable(skb, tcphoff+sizeof(*tcph)))
|
||||
|
@ -179,7 +247,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(cp->af, skb, pp))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -190,15 +258,19 @@ tcp_dnat_handler(struct sk_buff *skb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
tcph = (void *)ip_hdr(skb) + tcphoff;
|
||||
tcph = (void *)skb_network_header(skb) + tcphoff;
|
||||
tcph->dest = cp->dport;
|
||||
|
||||
/*
|
||||
* Adjust TCP checksums
|
||||
*/
|
||||
if (!cp->app) {
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
tcp_partial_csum_update(cp->af, tcph, &cp->daddr, &cp->vaddr,
|
||||
htonl(oldlen),
|
||||
htonl(skb->len - tcphoff));
|
||||
} else if (!cp->app) {
|
||||
/* Only port and addr are changed, do fast csum update */
|
||||
tcp_fast_csum_update(tcph, cp->vaddr, cp->daddr,
|
||||
tcp_fast_csum_update(cp->af, tcph, &cp->vaddr, &cp->daddr,
|
||||
cp->vport, cp->dport);
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
@ -206,9 +278,19 @@ tcp_dnat_handler(struct sk_buff *skb,
|
|||
/* full checksum calculation */
|
||||
tcph->check = 0;
|
||||
skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
|
||||
tcph->check = csum_tcpudp_magic(cp->caddr, cp->daddr,
|
||||
skb->len - tcphoff,
|
||||
cp->protocol, skb->csum);
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
tcph->check = csum_ipv6_magic(&cp->caddr.in6,
|
||||
&cp->daddr.in6,
|
||||
skb->len - tcphoff,
|
||||
cp->protocol, skb->csum);
|
||||
else
|
||||
#endif
|
||||
tcph->check = csum_tcpudp_magic(cp->caddr.ip,
|
||||
cp->daddr.ip,
|
||||
skb->len - tcphoff,
|
||||
cp->protocol,
|
||||
skb->csum);
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
}
|
||||
return 1;
|
||||
|
@ -216,21 +298,43 @@ 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,
|
||||
skb->len - tcphoff,
|
||||
ip_hdr(skb)->protocol, skb->csum)) {
|
||||
IP_VS_DBG_RL_PKT(0, pp, skb, 0,
|
||||
"Failed checksum for");
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6) {
|
||||
if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
|
||||
&ipv6_hdr(skb)->daddr,
|
||||
skb->len - tcphoff,
|
||||
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;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* No need to checksum. */
|
||||
|
@ -419,19 +523,23 @@ set_tcp_state(struct ip_vs_protocol *pp, struct ip_vs_conn *cp,
|
|||
if (new_state != cp->state) {
|
||||
struct ip_vs_dest *dest = cp->dest;
|
||||
|
||||
IP_VS_DBG(8, "%s %s [%c%c%c%c] %u.%u.%u.%u:%d->"
|
||||
"%u.%u.%u.%u:%d state: %s->%s conn->refcnt:%d\n",
|
||||
pp->name,
|
||||
(state_off==TCP_DIR_OUTPUT)?"output ":"input ",
|
||||
th->syn? 'S' : '.',
|
||||
th->fin? 'F' : '.',
|
||||
th->ack? 'A' : '.',
|
||||
th->rst? 'R' : '.',
|
||||
NIPQUAD(cp->daddr), ntohs(cp->dport),
|
||||
NIPQUAD(cp->caddr), ntohs(cp->cport),
|
||||
tcp_state_name(cp->state),
|
||||
tcp_state_name(new_state),
|
||||
atomic_read(&cp->refcnt));
|
||||
IP_VS_DBG_BUF(8, "%s %s [%c%c%c%c] %s:%d->"
|
||||
"%s:%d state: %s->%s conn->refcnt:%d\n",
|
||||
pp->name,
|
||||
((state_off == TCP_DIR_OUTPUT) ?
|
||||
"output " : "input "),
|
||||
th->syn ? 'S' : '.',
|
||||
th->fin ? 'F' : '.',
|
||||
th->ack ? 'A' : '.',
|
||||
th->rst ? 'R' : '.',
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->daddr),
|
||||
ntohs(cp->dport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr),
|
||||
ntohs(cp->cport),
|
||||
tcp_state_name(cp->state),
|
||||
tcp_state_name(new_state),
|
||||
atomic_read(&cp->refcnt));
|
||||
|
||||
if (dest) {
|
||||
if (!(cp->flags & IP_VS_CONN_F_INACTIVE) &&
|
||||
(new_state != IP_VS_TCP_S_ESTABLISHED)) {
|
||||
|
@ -461,7 +569,13 @@ tcp_state_transition(struct ip_vs_conn *cp, int direction,
|
|||
{
|
||||
struct tcphdr _tcph, *th;
|
||||
|
||||
th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
int ihl = cp->af == AF_INET ? ip_hdrlen(skb) : sizeof(struct ipv6hdr);
|
||||
#else
|
||||
int ihl = ip_hdrlen(skb);
|
||||
#endif
|
||||
|
||||
th = skb_header_pointer(skb, ihl, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL)
|
||||
return 0;
|
||||
|
||||
|
@ -546,12 +660,15 @@ tcp_app_conn_bind(struct ip_vs_conn *cp)
|
|||
break;
|
||||
spin_unlock(&tcp_app_lock);
|
||||
|
||||
IP_VS_DBG(9, "%s: Binding conn %u.%u.%u.%u:%u->"
|
||||
"%u.%u.%u.%u:%u to app %s on port %u\n",
|
||||
__func__,
|
||||
NIPQUAD(cp->caddr), ntohs(cp->cport),
|
||||
NIPQUAD(cp->vaddr), ntohs(cp->vport),
|
||||
inc->name, ntohs(inc->port));
|
||||
IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->"
|
||||
"%s:%u to app %s on port %u\n",
|
||||
__func__,
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr),
|
||||
ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->vaddr),
|
||||
ntohs(cp->vport),
|
||||
inc->name, ntohs(inc->port));
|
||||
|
||||
cp->app = inc;
|
||||
if (inc->init_conn)
|
||||
result = inc->init_conn(inc, cp);
|
||||
|
|
|
@ -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;
|
||||
|
@ -35,13 +36,13 @@ udp_conn_in_get(const struct sk_buff *skb, struct ip_vs_protocol *pp,
|
|||
return NULL;
|
||||
|
||||
if (likely(!inverse)) {
|
||||
cp = ip_vs_conn_in_get(iph->protocol,
|
||||
iph->saddr, pptr[0],
|
||||
iph->daddr, pptr[1]);
|
||||
cp = ip_vs_conn_in_get(af, iph->protocol,
|
||||
&iph->saddr, pptr[0],
|
||||
&iph->daddr, pptr[1]);
|
||||
} else {
|
||||
cp = ip_vs_conn_in_get(iph->protocol,
|
||||
iph->daddr, pptr[1],
|
||||
iph->saddr, pptr[0]);
|
||||
cp = ip_vs_conn_in_get(af, iph->protocol,
|
||||
&iph->daddr, pptr[1],
|
||||
&iph->saddr, 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]);
|
||||
cp = ip_vs_conn_out_get(af, iph->protocol,
|
||||
&iph->saddr, pptr[0],
|
||||
&iph->daddr, pptr[1]);
|
||||
} else {
|
||||
cp = ip_vs_conn_out_get(iph->protocol,
|
||||
iph->daddr, pptr[1],
|
||||
iph->saddr, pptr[0]);
|
||||
cp = ip_vs_conn_out_get(af, iph->protocol,
|
||||
&iph->daddr, pptr[1],
|
||||
&iph->saddr, pptr[0]);
|
||||
}
|
||||
|
||||
return cp;
|
||||
|
@ -75,21 +76,24 @@ 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;
|
||||
|
||||
uh = skb_header_pointer(skb, ip_hdrlen(skb),
|
||||
sizeof(_udph), &_udph);
|
||||
ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
|
||||
|
||||
uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph);
|
||||
if (uh == NULL) {
|
||||
*verdict = NF_DROP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((svc = ip_vs_service_get(skb->mark, ip_hdr(skb)->protocol,
|
||||
ip_hdr(skb)->daddr, uh->dest))) {
|
||||
svc = ip_vs_service_get(af, skb->mark, iph.protocol,
|
||||
&iph.daddr, uh->dest);
|
||||
if (svc) {
|
||||
if (ip_vs_todrop()) {
|
||||
/*
|
||||
* It seems that we are very loaded.
|
||||
|
@ -116,23 +120,63 @@ udp_conn_schedule(struct sk_buff *skb, struct ip_vs_protocol *pp,
|
|||
|
||||
|
||||
static inline void
|
||||
udp_fast_csum_update(struct udphdr *uhdr, __be32 oldip, __be32 newip,
|
||||
udp_fast_csum_update(int af, struct udphdr *uhdr,
|
||||
const union nf_inet_addr *oldip,
|
||||
const union nf_inet_addr *newip,
|
||||
__be16 oldport, __be16 newport)
|
||||
{
|
||||
uhdr->check =
|
||||
csum_fold(ip_vs_check_diff4(oldip, newip,
|
||||
ip_vs_check_diff2(oldport, newport,
|
||||
~csum_unfold(uhdr->check))));
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
uhdr->check =
|
||||
csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6,
|
||||
ip_vs_check_diff2(oldport, newport,
|
||||
~csum_unfold(uhdr->check))));
|
||||
else
|
||||
#endif
|
||||
uhdr->check =
|
||||
csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip,
|
||||
ip_vs_check_diff2(oldport, newport,
|
||||
~csum_unfold(uhdr->check))));
|
||||
if (!uhdr->check)
|
||||
uhdr->check = CSUM_MANGLED_0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
udp_partial_csum_update(int af, struct udphdr *uhdr,
|
||||
const union nf_inet_addr *oldip,
|
||||
const union nf_inet_addr *newip,
|
||||
__be16 oldlen, __be16 newlen)
|
||||
{
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (af == AF_INET6)
|
||||
uhdr->check =
|
||||
csum_fold(ip_vs_check_diff16(oldip->ip6, newip->ip6,
|
||||
ip_vs_check_diff2(oldlen, newlen,
|
||||
~csum_unfold(uhdr->check))));
|
||||
else
|
||||
#endif
|
||||
uhdr->check =
|
||||
csum_fold(ip_vs_check_diff4(oldip->ip, newip->ip,
|
||||
ip_vs_check_diff2(oldlen, newlen,
|
||||
~csum_unfold(uhdr->check))));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
udp_snat_handler(struct sk_buff *skb,
|
||||
struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
|
||||
{
|
||||
struct udphdr *udph;
|
||||
const unsigned int udphoff = ip_hdrlen(skb);
|
||||
unsigned int udphoff;
|
||||
int oldlen;
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
udphoff = sizeof(struct ipv6hdr);
|
||||
else
|
||||
#endif
|
||||
udphoff = ip_hdrlen(skb);
|
||||
oldlen = skb->len - udphoff;
|
||||
|
||||
/* csum_check requires unshared skb */
|
||||
if (!skb_make_writable(skb, udphoff+sizeof(*udph)))
|
||||
|
@ -140,7 +184,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(cp->af, skb, pp))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -150,15 +194,19 @@ udp_snat_handler(struct sk_buff *skb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
udph = (void *)ip_hdr(skb) + udphoff;
|
||||
udph = (void *)skb_network_header(skb) + udphoff;
|
||||
udph->source = cp->vport;
|
||||
|
||||
/*
|
||||
* Adjust UDP checksums
|
||||
*/
|
||||
if (!cp->app && (udph->check != 0)) {
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
|
||||
htonl(oldlen),
|
||||
htonl(skb->len - udphoff));
|
||||
} else if (!cp->app && (udph->check != 0)) {
|
||||
/* Only port and addr are changed, do fast csum update */
|
||||
udp_fast_csum_update(udph, cp->daddr, cp->vaddr,
|
||||
udp_fast_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
|
||||
cp->dport, cp->vport);
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
@ -166,9 +214,19 @@ udp_snat_handler(struct sk_buff *skb,
|
|||
/* full checksum calculation */
|
||||
udph->check = 0;
|
||||
skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0);
|
||||
udph->check = csum_tcpudp_magic(cp->vaddr, cp->caddr,
|
||||
skb->len - udphoff,
|
||||
cp->protocol, skb->csum);
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
udph->check = csum_ipv6_magic(&cp->vaddr.in6,
|
||||
&cp->caddr.in6,
|
||||
skb->len - udphoff,
|
||||
cp->protocol, skb->csum);
|
||||
else
|
||||
#endif
|
||||
udph->check = csum_tcpudp_magic(cp->vaddr.ip,
|
||||
cp->caddr.ip,
|
||||
skb->len - udphoff,
|
||||
cp->protocol,
|
||||
skb->csum);
|
||||
if (udph->check == 0)
|
||||
udph->check = CSUM_MANGLED_0;
|
||||
IP_VS_DBG(11, "O-pkt: %s O-csum=%d (+%zd)\n",
|
||||
|
@ -184,7 +242,16 @@ udp_dnat_handler(struct sk_buff *skb,
|
|||
struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
|
||||
{
|
||||
struct udphdr *udph;
|
||||
unsigned int udphoff = ip_hdrlen(skb);
|
||||
unsigned int udphoff;
|
||||
int oldlen;
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
udphoff = sizeof(struct ipv6hdr);
|
||||
else
|
||||
#endif
|
||||
udphoff = ip_hdrlen(skb);
|
||||
oldlen = skb->len - udphoff;
|
||||
|
||||
/* csum_check requires unshared skb */
|
||||
if (!skb_make_writable(skb, udphoff+sizeof(*udph)))
|
||||
|
@ -192,7 +259,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(cp->af, skb, pp))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -203,15 +270,19 @@ udp_dnat_handler(struct sk_buff *skb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
udph = (void *)ip_hdr(skb) + udphoff;
|
||||
udph = (void *)skb_network_header(skb) + udphoff;
|
||||
udph->dest = cp->dport;
|
||||
|
||||
/*
|
||||
* Adjust UDP checksums
|
||||
*/
|
||||
if (!cp->app && (udph->check != 0)) {
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
udp_partial_csum_update(cp->af, udph, &cp->daddr, &cp->vaddr,
|
||||
htonl(oldlen),
|
||||
htonl(skb->len - udphoff));
|
||||
} else if (!cp->app && (udph->check != 0)) {
|
||||
/* Only port and addr are changed, do fast csum update */
|
||||
udp_fast_csum_update(udph, cp->vaddr, cp->daddr,
|
||||
udp_fast_csum_update(cp->af, udph, &cp->vaddr, &cp->daddr,
|
||||
cp->vport, cp->dport);
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
@ -219,9 +290,19 @@ udp_dnat_handler(struct sk_buff *skb,
|
|||
/* full checksum calculation */
|
||||
udph->check = 0;
|
||||
skb->csum = skb_checksum(skb, udphoff, skb->len - udphoff, 0);
|
||||
udph->check = csum_tcpudp_magic(cp->caddr, cp->daddr,
|
||||
skb->len - udphoff,
|
||||
cp->protocol, skb->csum);
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
if (cp->af == AF_INET6)
|
||||
udph->check = csum_ipv6_magic(&cp->caddr.in6,
|
||||
&cp->daddr.in6,
|
||||
skb->len - udphoff,
|
||||
cp->protocol, skb->csum);
|
||||
else
|
||||
#endif
|
||||
udph->check = csum_tcpudp_magic(cp->caddr.ip,
|
||||
cp->daddr.ip,
|
||||
skb->len - udphoff,
|
||||
cp->protocol,
|
||||
skb->csum);
|
||||
if (udph->check == 0)
|
||||
udph->check = CSUM_MANGLED_0;
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
|
@ -231,10 +312,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)
|
||||
|
@ -246,15 +334,28 @@ 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:
|
||||
if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
|
||||
ip_hdr(skb)->daddr,
|
||||
skb->len - udphoff,
|
||||
ip_hdr(skb)->protocol,
|
||||
skb->csum)) {
|
||||
IP_VS_DBG_RL_PKT(0, pp, skb, 0,
|
||||
"Failed checksum for");
|
||||
return 0;
|
||||
}
|
||||
#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,
|
||||
ip_hdr(skb)->protocol,
|
||||
skb->csum)) {
|
||||
IP_VS_DBG_RL_PKT(0, pp, skb, 0,
|
||||
"Failed checksum for");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* No need to checksum. */
|
||||
|
@ -340,12 +441,15 @@ static int udp_app_conn_bind(struct ip_vs_conn *cp)
|
|||
break;
|
||||
spin_unlock(&udp_app_lock);
|
||||
|
||||
IP_VS_DBG(9, "%s: Binding conn %u.%u.%u.%u:%u->"
|
||||
"%u.%u.%u.%u:%u to app %s on port %u\n",
|
||||
__func__,
|
||||
NIPQUAD(cp->caddr), ntohs(cp->cport),
|
||||
NIPQUAD(cp->vaddr), ntohs(cp->vport),
|
||||
inc->name, ntohs(inc->port));
|
||||
IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->"
|
||||
"%s:%u to app %s on port %u\n",
|
||||
__func__,
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->caddr),
|
||||
ntohs(cp->cport),
|
||||
IP_VS_DBG_ADDR(cp->af, &cp->vaddr),
|
||||
ntohs(cp->vport),
|
||||
inc->name, ntohs(inc->port));
|
||||
|
||||
cp->app = inc;
|
||||
if (inc->init_conn)
|
||||
result = inc->init_conn(inc, cp);
|
||||
|
|
|
@ -74,11 +74,11 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|||
out:
|
||||
svc->sched_data = q;
|
||||
write_unlock(&svc->sched_lock);
|
||||
IP_VS_DBG(6, "RR: server %u.%u.%u.%u:%u "
|
||||
"activeconns %d refcnt %d weight %d\n",
|
||||
NIPQUAD(dest->addr), ntohs(dest->port),
|
||||
atomic_read(&dest->activeconns),
|
||||
atomic_read(&dest->refcnt), atomic_read(&dest->weight));
|
||||
IP_VS_DBG_BUF(6, "RR: server %s:%u "
|
||||
"activeconns %d refcnt %d weight %d\n",
|
||||
IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port),
|
||||
atomic_read(&dest->activeconns),
|
||||
atomic_read(&dest->refcnt), atomic_read(&dest->weight));
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
@ -89,6 +89,9 @@ static struct ip_vs_scheduler ip_vs_rr_scheduler = {
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 1,
|
||||
#endif
|
||||
.init_service = ip_vs_rr_init_svc,
|
||||
.update_service = ip_vs_rr_update_svc,
|
||||
.schedule = ip_vs_rr_schedule,
|
||||
|
|
|
@ -101,12 +101,12 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|||
}
|
||||
}
|
||||
|
||||
IP_VS_DBG(6, "SED: server %u.%u.%u.%u:%u "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
NIPQUAD(least->addr), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
IP_VS_DBG_BUF(6, "SED: server %s:%u "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
|
||||
return least;
|
||||
}
|
||||
|
@ -118,6 +118,9 @@ static struct ip_vs_scheduler ip_vs_sed_scheduler =
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_sed_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 1,
|
||||
#endif
|
||||
.schedule = ip_vs_sed_schedule,
|
||||
};
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|||
IP_VS_DBG(6, "SH: source IP address %u.%u.%u.%u "
|
||||
"--> server %u.%u.%u.%u:%d\n",
|
||||
NIPQUAD(iph->saddr),
|
||||
NIPQUAD(dest->addr),
|
||||
NIPQUAD(dest->addr.ip),
|
||||
ntohs(dest->port));
|
||||
|
||||
return dest;
|
||||
|
@ -231,6 +231,9 @@ static struct ip_vs_scheduler ip_vs_sh_scheduler =
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_sh_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 0,
|
||||
#endif
|
||||
.init_service = ip_vs_sh_init_svc,
|
||||
.done_service = ip_vs_sh_done_svc,
|
||||
.update_service = ip_vs_sh_update_svc,
|
||||
|
|
|
@ -256,9 +256,9 @@ void ip_vs_sync_conn(struct ip_vs_conn *cp)
|
|||
s->cport = cp->cport;
|
||||
s->vport = cp->vport;
|
||||
s->dport = cp->dport;
|
||||
s->caddr = cp->caddr;
|
||||
s->vaddr = cp->vaddr;
|
||||
s->daddr = cp->daddr;
|
||||
s->caddr = cp->caddr.ip;
|
||||
s->vaddr = cp->vaddr.ip;
|
||||
s->daddr = cp->daddr.ip;
|
||||
s->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED);
|
||||
s->state = htons(cp->state);
|
||||
if (cp->flags & IP_VS_CONN_F_SEQ_MASK) {
|
||||
|
@ -366,21 +366,28 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
|
|||
}
|
||||
|
||||
if (!(flags & IP_VS_CONN_F_TEMPLATE))
|
||||
cp = ip_vs_conn_in_get(s->protocol,
|
||||
s->caddr, s->cport,
|
||||
s->vaddr, s->vport);
|
||||
cp = ip_vs_conn_in_get(AF_INET, s->protocol,
|
||||
(union nf_inet_addr *)&s->caddr,
|
||||
s->cport,
|
||||
(union nf_inet_addr *)&s->vaddr,
|
||||
s->vport);
|
||||
else
|
||||
cp = ip_vs_ct_in_get(s->protocol,
|
||||
s->caddr, s->cport,
|
||||
s->vaddr, s->vport);
|
||||
cp = ip_vs_ct_in_get(AF_INET, s->protocol,
|
||||
(union nf_inet_addr *)&s->caddr,
|
||||
s->cport,
|
||||
(union nf_inet_addr *)&s->vaddr,
|
||||
s->vport);
|
||||
if (!cp) {
|
||||
/*
|
||||
* Find the appropriate destination for the connection.
|
||||
* If it is not found the connection will remain unbound
|
||||
* but still handled.
|
||||
*/
|
||||
dest = ip_vs_find_dest(s->daddr, s->dport,
|
||||
s->vaddr, s->vport,
|
||||
dest = ip_vs_find_dest(AF_INET,
|
||||
(union nf_inet_addr *)&s->daddr,
|
||||
s->dport,
|
||||
(union nf_inet_addr *)&s->vaddr,
|
||||
s->vport,
|
||||
s->protocol);
|
||||
/* Set the approprite ativity flag */
|
||||
if (s->protocol == IPPROTO_TCP) {
|
||||
|
@ -389,10 +396,13 @@ static void ip_vs_process_message(const char *buffer, const size_t buflen)
|
|||
else
|
||||
flags &= ~IP_VS_CONN_F_INACTIVE;
|
||||
}
|
||||
cp = ip_vs_conn_new(s->protocol,
|
||||
s->caddr, s->cport,
|
||||
s->vaddr, s->vport,
|
||||
s->daddr, s->dport,
|
||||
cp = ip_vs_conn_new(AF_INET, s->protocol,
|
||||
(union nf_inet_addr *)&s->caddr,
|
||||
s->cport,
|
||||
(union nf_inet_addr *)&s->vaddr,
|
||||
s->vport,
|
||||
(union nf_inet_addr *)&s->daddr,
|
||||
s->dport,
|
||||
flags, dest);
|
||||
if (dest)
|
||||
atomic_dec(&dest->refcnt);
|
||||
|
|
|
@ -89,12 +89,12 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|||
}
|
||||
}
|
||||
|
||||
IP_VS_DBG(6, "WLC: server %u.%u.%u.%u:%u "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
NIPQUAD(least->addr), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
IP_VS_DBG_BUF(6, "WLC: server %s:%u "
|
||||
"activeconns %d refcnt %d weight %d overhead %d\n",
|
||||
IP_VS_DBG_ADDR(svc->af, &least->addr), ntohs(least->port),
|
||||
atomic_read(&least->activeconns),
|
||||
atomic_read(&least->refcnt),
|
||||
atomic_read(&least->weight), loh);
|
||||
|
||||
return least;
|
||||
}
|
||||
|
@ -106,6 +106,9 @@ static struct ip_vs_scheduler ip_vs_wlc_scheduler =
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_wlc_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 1,
|
||||
#endif
|
||||
.schedule = ip_vs_wlc_schedule,
|
||||
};
|
||||
|
||||
|
|
|
@ -195,12 +195,12 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
|
|||
}
|
||||
}
|
||||
|
||||
IP_VS_DBG(6, "WRR: server %u.%u.%u.%u:%u "
|
||||
"activeconns %d refcnt %d weight %d\n",
|
||||
NIPQUAD(dest->addr), ntohs(dest->port),
|
||||
atomic_read(&dest->activeconns),
|
||||
atomic_read(&dest->refcnt),
|
||||
atomic_read(&dest->weight));
|
||||
IP_VS_DBG_BUF(6, "WRR: server %s:%u "
|
||||
"activeconns %d refcnt %d weight %d\n",
|
||||
IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port),
|
||||
atomic_read(&dest->activeconns),
|
||||
atomic_read(&dest->refcnt),
|
||||
atomic_read(&dest->weight));
|
||||
|
||||
out:
|
||||
write_unlock(&svc->sched_lock);
|
||||
|
@ -213,6 +213,9 @@ static struct ip_vs_scheduler ip_vs_wrr_scheduler = {
|
|||
.refcnt = ATOMIC_INIT(0),
|
||||
.module = THIS_MODULE,
|
||||
.n_list = LIST_HEAD_INIT(ip_vs_wrr_scheduler.n_list),
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
.supports_ipv6 = 1,
|
||||
#endif
|
||||
.init_service = ip_vs_wrr_init_svc,
|
||||
.done_service = ip_vs_wrr_done_svc,
|
||||
.update_service = ip_vs_wrr_update_svc,
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#include <net/udp.h>
|
||||
#include <net/icmp.h> /* for icmp_send */
|
||||
#include <net/route.h> /* for ip_route_output */
|
||||
#include <net/ipv6.h>
|
||||
#include <net/ip6_route.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
|
||||
|
@ -47,7 +50,8 @@ __ip_vs_dst_check(struct ip_vs_dest *dest, u32 rtos, u32 cookie)
|
|||
|
||||
if (!dst)
|
||||
return NULL;
|
||||
if ((dst->obsolete || rtos != dest->dst_rtos) &&
|
||||
if ((dst->obsolete
|
||||
|| (dest->af == AF_INET && rtos != dest->dst_rtos)) &&
|
||||
dst->ops->check(dst, cookie) == NULL) {
|
||||
dest->dst_cache = NULL;
|
||||
dst_release(dst);
|
||||
|
@ -71,7 +75,7 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos)
|
|||
.oif = 0,
|
||||
.nl_u = {
|
||||
.ip4_u = {
|
||||
.daddr = dest->addr,
|
||||
.daddr = dest->addr.ip,
|
||||
.saddr = 0,
|
||||
.tos = rtos, } },
|
||||
};
|
||||
|
@ -80,12 +84,12 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos)
|
|||
spin_unlock(&dest->dst_lock);
|
||||
IP_VS_DBG_RL("ip_route_output error, "
|
||||
"dest: %u.%u.%u.%u\n",
|
||||
NIPQUAD(dest->addr));
|
||||
NIPQUAD(dest->addr.ip));
|
||||
return NULL;
|
||||
}
|
||||
__ip_vs_dst_set(dest, rtos, dst_clone(&rt->u.dst));
|
||||
IP_VS_DBG(10, "new dst %u.%u.%u.%u, refcnt=%d, rtos=%X\n",
|
||||
NIPQUAD(dest->addr),
|
||||
NIPQUAD(dest->addr.ip),
|
||||
atomic_read(&rt->u.dst.__refcnt), rtos);
|
||||
}
|
||||
spin_unlock(&dest->dst_lock);
|
||||
|
@ -94,14 +98,14 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos)
|
|||
.oif = 0,
|
||||
.nl_u = {
|
||||
.ip4_u = {
|
||||
.daddr = cp->daddr,
|
||||
.daddr = cp->daddr.ip,
|
||||
.saddr = 0,
|
||||
.tos = rtos, } },
|
||||
};
|
||||
|
||||
if (ip_route_output_key(&init_net, &rt, &fl)) {
|
||||
IP_VS_DBG_RL("ip_route_output error, dest: "
|
||||
"%u.%u.%u.%u\n", NIPQUAD(cp->daddr));
|
||||
"%u.%u.%u.%u\n", NIPQUAD(cp->daddr.ip));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +113,70 @@ __ip_vs_get_out_rt(struct ip_vs_conn *cp, u32 rtos)
|
|||
return rt;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
static struct rt6_info *
|
||||
__ip_vs_get_out_rt_v6(struct ip_vs_conn *cp)
|
||||
{
|
||||
struct rt6_info *rt; /* Route to the other host */
|
||||
struct ip_vs_dest *dest = cp->dest;
|
||||
|
||||
if (dest) {
|
||||
spin_lock(&dest->dst_lock);
|
||||
rt = (struct rt6_info *)__ip_vs_dst_check(dest, 0, 0);
|
||||
if (!rt) {
|
||||
struct flowi fl = {
|
||||
.oif = 0,
|
||||
.nl_u = {
|
||||
.ip6_u = {
|
||||
.daddr = dest->addr.in6,
|
||||
.saddr = {
|
||||
.s6_addr32 =
|
||||
{ 0, 0, 0, 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
rt = (struct rt6_info *)ip6_route_output(&init_net,
|
||||
NULL, &fl);
|
||||
if (!rt) {
|
||||
spin_unlock(&dest->dst_lock);
|
||||
IP_VS_DBG_RL("ip6_route_output error, "
|
||||
"dest: " NIP6_FMT "\n",
|
||||
NIP6(dest->addr.in6));
|
||||
return NULL;
|
||||
}
|
||||
__ip_vs_dst_set(dest, 0, dst_clone(&rt->u.dst));
|
||||
IP_VS_DBG(10, "new dst " NIP6_FMT ", refcnt=%d\n",
|
||||
NIP6(dest->addr.in6),
|
||||
atomic_read(&rt->u.dst.__refcnt));
|
||||
}
|
||||
spin_unlock(&dest->dst_lock);
|
||||
} else {
|
||||
struct flowi fl = {
|
||||
.oif = 0,
|
||||
.nl_u = {
|
||||
.ip6_u = {
|
||||
.daddr = cp->daddr.in6,
|
||||
.saddr = {
|
||||
.s6_addr32 = { 0, 0, 0, 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
|
||||
if (!rt) {
|
||||
IP_VS_DBG_RL("ip6_route_output error, dest: "
|
||||
NIP6_FMT "\n", NIP6(cp->daddr.in6));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Release dest->dst_cache before a dest is removed
|
||||
|
@ -123,11 +191,11 @@ ip_vs_dst_reset(struct ip_vs_dest *dest)
|
|||
dst_release(old_dst);
|
||||
}
|
||||
|
||||
#define IP_VS_XMIT(skb, rt) \
|
||||
#define IP_VS_XMIT(pf, skb, rt) \
|
||||
do { \
|
||||
(skb)->ipvs_property = 1; \
|
||||
skb_forward_csum(skb); \
|
||||
NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, (skb), NULL, \
|
||||
NF_HOOK(pf, NF_INET_LOCAL_OUT, (skb), NULL, \
|
||||
(rt)->u.dst.dev, dst_output); \
|
||||
} while (0)
|
||||
|
||||
|
@ -200,7 +268,7 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
IP_VS_XMIT(skb, rt);
|
||||
IP_VS_XMIT(PF_INET, skb, rt);
|
||||
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
|
@ -213,6 +281,70 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
int
|
||||
ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp)
|
||||
{
|
||||
struct rt6_info *rt; /* Route to the other host */
|
||||
struct ipv6hdr *iph = ipv6_hdr(skb);
|
||||
int mtu;
|
||||
struct flowi fl = {
|
||||
.oif = 0,
|
||||
.nl_u = {
|
||||
.ip6_u = {
|
||||
.daddr = iph->daddr,
|
||||
.saddr = { .s6_addr32 = {0, 0, 0, 0} }, } },
|
||||
};
|
||||
|
||||
EnterFunction(10);
|
||||
|
||||
rt = (struct rt6_info *)ip6_route_output(&init_net, NULL, &fl);
|
||||
if (!rt) {
|
||||
IP_VS_DBG_RL("ip_vs_bypass_xmit_v6(): ip6_route_output error, "
|
||||
"dest: " NIP6_FMT "\n", NIP6(iph->daddr));
|
||||
goto tx_error_icmp;
|
||||
}
|
||||
|
||||
/* MTU checking */
|
||||
mtu = dst_mtu(&rt->u.dst);
|
||||
if (skb->len > mtu) {
|
||||
dst_release(&rt->u.dst);
|
||||
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
|
||||
IP_VS_DBG_RL("ip_vs_bypass_xmit_v6(): frag needed\n");
|
||||
goto tx_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call ip_send_check because we are not sure it is called
|
||||
* after ip_defrag. Is copy-on-write needed?
|
||||
*/
|
||||
skb = skb_share_check(skb, GFP_ATOMIC);
|
||||
if (unlikely(skb == NULL)) {
|
||||
dst_release(&rt->u.dst);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
/* drop old route */
|
||||
dst_release(skb->dst);
|
||||
skb->dst = &rt->u.dst;
|
||||
|
||||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
IP_VS_XMIT(PF_INET6, skb, rt);
|
||||
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
|
||||
tx_error_icmp:
|
||||
dst_link_failure(skb);
|
||||
tx_error:
|
||||
kfree_skb(skb);
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* NAT transmitter (only for outside-to-inside nat forwarding)
|
||||
|
@ -264,7 +396,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
/* mangle the packet */
|
||||
if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp))
|
||||
goto tx_error;
|
||||
ip_hdr(skb)->daddr = cp->daddr;
|
||||
ip_hdr(skb)->daddr = cp->daddr.ip;
|
||||
ip_send_check(ip_hdr(skb));
|
||||
|
||||
IP_VS_DBG_PKT(10, pp, skb, 0, "After DNAT");
|
||||
|
@ -276,7 +408,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
IP_VS_XMIT(skb, rt);
|
||||
IP_VS_XMIT(PF_INET, skb, rt);
|
||||
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
|
@ -292,6 +424,83 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
goto tx_error;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
int
|
||||
ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp)
|
||||
{
|
||||
struct rt6_info *rt; /* Route to the other host */
|
||||
int mtu;
|
||||
|
||||
EnterFunction(10);
|
||||
|
||||
/* check if it is a connection of no-client-port */
|
||||
if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) {
|
||||
__be16 _pt, *p;
|
||||
p = skb_header_pointer(skb, sizeof(struct ipv6hdr),
|
||||
sizeof(_pt), &_pt);
|
||||
if (p == NULL)
|
||||
goto tx_error;
|
||||
ip_vs_conn_fill_cport(cp, *p);
|
||||
IP_VS_DBG(10, "filled cport=%d\n", ntohs(*p));
|
||||
}
|
||||
|
||||
rt = __ip_vs_get_out_rt_v6(cp);
|
||||
if (!rt)
|
||||
goto tx_error_icmp;
|
||||
|
||||
/* MTU checking */
|
||||
mtu = dst_mtu(&rt->u.dst);
|
||||
if (skb->len > mtu) {
|
||||
dst_release(&rt->u.dst);
|
||||
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
|
||||
IP_VS_DBG_RL_PKT(0, pp, skb, 0,
|
||||
"ip_vs_nat_xmit_v6(): frag needed for");
|
||||
goto tx_error;
|
||||
}
|
||||
|
||||
/* copy-on-write the packet before mangling it */
|
||||
if (!skb_make_writable(skb, sizeof(struct ipv6hdr)))
|
||||
goto tx_error_put;
|
||||
|
||||
if (skb_cow(skb, rt->u.dst.dev->hard_header_len))
|
||||
goto tx_error_put;
|
||||
|
||||
/* drop old route */
|
||||
dst_release(skb->dst);
|
||||
skb->dst = &rt->u.dst;
|
||||
|
||||
/* mangle the packet */
|
||||
if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp))
|
||||
goto tx_error;
|
||||
ipv6_hdr(skb)->daddr = cp->daddr.in6;
|
||||
|
||||
IP_VS_DBG_PKT(10, pp, skb, 0, "After DNAT");
|
||||
|
||||
/* FIXME: when application helper enlarges the packet and the length
|
||||
is larger than the MTU of outgoing device, there will be still
|
||||
MTU problem. */
|
||||
|
||||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
IP_VS_XMIT(PF_INET6, skb, rt);
|
||||
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
|
||||
tx_error_icmp:
|
||||
dst_link_failure(skb);
|
||||
tx_error:
|
||||
LeaveFunction(10);
|
||||
kfree_skb(skb);
|
||||
return NF_STOLEN;
|
||||
tx_error_put:
|
||||
dst_release(&rt->u.dst);
|
||||
goto tx_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* IP Tunneling transmitter
|
||||
|
@ -423,6 +632,112 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
int
|
||||
ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp)
|
||||
{
|
||||
struct rt6_info *rt; /* Route to the other host */
|
||||
struct net_device *tdev; /* Device to other host */
|
||||
struct ipv6hdr *old_iph = ipv6_hdr(skb);
|
||||
sk_buff_data_t old_transport_header = skb->transport_header;
|
||||
struct ipv6hdr *iph; /* Our new IP header */
|
||||
unsigned int max_headroom; /* The extra header space needed */
|
||||
int mtu;
|
||||
|
||||
EnterFunction(10);
|
||||
|
||||
if (skb->protocol != htons(ETH_P_IPV6)) {
|
||||
IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): protocol error, "
|
||||
"ETH_P_IPV6: %d, skb protocol: %d\n",
|
||||
htons(ETH_P_IPV6), skb->protocol);
|
||||
goto tx_error;
|
||||
}
|
||||
|
||||
rt = __ip_vs_get_out_rt_v6(cp);
|
||||
if (!rt)
|
||||
goto tx_error_icmp;
|
||||
|
||||
tdev = rt->u.dst.dev;
|
||||
|
||||
mtu = dst_mtu(&rt->u.dst) - sizeof(struct ipv6hdr);
|
||||
/* TODO IPv6: do we need this check in IPv6? */
|
||||
if (mtu < 1280) {
|
||||
dst_release(&rt->u.dst);
|
||||
IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): mtu less than 1280\n");
|
||||
goto tx_error;
|
||||
}
|
||||
if (skb->dst)
|
||||
skb->dst->ops->update_pmtu(skb->dst, mtu);
|
||||
|
||||
if (mtu < ntohs(old_iph->payload_len) + sizeof(struct ipv6hdr)) {
|
||||
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
|
||||
dst_release(&rt->u.dst);
|
||||
IP_VS_DBG_RL("ip_vs_tunnel_xmit_v6(): frag needed\n");
|
||||
goto tx_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Okay, now see if we can stuff it in the buffer as-is.
|
||||
*/
|
||||
max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr);
|
||||
|
||||
if (skb_headroom(skb) < max_headroom
|
||||
|| skb_cloned(skb) || skb_shared(skb)) {
|
||||
struct sk_buff *new_skb =
|
||||
skb_realloc_headroom(skb, max_headroom);
|
||||
if (!new_skb) {
|
||||
dst_release(&rt->u.dst);
|
||||
kfree_skb(skb);
|
||||
IP_VS_ERR_RL("ip_vs_tunnel_xmit_v6(): no memory\n");
|
||||
return NF_STOLEN;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
skb = new_skb;
|
||||
old_iph = ipv6_hdr(skb);
|
||||
}
|
||||
|
||||
skb->transport_header = old_transport_header;
|
||||
|
||||
skb_push(skb, sizeof(struct ipv6hdr));
|
||||
skb_reset_network_header(skb);
|
||||
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
|
||||
|
||||
/* drop old route */
|
||||
dst_release(skb->dst);
|
||||
skb->dst = &rt->u.dst;
|
||||
|
||||
/*
|
||||
* Push down and install the IPIP header.
|
||||
*/
|
||||
iph = ipv6_hdr(skb);
|
||||
iph->version = 6;
|
||||
iph->nexthdr = IPPROTO_IPV6;
|
||||
iph->payload_len = old_iph->payload_len + sizeof(old_iph);
|
||||
iph->priority = old_iph->priority;
|
||||
memset(&iph->flow_lbl, 0, sizeof(iph->flow_lbl));
|
||||
iph->daddr = rt->rt6i_dst.addr;
|
||||
iph->saddr = cp->vaddr.in6; /* rt->rt6i_src.addr; */
|
||||
iph->hop_limit = old_iph->hop_limit;
|
||||
|
||||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
ip6_local_out(skb);
|
||||
|
||||
LeaveFunction(10);
|
||||
|
||||
return NF_STOLEN;
|
||||
|
||||
tx_error_icmp:
|
||||
dst_link_failure(skb);
|
||||
tx_error:
|
||||
kfree_skb(skb);
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Direct Routing transmitter
|
||||
|
@ -467,7 +782,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
IP_VS_XMIT(skb, rt);
|
||||
IP_VS_XMIT(PF_INET, skb, rt);
|
||||
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
|
@ -480,6 +795,60 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
int
|
||||
ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp)
|
||||
{
|
||||
struct rt6_info *rt; /* Route to the other host */
|
||||
int mtu;
|
||||
|
||||
EnterFunction(10);
|
||||
|
||||
rt = __ip_vs_get_out_rt_v6(cp);
|
||||
if (!rt)
|
||||
goto tx_error_icmp;
|
||||
|
||||
/* MTU checking */
|
||||
mtu = dst_mtu(&rt->u.dst);
|
||||
if (skb->len > mtu) {
|
||||
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
|
||||
dst_release(&rt->u.dst);
|
||||
IP_VS_DBG_RL("ip_vs_dr_xmit_v6(): frag needed\n");
|
||||
goto tx_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call ip_send_check because we are not sure it is called
|
||||
* after ip_defrag. Is copy-on-write needed?
|
||||
*/
|
||||
skb = skb_share_check(skb, GFP_ATOMIC);
|
||||
if (unlikely(skb == NULL)) {
|
||||
dst_release(&rt->u.dst);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
/* drop old route */
|
||||
dst_release(skb->dst);
|
||||
skb->dst = &rt->u.dst;
|
||||
|
||||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
IP_VS_XMIT(PF_INET6, skb, rt);
|
||||
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
|
||||
tx_error_icmp:
|
||||
dst_link_failure(skb);
|
||||
tx_error:
|
||||
kfree_skb(skb);
|
||||
LeaveFunction(10);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* ICMP packet transmitter
|
||||
|
@ -540,7 +909,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
IP_VS_XMIT(skb, rt);
|
||||
IP_VS_XMIT(PF_INET, skb, rt);
|
||||
|
||||
rc = NF_STOLEN;
|
||||
goto out;
|
||||
|
@ -557,3 +926,79 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
|
|||
ip_rt_put(rt);
|
||||
goto tx_error;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IP_VS_IPV6
|
||||
int
|
||||
ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
|
||||
struct ip_vs_protocol *pp, int offset)
|
||||
{
|
||||
struct rt6_info *rt; /* Route to the other host */
|
||||
int mtu;
|
||||
int rc;
|
||||
|
||||
EnterFunction(10);
|
||||
|
||||
/* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
|
||||
forwarded directly here, because there is no need to
|
||||
translate address/port back */
|
||||
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
|
||||
if (cp->packet_xmit)
|
||||
rc = cp->packet_xmit(skb, cp, pp);
|
||||
else
|
||||
rc = NF_ACCEPT;
|
||||
/* do not touch skb anymore */
|
||||
atomic_inc(&cp->in_pkts);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* mangle and send the packet here (only for VS/NAT)
|
||||
*/
|
||||
|
||||
rt = __ip_vs_get_out_rt_v6(cp);
|
||||
if (!rt)
|
||||
goto tx_error_icmp;
|
||||
|
||||
/* MTU checking */
|
||||
mtu = dst_mtu(&rt->u.dst);
|
||||
if (skb->len > mtu) {
|
||||
dst_release(&rt->u.dst);
|
||||
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
|
||||
IP_VS_DBG_RL("ip_vs_in_icmp(): frag needed\n");
|
||||
goto tx_error;
|
||||
}
|
||||
|
||||
/* copy-on-write the packet before mangling it */
|
||||
if (!skb_make_writable(skb, offset))
|
||||
goto tx_error_put;
|
||||
|
||||
if (skb_cow(skb, rt->u.dst.dev->hard_header_len))
|
||||
goto tx_error_put;
|
||||
|
||||
/* drop the old route when skb is not shared */
|
||||
dst_release(skb->dst);
|
||||
skb->dst = &rt->u.dst;
|
||||
|
||||
ip_vs_nat_icmp_v6(skb, pp, cp, 0);
|
||||
|
||||
/* Another hack: avoid icmp_send in ip_fragment */
|
||||
skb->local_df = 1;
|
||||
|
||||
IP_VS_XMIT(PF_INET6, skb, rt);
|
||||
|
||||
rc = NF_STOLEN;
|
||||
goto out;
|
||||
|
||||
tx_error_icmp:
|
||||
dst_link_failure(skb);
|
||||
tx_error:
|
||||
dev_kfree_skb(skb);
|
||||
rc = NF_STOLEN;
|
||||
out:
|
||||
LeaveFunction(10);
|
||||
return rc;
|
||||
tx_error_put:
|
||||
dst_release(&rt->u.dst);
|
||||
goto tx_error;
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue