All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.