netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [2/2] CARP implementation. HA master's failover.
       [not found] <1089898339.6114.860.camel@uganda>
@ 2004-07-15 13:37 ` Evgeniy Polyakov
  0 siblings, 0 replies; only message in thread
From: Evgeniy Polyakov @ 2004-07-15 13:37 UTC (permalink / raw)
  To: netdev; +Cc: netfilter-failover


[-- Attachment #1.1: Type: text/plain, Size: 2093 bytes --]

On Thu, 2004-07-15 at 17:32, Evgeniy Polyakov wrote:
> Hello, network developers.
> 
> I'm glad to introduce CARP failover mechanism implementation.
> It is based on OpenBSD's CARP protocol but is not compatible with it
> since OpenBSD's implementation does not contain protection against
> repeated message sending.
> 
> The main goal of the project is to implement CARP + firewall sync, but
> second part already implemented by Harald Welte <laforge@gnumonks.org> and 
> KOVACS Krisztian <hidden@sch.bme.hu> in ct_sync.
> 
> By design each node has it's own advertisement base and skew, node with
> the least timeval constructed from them became a master.
> It begins to advertise it's base and skew until shutdown or other node
> lower it's base+skew pair.
> CARP uses currently only IPv4 multicast, but can be easily changed to
> use IPv6. 
> Each CARP packet contains unique 64bit counter with it's SHA1 hmac
> digest with 20byte secret key. By design this counter is incremented in
> both master and backup before sending and while receiving accordingly.
> If master and backup counters do not coincide with each other while
> receiving backup node drops this packet and thus preventing repeated
> sending attack.
> When after predefined interval master didn't send any packet or it's
> base+skew is bigger than that in the remote node those node becomes a
> master and begins to advertise.
> 
> CARP has 2 work queues for "became_master" and "became_backup" events.
> Such events may be easily registered in runtime by external modules.
> One of such event handlers may send netlink message to ct_sync and/or
> userspace daemon which will flush iptables rules, up/down interfaces and
> so on...
> 
> Please review and comment.
> 
> Code against 2.6 attached 
> in next 2 e-mails since netfilter-failover@lists.netfilter.org doesn't accept
> e-mail greater than 40kb.
> 
> Code also is available at
> http://www.2ka.mipt.ru/~johnpol/carp_latest.tar.gz
-- 
	Evgeniy Polaykov ( s0mbre )

Crash is better than data corruption. -- Art Grabowski

[-- Attachment #1.2: carpctl.c --]
[-- Type: text/x-csrc, Size: 3849 bytes --]

/*
 * 	carpctl.c
 * 
 * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <linux/if.h>

#include "carp_ioctl.h"

#define	CARP_KEY_LEN		20
#define	CARP_HMAC_PAD_LEN	64
#define IPPROTO_CARP 		112

static void usage(const char *pr)
{
	fprintf(stderr, "Usage: %s: [-h] [-i iph] [-b advbase] [-s advskew] [-d device] [-v vhid] [-k key] [-p pad] [-S state] "
			"[-m md_timeout] [-a adv_timout].\n",
			pr);
}

static void carp_dump_params(struct carp_ioctl_params p)
{
	printf("Attached to device %s.\n", p.devname);
	printf("ADV: base=%d, skew=%d.\n", p.carp_advbase, p.carp_advskew);
	printf("VHID=%d, STATE=%d.\n", p.carp_vhid, p.state);
}

int main(int argc, char *argv[])
{
	int ch, err, s, need_change;
	struct ifreq ifr;
	char devname[IFNAMSIZ] = "carp0";
	struct carp_ioctl_params p;

	memset(&p, 0, sizeof(p));
	
	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name));
	ifr.ifr_ifru.ifru_data = (void *)&p;

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s == -1)
	{
		fprintf(stderr, "Failed to create CARP control socket: [%d] %s.\n",
				errno, strerror(errno));
		return -1;
	}
	
	err = ioctl(s, SIOC_GETCARPPARAMS, &ifr);
	if (err == -1)
	{
		fprintf(stderr, "Failed to call ioctl: [%d] %s.\n",
				errno, strerror(errno));
		close(s);
		return -1;
	}

	carp_dump_params(p);

	need_change = 0;
	while((ch = getopt(argc, argv, "hi:b:s:d:v:k:p:S:m:a:")) != -1)
	{
		need_change = 1;
		switch (ch)
		{
			case 'm':
				p.md_timeout = atoi(optarg);
				break;
			case 'a':
				p.adv_timeout = atoi(optarg);
				break;
			case 'b':
				p.carp_advbase = atoi(optarg);
				break;
			case 's':
				p.carp_advskew = atoi(optarg);
				break;
			case 'd':
				memcpy(p.devname, optarg, sizeof(p.devname));
				p.devname[sizeof(p.devname) - 1] = '\0';
				break;
			case 'v':
				p.carp_vhid = atoi(optarg);
				break;
			case 'k':
				if (strlen(optarg) != sizeof(p.carp_key))
				{
					fprintf(stderr, "Wrong key length. Must be %d.\n", sizeof(p.carp_key));
					return -1;
				}
				memcpy(p.carp_key, optarg, sizeof(p.carp_key));
				break;
			case 'p':
				if (strlen(optarg) != sizeof(p.carp_pad))
				{
					fprintf(stderr, "Wrong pad length. Must be %d.\n", sizeof(p.carp_pad));
					return -1;
				}
				memcpy(p.carp_pad, optarg, sizeof(p.carp_pad));
				break;
			case 'S':
				p.state = atoi(optarg);
				break;
			case 'h':
			default:
				need_change = 0;
				usage(argv[0]);
				return -1;
		}
	}

	if (!need_change)
	{
		usage(argv[0]);
		return -1;
	}
	
	ifr.ifr_ifru.ifru_data = (void *)&p;
	
	err = ioctl(s, SIOC_SETCARPPARAMS, &ifr);
	if (err == -1)
	{
		fprintf(stderr, "Failed to call ioctl: [%d] %s.\n",
				errno, strerror(errno));
		close(s);
		return -1;
	}

	close(s);

	return 0;
}

[-- Attachment #1.3: carp_queue.c --]
[-- Type: text/x-csrc, Size: 4182 bytes --]

/*
 * 	carp_queue.c
 * 
 * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <linux/workqueue.h>
#include <asm/spinlock.h>

#include "carp.h"
#include "carp_log.h"
#include "carp_queue.h"

static spinlock_t carp_queue_lock = SPIN_LOCK_UNLOCKED;
static struct workqueue_struct *carp_queue[2];
static struct list_head carp_works[2];
static int carp_work_counter;

static void carp_queue_wrapper(void *data)
{
	struct carp_master_task *t = (struct carp_master_task *)data;

	atomic_inc(&t->task->refcnt);
	t->task->callback(t->task->data);
	atomic_dec(&t->task->refcnt);
}

struct carp_master_task *carp_alloc_task(struct __carp_master_task *t)
{
	struct carp_master_task *newt;

	newt = kmalloc(sizeof(struct carp_master_task), GFP_KERNEL);
	if (!newt)
	{
		log("Failed to create new CARP master task.\n");
		return NULL;
	}

	memset(newt, 0, sizeof(struct carp_master_task));
	
	atomic_set(&t->refcnt, 1);
	newt->task = t;
	newt->task->id = carp_work_counter++;
	
	INIT_WORK(&newt->work, &carp_queue_wrapper, newt);

	return newt;
}

static void carp_free_task(struct carp_master_task *t)
{
	cancel_delayed_work(&t->work);

	while(atomic_read(&t->task->refcnt))
		schedule_timeout(10);
	
	kfree(t);
}

int carp_add_task(struct __carp_master_task *mt, int num)
{
	struct carp_master_task *newt;

	if (num != MASTER_QUEUE && num != BACKUP_QUEUE)
		return -EINVAL;

	newt = carp_alloc_task(mt);
	if (!newt)
		return -ENOMEM;
	
	spin_lock(&carp_queue_lock);
	list_add_tail(&newt->entry, &carp_works[num]);
	spin_unlock(&carp_queue_lock);

	return 0;
}

void carp_del_task(struct __carp_master_task *mt, int num)
{
	struct list_head *ent, *n;
	struct carp_master_task *t = NULL;
	int found = 0;
	
	if (num != MASTER_QUEUE && num != BACKUP_QUEUE)
		return;
	
	spin_lock(&carp_queue_lock);
	list_for_each_safe(ent, n, &carp_works[num])
	{
		t = list_entry(ent, struct carp_master_task, entry);

		if (t->task->id == mt->id) {
			list_del(&t->entry);
			found = 1;
			break;
		}
	}
	spin_unlock(&carp_queue_lock);

	if (found)
	{
		atomic_dec(&t->task->refcnt);
		carp_free_task(t);
	}
}

void carp_call_queue(int num)
{
	struct list_head *ent, *n;
	struct carp_master_task *t;
	
	if (num != MASTER_QUEUE && num != BACKUP_QUEUE)
		return;
	
	spin_lock(&carp_queue_lock);
	list_for_each_safe(ent, n, &carp_works[num])
	{
		t = list_entry(ent, struct carp_master_task, entry);
		queue_work(carp_queue[num], &t->work);
	}
	spin_unlock(&carp_queue_lock);
}

int carp_init_queues(void)
{
	int i;

	for (i=0; i<2; ++i)
		INIT_LIST_HEAD(&carp_works[i]);

	carp_queue[MASTER_QUEUE] = create_workqueue("CARP_m");
	if (!carp_queue[MASTER_QUEUE])
	{
		log("Failed to create master CARP queue.\n");
		return -1;
	}
	
	carp_queue[BACKUP_QUEUE] = create_workqueue("CARP_b");
	if (!carp_queue[BACKUP_QUEUE])
	{
		destroy_workqueue(carp_queue[MASTER_QUEUE]);
		log("Failed to create backup CARP queue.\n");
		return -1;
	}

	return 0;
}

void carp_flush_queue(int num)
{
	if (num != MASTER_QUEUE && num != BACKUP_QUEUE)
		return;
	
	flush_workqueue(carp_queue[num]);
}

void carp_fini_queues(void)
{
	carp_flush_queue(MASTER_QUEUE);
	destroy_workqueue(carp_queue[MASTER_QUEUE]);
	carp_flush_queue(BACKUP_QUEUE);
	destroy_workqueue(carp_queue[BACKUP_QUEUE]);
}


[-- Attachment #1.4: carp_queue.h --]
[-- Type: text/x-chdr, Size: 1540 bytes --]

/*
 * 	carp_queue.h
 * 
 * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef __CARP_QUEUE_H
#define __CARP_QUEUE_H

#include <linux/workqueue.h>

#include <asm/atomic.h>

enum carp_queue_number {MASTER_QUEUE = 0, BACKUP_QUEUE};

struct __carp_master_task
{
	atomic_t	refcnt;
	u32		id;

	void 		(* callback)(void *);
	void		*data;

	void		*priv;
};

struct carp_master_task
{
	struct list_head		entry;
	struct __carp_master_task	*task;

	struct work_struct		work;
};

int carp_init_queues(void);
void carp_flush_queue(int);
void carp_fini_queues(void);
void carp_call_queue(int);

int carp_add_task(struct __carp_master_task *, int);
void carp_del_task(struct __carp_master_task *, int);

#endif /* __CARP_QUEUE_H */

[-- Attachment #1.5: carp_log.c --]
[-- Type: text/x-csrc, Size: 2967 bytes --]

/*
 * 	carp_log.c
 * 
 * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/random.h>
#include <linux/crypto.h>

#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/protocol.h>
#include <net/arp.h>

#include <asm/scatterlist.h>
#include <asm/delay.h>

#include "carp.h"
#include "carp_log.h"

void dump_addr_info(struct carp_priv *cp)
{
	int i;
	
	printk(KERN_INFO "CARP addr: hw=");
	for (i=0; i<ETH_ALEN; ++i)
		printk("%02x%c", (unsigned char)cp->odev->dev_addr[i], (i==ETH_ALEN-1)?' ':':');
	printk(", sw=");
	for (i=0; i<4; ++i)
		printk("%d%c", (ntohl(cp->iph.saddr) >> (3-i)*8)&0xff, (i==3)?' ':'.');
	printk(", dst=");
	for (i=0; i<4; ++i)
		printk("%d%c", (ntohl(cp->iph.daddr) >> (3-i)*8)&0xff, (i==3)?' ':'.');
	printk("\n");
}

void dump_hmac_params(struct carp_priv *cp)
{
	int i;
	unsigned int keylen;
	struct scatterlist sg;
	u8 carp_md[CARP_SIG_LEN];

	keylen = sizeof(cp->carp_key);

	sg.page	= virt_to_page(&cp->carp_adv_counter);
	sg.offset = (unsigned long)(&cp->carp_adv_counter) % PAGE_SIZE;
	sg.length = sizeof(cp->carp_adv_counter);
	
	crypto_hmac(cp->tfm, cp->carp_key, &keylen, &sg, 1, carp_md);
	
	printk(KERN_INFO "key: ");
	for (i=0; i<CARP_KEY_LEN; ++i)
		printk("%02x ", cp->carp_key[i]);
	printk("\n");

	printk("counter: %llx ", cp->carp_adv_counter);

	printk("hmac: ");
	for (i=0; i<CARP_SIG_LEN; ++i)
		printk("%02x ", carp_md[i]);
	printk("\n");
}

void dump_carp_header(struct carp_header *ch)
{
	u64 counter;
	int i;
	
	counter = ntohl(ch->carp_counter[0]);
	counter = counter<<32;
	counter += ntohl(ch->carp_counter[1]);
	
	log("type=%u, version=%u, vhid=%u, skew=%u, base=%u, counter=%llu, md={",
			ch->carp_type,
			ch->carp_version,
			ch->carp_vhid,
			ch->carp_advskew,
			ch->carp_advbase,
			counter);
	
	for (i=0; i<sizeof(ch->carp_md); ++i)
	{
		printk("%02x ", ch->carp_md[i]);
	}
	printk("}\n");
}


[-- Attachment #1.6: carp_log.h --]
[-- Type: text/x-chdr, Size: 1131 bytes --]

/*
 * 	carp_log.h
 * 
 * 2004 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifndef __CARP_LOG_H
#define __CARP_LOG_H

#include "carp.h"

#define log(f, a...) printk(KERN_INFO f, ##a)

void dump_addr_info(struct carp_priv *);
void dump_hmac_params(struct carp_priv *);
void dump_carp_header(struct carp_header *);

#endif /* __CARP_LOG_H */

[-- Attachment #1.7: Makefile --]
[-- Type: text/x-makefile, Size: 382 bytes --]

obj-m		:= ip_carp.o
ip_carp-objs	:= carp.o carp_log.o carp_queue.o

KDIR	:= /usr/local/src/linux-2.6
PWD	:= $(shell pwd)

default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
	gcc -W -Wall carpctl.c -o carpctl

copy:
	scp ip_carp.ko carpctl mtest:carp/
	scp ip_carp.ko carpctl pcix:aWork/carp/

clean:
	rm -f *.o *.ko *.mod.* .*.cmd *~ carpctl
	rm -rf .tmp_versions

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2004-07-15 13:37 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <1089898339.6114.860.camel@uganda>
2004-07-15 13:37 ` [2/2] CARP implementation. HA master's failover Evgeniy Polyakov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).