905 lines · 21.8 KB · C
RawDownload
/* * IPVS         An implementation of the IP virtual server support for the *              LINUX operating system.  IPVS is now implemented as a module *              over the NetFilter framework. IPVS can be used to build a *              high-performance and highly available server based on a *              cluster of servers. * * Version:     $Id: ip_vs_sync.c,v 1.13 2003/06/08 09:31:19 wensong Exp $ * * Authors:     Wensong Zhang <wensong@linuxvirtualserver.org> * * ip_vs_sync:  sync connection info from master load balancer to backups *              through multicast * * Changes: *	Alexandre Cassen	:	Added master & backup support at a time. *	Alexandre Cassen	:	Added SyncID support for incoming sync *					messages filtering. *	Justin Ossevoort	:	Fix endian problem on sync message size. */ #include <linux/module.h>#include <linux/slab.h>#include <linux/inetdevice.h>#include <linux/net.h>#include <linux/completion.h>#include <linux/delay.h>#include <linux/skbuff.h>#include <linux/in.h>#include <linux/igmp.h>                 /* for ip_mc_join_group */#include <linux/udp.h> #include <net/ip.h>#include <net/sock.h>#include <asm/uaccess.h>                /* for get_fs and set_fs */ #include <net/ip_vs.h> #define IP_VS_SYNC_GROUP 0xe0000051    /* multicast addr - 224.0.0.81 */#define IP_VS_SYNC_PORT  8848          /* multicast port */  /* *	IPVS sync connection entry */struct ip_vs_sync_conn {	__u8			reserved; 	/* Protocol, addresses and port numbers */	__u8			protocol;       /* Which protocol (TCP/UDP) */	__be16			cport;	__be16                  vport;	__be16                  dport;	__be32                  caddr;          /* client address */	__be32                  vaddr;          /* virtual address */	__be32                  daddr;          /* destination address */ 	/* Flags and state transition */	__be16                  flags;          /* status flags */	__be16                  state;          /* state info */ 	/* The sequence options start here */}; struct ip_vs_sync_conn_options {	struct ip_vs_seq        in_seq;         /* incoming seq. struct */	struct ip_vs_seq        out_seq;        /* outgoing seq. struct */}; #define IP_VS_SYNC_CONN_TIMEOUT (3*60*HZ)#define SIMPLE_CONN_SIZE  (sizeof(struct ip_vs_sync_conn))#define FULL_CONN_SIZE  \(sizeof(struct ip_vs_sync_conn) + sizeof(struct ip_vs_sync_conn_options))  /*  The master mulitcasts messages to the backup load balancers in the  following format.        0                   1                   2                   3       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+      |  Count Conns  |    SyncID     |            Size               |      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+      |                                                               |      |                    IPVS Sync Connection (1)                   |      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+      |                            .                                  |      |                            .                                  |      |                            .                                  |      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+      |                                                               |      |                    IPVS Sync Connection (n)                   |      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/ #define SYNC_MESG_HEADER_LEN	4 struct ip_vs_sync_mesg {	__u8                    nr_conns;	__u8                    syncid;	__u16                   size; 	/* ip_vs_sync_conn entries start here */}; /* the maximum length of sync (sending/receiving) message */static int sync_send_mesg_maxlen;static int sync_recv_mesg_maxlen; struct ip_vs_sync_buff {	struct list_head        list;	unsigned long           firstuse; 	/* pointers for the message data */	struct ip_vs_sync_mesg  *mesg;	unsigned char           *head;	unsigned char           *end;};  /* the sync_buff list head and the lock */static LIST_HEAD(ip_vs_sync_queue);static DEFINE_SPINLOCK(ip_vs_sync_lock); /* current sync_buff for accepting new conn entries */static struct ip_vs_sync_buff   *curr_sb = NULL;static DEFINE_SPINLOCK(curr_sb_lock); /* ipvs sync daemon state */volatile int ip_vs_sync_state = IP_VS_STATE_NONE;volatile int ip_vs_master_syncid = 0;volatile int ip_vs_backup_syncid = 0; /* multicast interface name */char ip_vs_master_mcast_ifn[IP_VS_IFNAME_MAXLEN];char ip_vs_backup_mcast_ifn[IP_VS_IFNAME_MAXLEN]; /* multicast addr */static struct sockaddr_in mcast_addr;  static inline void sb_queue_tail(struct ip_vs_sync_buff *sb){	spin_lock(&ip_vs_sync_lock);	list_add_tail(&sb->list, &ip_vs_sync_queue);	spin_unlock(&ip_vs_sync_lock);} static inline struct ip_vs_sync_buff * sb_dequeue(void){	struct ip_vs_sync_buff *sb; 	spin_lock_bh(&ip_vs_sync_lock);	if (list_empty(&ip_vs_sync_queue)) {		sb = NULL;	} else {		sb = list_entry(ip_vs_sync_queue.next,				struct ip_vs_sync_buff,				list);		list_del(&sb->list);	}	spin_unlock_bh(&ip_vs_sync_lock); 	return sb;} static inline struct ip_vs_sync_buff * ip_vs_sync_buff_create(void){	struct ip_vs_sync_buff *sb; 	if (!(sb=kmalloc(sizeof(struct ip_vs_sync_buff), GFP_ATOMIC)))		return NULL; 	if (!(sb->mesg=kmalloc(sync_send_mesg_maxlen, GFP_ATOMIC))) {		kfree(sb);		return NULL;	}	sb->mesg->nr_conns = 0;	sb->mesg->syncid = ip_vs_master_syncid;	sb->mesg->size = 4;	sb->head = (unsigned char *)sb->mesg + 4;	sb->end = (unsigned char *)sb->mesg + sync_send_mesg_maxlen;	sb->firstuse = jiffies;	return sb;} static inline void ip_vs_sync_buff_release(struct ip_vs_sync_buff *sb){	kfree(sb->mesg);	kfree(sb);} /* *	Get the current sync buffer if it has been created for more *	than the specified time or the specified time is zero. */static inline struct ip_vs_sync_buff *get_curr_sync_buff(unsigned long time){	struct ip_vs_sync_buff *sb; 	spin_lock_bh(&curr_sb_lock);	if (curr_sb && (time == 0 ||			time_before(jiffies - curr_sb->firstuse, time))) {		sb = curr_sb;		curr_sb = NULL;	} else		sb = NULL;	spin_unlock_bh(&curr_sb_lock);	return sb;}  /* *      Add an ip_vs_conn information into the current sync_buff. *      Called by ip_vs_in. */void ip_vs_sync_conn(struct ip_vs_conn *cp){	struct ip_vs_sync_mesg *m;	struct ip_vs_sync_conn *s;	int len; 	spin_lock(&curr_sb_lock);	if (!curr_sb) {		if (!(curr_sb=ip_vs_sync_buff_create())) {			spin_unlock(&curr_sb_lock);			IP_VS_ERR("ip_vs_sync_buff_create failed.\n");			return;		}	} 	len = (cp->flags & IP_VS_CONN_F_SEQ_MASK) ? FULL_CONN_SIZE :		SIMPLE_CONN_SIZE;	m = curr_sb->mesg;	s = (struct ip_vs_sync_conn *)curr_sb->head; 	/* copy members */	s->protocol = cp->protocol;	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->flags = htons(cp->flags & ~IP_VS_CONN_F_HASHED);	s->state = htons(cp->state);	if (cp->flags & IP_VS_CONN_F_SEQ_MASK) {		struct ip_vs_sync_conn_options *opt =			(struct ip_vs_sync_conn_options *)&s[1];		memcpy(opt, &cp->in_seq, sizeof(*opt));	} 	m->nr_conns++;	m->size += len;	curr_sb->head += len; 	/* check if there is a space for next one */	if (curr_sb->head+FULL_CONN_SIZE > curr_sb->end) {		sb_queue_tail(curr_sb);		curr_sb = NULL;	}	spin_unlock(&curr_sb_lock); 	/* synchronize its controller if it has */	if (cp->control)		ip_vs_sync_conn(cp->control);}  /* *      Process received multicast message and create the corresponding *      ip_vs_conn entries. */static void ip_vs_process_message(const char *buffer, const size_t buflen){	struct ip_vs_sync_mesg *m = (struct ip_vs_sync_mesg *)buffer;	struct ip_vs_sync_conn *s;	struct ip_vs_sync_conn_options *opt;	struct ip_vs_conn *cp;	char *p;	int i; 	/* Convert size back to host byte order */	m->size = ntohs(m->size); 	if (buflen != m->size) {		IP_VS_ERR("bogus message\n");		return;	} 	/* SyncID sanity check */	if (ip_vs_backup_syncid != 0 && m->syncid != ip_vs_backup_syncid) {		IP_VS_DBG(7, "Ignoring incoming msg with syncid = %d\n",			  m->syncid);		return;	} 	p = (char *)buffer + sizeof(struct ip_vs_sync_mesg);	for (i=0; i<m->nr_conns; i++) {		unsigned flags; 		s = (struct ip_vs_sync_conn *)p;		flags = ntohs(s->flags);		if (!(flags & IP_VS_CONN_F_TEMPLATE))			cp = ip_vs_conn_in_get(s->protocol,					       s->caddr, s->cport,					       s->vaddr, s->vport);		else			cp = ip_vs_ct_in_get(s->protocol,					       s->caddr, s->cport,					       s->vaddr, s->vport);		if (!cp) {			cp = ip_vs_conn_new(s->protocol,					    s->caddr, s->cport,					    s->vaddr, s->vport,					    s->daddr, s->dport,					    flags, NULL);			if (!cp) {				IP_VS_ERR("ip_vs_conn_new failed\n");				return;			}			cp->state = ntohs(s->state);		} else if (!cp->dest) {			/* it is an entry created by the synchronization */			cp->state = ntohs(s->state);			cp->flags = flags | IP_VS_CONN_F_HASHED;		}	/* Note that we don't touch its state and flags			   if it is a normal entry. */ 		if (flags & IP_VS_CONN_F_SEQ_MASK) {			opt = (struct ip_vs_sync_conn_options *)&s[1];			memcpy(&cp->in_seq, opt, sizeof(*opt));			p += FULL_CONN_SIZE;		} else			p += SIMPLE_CONN_SIZE; 		atomic_set(&cp->in_pkts, sysctl_ip_vs_sync_threshold[0]);		cp->timeout = IP_VS_SYNC_CONN_TIMEOUT;		ip_vs_conn_put(cp); 		if (p > buffer+buflen) {			IP_VS_ERR("bogus message\n");			return;		}	}}  /* *      Setup loopback of outgoing multicasts on a sending socket */static void set_mcast_loop(struct sock *sk, u_char loop){	struct inet_sock *inet = inet_sk(sk); 	/* setsockopt(sock, SOL_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); */	lock_sock(sk);	inet->mc_loop = loop ? 1 : 0;	release_sock(sk);} /* *      Specify TTL for outgoing multicasts on a sending socket */static void set_mcast_ttl(struct sock *sk, u_char ttl){	struct inet_sock *inet = inet_sk(sk); 	/* setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); */	lock_sock(sk);	inet->mc_ttl = ttl;	release_sock(sk);} /* *      Specifiy default interface for outgoing multicasts */static int set_mcast_if(struct sock *sk, char *ifname){	struct net_device *dev;	struct inet_sock *inet = inet_sk(sk); 	if ((dev = __dev_get_by_name(ifname)) == NULL)		return -ENODEV; 	if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if)		return -EINVAL; 	lock_sock(sk);	inet->mc_index = dev->ifindex;	/*  inet->mc_addr  = 0; */	release_sock(sk); 	return 0;}  /* *	Set the maximum length of sync message according to the *	specified interface's MTU. */static int set_sync_mesg_maxlen(int sync_state){	struct net_device *dev;	int num; 	if (sync_state == IP_VS_STATE_MASTER) {		if ((dev = __dev_get_by_name(ip_vs_master_mcast_ifn)) == NULL)			return -ENODEV; 		num = (dev->mtu - sizeof(struct iphdr) -		       sizeof(struct udphdr) -		       SYNC_MESG_HEADER_LEN - 20) / SIMPLE_CONN_SIZE;		sync_send_mesg_maxlen =			SYNC_MESG_HEADER_LEN + SIMPLE_CONN_SIZE * num;		IP_VS_DBG(7, "setting the maximum length of sync sending "			  "message %d.\n", sync_send_mesg_maxlen);	} else if (sync_state == IP_VS_STATE_BACKUP) {		if ((dev = __dev_get_by_name(ip_vs_backup_mcast_ifn)) == NULL)			return -ENODEV; 		sync_recv_mesg_maxlen = dev->mtu -			sizeof(struct iphdr) - sizeof(struct udphdr);		IP_VS_DBG(7, "setting the maximum length of sync receiving "			  "message %d.\n", sync_recv_mesg_maxlen);	} 	return 0;}  /* *      Join a multicast group. *      the group is specified by a class D multicast address 224.0.0.0/8 *      in the in_addr structure passed in as a parameter. */static intjoin_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname){	struct ip_mreqn mreq;	struct net_device *dev;	int ret; 	memset(&mreq, 0, sizeof(mreq));	memcpy(&mreq.imr_multiaddr, addr, sizeof(struct in_addr)); 	if ((dev = __dev_get_by_name(ifname)) == NULL)		return -ENODEV;	if (sk->sk_bound_dev_if && dev->ifindex != sk->sk_bound_dev_if)		return -EINVAL; 	mreq.imr_ifindex = dev->ifindex; 	lock_sock(sk);	ret = ip_mc_join_group(sk, &mreq);	release_sock(sk); 	return ret;}  static int bind_mcastif_addr(struct socket *sock, char *ifname){	struct net_device *dev;	__be32 addr;	struct sockaddr_in sin; 	if ((dev = __dev_get_by_name(ifname)) == NULL)		return -ENODEV; 	addr = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);	if (!addr)		IP_VS_ERR("You probably need to specify IP address on "			  "multicast interface.\n"); 	IP_VS_DBG(7, "binding socket with (%s) %u.%u.%u.%u\n",		  ifname, NIPQUAD(addr)); 	/* Now bind the socket with the address of multicast interface */	sin.sin_family	     = AF_INET;	sin.sin_addr.s_addr  = addr;	sin.sin_port         = 0; 	return sock->ops->bind(sock, (struct sockaddr*)&sin, sizeof(sin));} /* *      Set up sending multicast socket over UDP */static struct socket * make_send_sock(void){	struct socket *sock; 	/* First create a socket */	if (sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock) < 0) {		IP_VS_ERR("Error during creation of socket; terminating\n");		return NULL;	}

Lines 1–500 of 905.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t