From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
To: netdev@oss.sgi.com
Cc: netfilter-failover@lists.netfilter.org
Subject: [1/2] CARP implementation. HA master's failover.
Date: Thu, 15 Jul 2004 17:36:35 +0400 [thread overview]
Message-ID: <1089898595.6114.866.camel@uganda> (raw)
In-Reply-To: <1089898303.6114.859.camel@uganda>
[-- Attachment #1.1: Type: text/plain, Size: 2096 bytes --]
On Thu, 2004-07-15 at 17:31, 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: carp.c --]
[-- Type: text/x-csrc, Size: 19948 bytes --]
/*
* carp.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 <asm/uaccess.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
#include <linux/igmp.h>
#include <linux/netfilter_ipv4.h>
#include <linux/crypto.h>
#include <linux/random.h>
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/protocol.h>
#include <net/arp.h>
#include <net/checksum.h>
#include <net/inet_ecn.h>
#include <asm/scatterlist.h>
#ifdef CONFIG_IPV6
#include <net/ipv6.h>
#include <net/ip6_fib.h>
#include <net/ip6_route.h>
#endif
#include "carp.h"
#include "carp_log.h"
#include "carp_queue.h"
#include "carp_ioctl.h"
#define timeval_before(before, after) \
(((before)->tv_sec == (after)->tv_sec) ? ((before)->tv_usec < (after)->tv_usec) : ((before)->tv_sec < (after)->tv_sec))
static int carp_init(struct net_device *);
static void carp_uninit(struct net_device *);
static void carp_setup(struct net_device *);
static int carp_close(struct net_device *);
static int carp_open(struct net_device *);
static int carp_ioctl (struct net_device *, struct ifreq *, int);
static int carp_check_params(struct carp_ioctl_params);
static void carp_err(struct sk_buff *, u32);
static int carp_rcv(struct sk_buff *);
static int carp_xmit(struct sk_buff *, struct net_device *);
static struct net_device_stats *carp_get_stats(struct net_device *);
static void carp_hmac_sign(struct carp_priv *, struct carp_header *);
static int carp_hmac_verify(struct carp_priv *, struct carp_header *);
static u32 inline addr2val(u8, u8, u8, u8);
static void carp_set_state(struct carp_priv *, enum carp_state);
static void carp_master_down(unsigned long);
static void carp_advertise(unsigned long);
static int __init device_carp_init(void);
void __exit device_carp_fini(void);
static struct net_device *carp_dev;
static void carp_uninit(struct net_device *dev)
{
struct carp_priv *cp = dev->priv;
if (timer_pending(&cp->md_timer))
del_timer_sync(&cp->md_timer);
if (timer_pending(&cp->adv_timer))
del_timer_sync(&cp->adv_timer);
log("%s\n", __func__);
dev_put(cp->odev);
dev_put(dev);
}
static void carp_err(struct sk_buff *skb, u32 info)
{
log("%s\n", __func__);
kfree_skb(skb);
}
static void carp_hmac_sign(struct carp_priv *cp, struct carp_header *ch)
{
unsigned int keylen = sizeof(cp->carp_key);
struct scatterlist sg;
sg.page = virt_to_page(ch->carp_counter);
sg.offset = ((unsigned long)(ch->carp_counter)) % PAGE_SIZE;
sg.length = sizeof(ch->carp_counter);
crypto_hmac(cp->tfm, cp->carp_key, &keylen, &sg, 1, ch->carp_md);
}
static int carp_hmac_verify(struct carp_priv *cp, struct carp_header *ch)
{
u8 tmp_md[CARP_SIG_LEN];
unsigned int keylen = sizeof(cp->carp_key);
struct scatterlist sg;
sg.page = virt_to_page(ch->carp_counter);
sg.offset = ((unsigned long)(ch->carp_counter)) % PAGE_SIZE;
sg.length = sizeof(ch->carp_counter);
crypto_hmac(cp->tfm, cp->carp_key, &keylen, &sg, 1, tmp_md);
#if 0
{
int i;
printk("calculated: ");
for (i=0; i<CARP_SIG_LEN; ++i)
printk("%02x ", tmp_md[i]);
printk("\n");
printk("from header: ");
for (i=0; i<CARP_SIG_LEN; ++i)
printk("%02x ", ch->carp_md[i]);
printk("\n");
}
#endif
return memcmp(tmp_md, ch->carp_md, CARP_SIG_LEN);
}
static int carp_check_params(struct carp_ioctl_params p)
{
if (p.state != INIT && p.state != BACKUP && p.state != MASTER)
{
log("Wrong state %d.\n", p.state);
return -1;
}
if (!__dev_get_by_name(p.devname))
{
log("No such device %s.\n", p.devname);
return -2;
}
if (p.md_timeout > MAX_MD_TIMEOUT || p.adv_timeout > MAX_ADV_TIMEOUT ||
!p.md_timeout || !p.adv_timeout)
return -3;
return 0;
}
static void carp_set_state(struct carp_priv *cp, enum carp_state state)
{
log("%s: Setting CARP state from %d to %d.\n", __func__, cp->state, state);
cp->state = state;
switch (state)
{
case MASTER:
carp_call_queue(MASTER_QUEUE);
if (!timer_pending(&cp->adv_timer))
mod_timer(&cp->adv_timer, jiffies + cp->adv_timeout*HZ);
break;
case BACKUP:
carp_call_queue(BACKUP_QUEUE);
if (!timer_pending(&cp->md_timer))
mod_timer(&cp->md_timer, jiffies + cp->md_timeout*HZ);
break;
case INIT:
if (!timer_pending(&cp->md_timer))
mod_timer(&cp->md_timer, jiffies + cp->md_timeout*HZ);
break;
}
}
static void carp_master_down(unsigned long data)
{
struct carp_priv *cp = (struct carp_priv *)data;
//log("%s: state=%d.\n", __func__, cp->state);
if (cp->state != MASTER)
{
if (test_bit(CARP_DATA_AVAIL, (long *)&cp->flags))
{
if (!timer_pending(&cp->md_timer))
mod_timer(&cp->md_timer, jiffies + cp->md_timeout*HZ);
}
else
carp_set_state(cp, MASTER);
}
clear_bit(CARP_DATA_AVAIL, (long *)&cp->flags);
}
static int carp_rcv(struct sk_buff *skb)
{
struct iphdr *iph;
struct carp_priv *cp = carp_dev->priv;
struct carp_header *ch;
int err = 0;
u64 tmp_counter;
struct timeval cptv, chtv;
//log("%s: state=%d\n", __func__, cp->state);
spin_lock(&cp->lock);
iph = skb->nh.iph;
ch = (struct carp_header *)skb->data;
//dump_carp_header(ch);
if (ch->carp_version != cp->hdr.carp_version)
{
log("CARP version mismatch: remote=%d, local=%d.\n",
ch->carp_version, cp->hdr.carp_version);
cp->cstat.ver_errors++;
goto err_out_skb_drop;
}
if (ch->carp_vhid != cp->hdr.carp_vhid)
{
log("CARP virtual host id mismatch: remote=%d, local=%d.\n",
ch->carp_vhid, cp->hdr.carp_vhid);
cp->cstat.vhid_errors++;
goto err_out_skb_drop;
}
if (carp_hmac_verify(cp, ch))
{
log("HMAC mismatch.\n");
cp->cstat.hmac_errors++;
goto err_out_skb_drop;
}
tmp_counter = ntohl(ch->carp_counter[0]);
tmp_counter = tmp_counter<<32;
tmp_counter += ntohl(ch->carp_counter[1]);
if (cp->state == BACKUP && ++cp->carp_adv_counter != tmp_counter)
{
log("Counter mismatch: remote=%llu, local=%llu.\n", tmp_counter, cp->carp_adv_counter);
cp->cstat.counter_errors++;
goto err_out_skb_drop;
}
cptv.tv_sec = cp->hdr.carp_advbase;
if (cp->hdr.carp_advbase < 240)
cptv.tv_usec = 240 * 1000000 / 256;
else
cptv.tv_usec = cp->hdr.carp_advskew * 1000000 / 256;
chtv.tv_sec = ch->carp_advbase;
chtv.tv_usec = ch->carp_advskew * 1000000 / 256;
/*log("local=%lu.%lu, remote=%lu.%lu, lcounter=%llu, remcounter=%llu, state=%d\n",
cptv.tv_sec, cptv.tv_usec,
chtv.tv_sec, chtv.tv_usec,
cp->carp_adv_counter, tmp_counter,
cp->state);
*/
set_bit(CARP_DATA_AVAIL, (long *)&cp->flags);
switch (cp->state)
{
case INIT:
if (timeval_before(&chtv, &cptv))
{
cp->carp_adv_counter = tmp_counter;
carp_set_state(cp, BACKUP);
}
else
{
carp_set_state(cp, MASTER);
}
break;
case MASTER:
if (timeval_before(&chtv, &cptv))
{
cp->carp_adv_counter = tmp_counter;
carp_set_state(cp, BACKUP);
}
break;
case BACKUP:
if (timeval_before(&cptv, &chtv))
{
carp_set_state(cp, MASTER);
}
break;
}
err_out_skb_drop:
kfree_skb(skb);
spin_unlock(&cp->lock);
return err;
}
static int carp_xmit(struct sk_buff *skb, struct net_device *dev)
{
#if 0
struct carp_priv *cp = (struct carp_priv *)dev->priv;
struct net_device_stats *stats = &cp->stat;
struct iphdr *iph = skb->nh.iph;
u8 tos;
u16 df;
struct rtable *rt;
struct net_device *tdev;
u32 dst;
int mtu;
int err;
int pkt_len = skb->len;
log("%s\n", __func__);
skb->ip_summed = CHECKSUM_NONE;
skb->protocol = htons(ETH_P_IP);
ip_select_ident(iph, &rt->u.dst, NULL);
ip_send_check(iph);
err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, dst_output);
if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) {
stats->tx_bytes += pkt_len;
stats->tx_packets++;
} else {
stats->tx_errors++;
stats->tx_aborted_errors++;
}
#endif
return 0;
}
static int carp_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
{
int err = 0;
struct carp_priv *cp = (struct carp_priv *)dev->priv;
struct net_device *tdev = NULL;
struct carp_ioctl_params p;
log("%s\n", __func__);
memset(&p, 0, sizeof(p));
err = -EPERM;
if (!capable(CAP_NET_ADMIN))
goto err_out;
switch (cmd)
{
#if 0
case SIOC_SETIPHDR:
log("Setting new header.\n");
err = -EFAULT;
if (copy_from_user(&iph, ifr->ifr_ifru.ifru_data, sizeof(iph)))
goto err_out;
err = -EINVAL;
if (iph.version != 4 || iph.protocol != IPPROTO_CARP || iph.ihl != 5 || !MULTICAST(iph.daddr))
goto err_out;
spin_lock(&cp->lock);
carp_close(cp->dev);
memcpy(&cp->iph, &iph, sizeof(iph));
carp_open(cp->dev);
spin_unlock(&cp->lock);
break;
#endif
case SIOC_SETCARPPARAMS:
err = -EFAULT;
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
goto err_out;
err = -EINVAL;
if (carp_check_params(p))
goto err_out;
log("Setting new CARP parameters.\n");
if (memcmp(p.devname, cp->odev->name, IFNAMSIZ) && (tdev = dev_get_by_name(p.devname)) != NULL)
carp_close(cp->dev);
spin_lock(&cp->lock);
if (tdev)
{
cp->odev->flags = cp->oflags;
dev_put(cp->odev);
cp->odev = tdev;
cp->link = cp->odev->ifindex;
cp->oflags = cp->odev->flags;
cp->odev->flags |= IFF_BROADCAST | IFF_ALLMULTI;
}
cp->md_timeout = p.md_timeout;
cp->adv_timeout = p.adv_timeout;
carp_set_state(cp, p.state);
memcpy(cp->carp_pad, p.carp_pad, sizeof(cp->carp_pad));
memcpy(cp->carp_key, p.carp_key, sizeof(cp->carp_key));
cp->hdr.carp_vhid = p.carp_vhid;
cp->hdr.carp_advbase = p.carp_advbase;
cp->hdr.carp_advskew = p.carp_advskew;
spin_unlock(&cp->lock);
if (tdev)
carp_open(cp->dev);
break;
case SIOC_GETCARPPARAMS:
log("Dumping CARP parameters.\n");
spin_lock(&cp->lock);
p.state = cp->state;
memcpy(p.carp_pad, cp->carp_pad, sizeof(cp->carp_pad));
memcpy(p.carp_key, cp->carp_key, sizeof(cp->carp_key));
p.carp_vhid = cp->hdr.carp_vhid;
p.carp_advbase = cp->hdr.carp_advbase;
p.carp_advskew = cp->hdr.carp_advskew;
p.md_timeout = cp->md_timeout;
p.adv_timeout = cp->adv_timeout;
memcpy(p.devname, cp->odev->name, sizeof(p.devname));
p.devname[sizeof(p.devname) - 1] = '\0';
spin_unlock(&cp->lock);
err = -EFAULT;
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
goto err_out;
break;
default:
err = -EINVAL;
break;
}
err = 0;
err_out:
return err;
}
static struct net_device_stats *carp_get_stats(struct net_device *dev)
{
struct carp_priv *cp = (struct carp_priv *)dev->priv;
struct carp_stat *cs = &cp->cstat;
log("%s: crc=%8d, ver=%8d, mem=%8d, xmit=%8d | bytes_sent=%8d\n",
__func__,
cs->crc_errors, cs->ver_errors, cs->mem_errors, cs->xmit_errors,
cs->bytes_sent);
return &(((struct carp_priv *)dev->priv)->stat);
}
static int carp_change_mtu(struct net_device *dev, int new_mtu)
{
log("%s\n", __func__);
dev->mtu = new_mtu;
return 0;
}
static void carp_setup(struct net_device *dev)
{
log("%s\n", __func__);
SET_MODULE_OWNER(dev);
dev->uninit = carp_uninit;
dev->destructor = free_netdev;
dev->hard_start_xmit = carp_xmit;
dev->get_stats = carp_get_stats;
dev->do_ioctl = carp_ioctl;
dev->change_mtu = carp_change_mtu;
dev->type = ARPHRD_ETHER;
dev->hard_header_len = LL_MAX_HEADER;
dev->mtu = 1500;
dev->flags = IFF_NOARP;
dev->iflink = 0;
dev->addr_len = 4;
}
static int carp_open(struct net_device *dev)
{
struct carp_priv *cp = (struct carp_priv *)dev->priv;
struct flowi fl = { .oif = cp->link,
.nl_u = { .ip4_u =
{ .daddr = cp->iph.daddr,
.saddr = cp->iph.saddr,
.tos = RT_TOS(cp->iph.tos) } },
.proto = IPPROTO_CARP };
struct rtable *rt;
if (ip_route_output_key(&rt, &fl))
return -EADDRNOTAVAIL;
dev = rt->u.dst.dev;
ip_rt_put(rt);
if (__in_dev_get(dev) == NULL)
return -EADDRNOTAVAIL;
cp->mlink = dev->ifindex;
ip_mc_inc_group(__in_dev_get(dev), cp->iph.daddr);
return 0;
}
static int carp_close(struct net_device *dev)
{
struct carp_priv *cp = (struct carp_priv *)dev->priv;
struct in_device *in_dev = inetdev_by_index(cp->mlink);
if (in_dev) {
ip_mc_dec_group(in_dev, cp->iph.daddr);
in_dev_put(in_dev);
}
return 0;
}
static int carp_init(struct net_device *dev)
{
struct net_device *tdev = NULL;
struct carp_priv *cp;
struct iphdr *iph;
int hlen = LL_MAX_HEADER;
int mtu = 1500;
log("%s - %s\n", __func__, dev->name);
cp = (struct carp_priv *)dev->priv;
iph = &cp->iph;
if (!iph->daddr || !MULTICAST(iph->daddr) || !iph->saddr)
return -EINVAL;
dev_hold(dev);
cp->dev = dev;
strncpy(cp->name, dev->name, IFNAMSIZ);
ip_eth_mc_map(cp->iph.daddr, dev->dev_addr);
memcpy(dev->broadcast, &iph->daddr, 4);
{
struct flowi fl = { .oif = cp->link,
.nl_u = { .ip4_u =
{ .daddr = iph->daddr,
.saddr = iph->saddr,
.tos = RT_TOS(iph->tos) } },
.proto = IPPROTO_CARP };
struct rtable *rt;
if (!ip_route_output_key(&rt, &fl)) {
tdev = rt->u.dst.dev;
ip_rt_put(rt);
}
}
cp->oflags = cp->odev->flags;
dev->flags |= IFF_BROADCAST | IFF_ALLMULTI;
cp->odev->flags |= IFF_BROADCAST | IFF_ALLMULTI;
dev->open = carp_open;
dev->stop = carp_close;
if (!tdev && cp->link)
tdev = __dev_get_by_index(cp->link);
if (tdev) {
hlen = tdev->hard_header_len;
mtu = tdev->mtu;
}
dev->iflink = cp->link;
dev->hard_header_len = hlen;
dev->mtu = mtu;
return 0;
}
static struct inet_protocol carp_protocol = {
.handler = carp_rcv,
.err_handler = carp_err,
};
static u32 inline addr2val(u8 a1, u8 a2, u8 a3, u8 a4)
{
u32 ret;
ret = ((a1 << 24) | (a2 << 16) | (a3 << 8) | (a4 << 0));
return htonl(ret);
}
static void carp_advertise(unsigned long data)
{
struct carp_priv *cp = (struct carp_priv *)data;
struct carp_header *ch = &cp->hdr;
struct carp_stat *cs = &cp->cstat;
struct sk_buff *skb;
int len;
struct ethhdr *eth;
struct iphdr *ip;
struct carp_header *c;
if (cp->state == BACKUP || !cp->odev)
return;
len = sizeof(struct iphdr) + sizeof(struct carp_header) + sizeof(struct ethhdr);
skb = alloc_skb(len + 2, GFP_ATOMIC);
if (!skb)
{
log("Failed to allocate new carp frame.\n");
cs->mem_errors++;
goto out;
}
skb_reserve(skb, 16);
eth = (struct ethhdr *) skb_push(skb, 14);
ip = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
c = (struct carp_header *)skb_put(skb, sizeof(struct carp_header));
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
ip_eth_mc_map(cp->iph.daddr, eth->h_dest);
memcpy(eth->h_source, cp->odev->dev_addr, ETH_ALEN);
eth->h_proto = htons(ETH_P_IP);
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = htons(len - sizeof(struct ethhdr));
ip->frag_off = 0;
ip->ttl = CARP_TTL;
ip->protocol = IPPROTO_CARP;
ip->check = 0;
ip->saddr = cp->iph.saddr;
ip->daddr = cp->iph.daddr;
get_random_bytes(&ip->id, 2);
ip_send_check(ip);
memcpy(c, ch, sizeof(struct carp_header));
spin_lock(&cp->lock);
cp->carp_adv_counter++;
spin_unlock(&cp->lock);
ch->carp_counter[1] = htonl(cp->carp_adv_counter & 0xffffffff);
ch->carp_counter[0] = htonl((cp->carp_adv_counter >> 32) & 0xffffffff);
carp_hmac_sign(cp, ch);
skb->protocol = __constant_htons(ETH_P_IP);
skb->mac.raw = ((u8 *)ip) - 14;
skb->dev = cp->odev;
skb->pkt_type = PACKET_MULTICAST;
spin_lock_bh(&cp->odev->xmit_lock);
if (!netif_queue_stopped(cp->odev))
{
atomic_inc(&skb->users);
if (cp->odev->hard_start_xmit(skb, cp->odev))
{
atomic_dec(&skb->users);
cs->xmit_errors++;
log("Hard xmit error.\n");
}
cs->bytes_sent += len;
}
spin_unlock_bh(&cp->odev->xmit_lock);
mod_timer(&cp->adv_timer, jiffies + cp->adv_timeout*HZ);
kfree_skb(skb);
out:
return;
}
static int __init device_carp_init(void)
{
int err;
struct carp_priv *cp;
printk(KERN_INFO "CARP driver.\n");
carp_dev = alloc_netdev(sizeof(struct carp_priv), "carp0", carp_setup);
if (!carp_dev)
{
printk(KERN_ERR "Failed to allocate CARP network device structure.\n");
return -ENOMEM;
}
if (inet_add_protocol(&carp_protocol, IPPROTO_CARP) < 0)
{
printk(KERN_INFO "Failed to add CARP protocol.\n");
err = -EAGAIN;
goto err_out_mem_free;
}
cp = carp_dev->priv;
cp->iph.saddr = addr2val(10, 0, 0, 3);
cp->iph.daddr = addr2val(224, 0, 1, 10);
cp->iph.tos = 0;
cp->md_timeout = 3;
cp->adv_timeout = 1;
cp->state = INIT;
spin_lock_init(&cp->lock);
cp->odev = dev_get_by_name("eth0");
if (cp->odev)
{
cp->link = cp->odev->ifindex;
cp->iph.saddr = (__in_dev_get(cp->odev))->ifa_list[0].ifa_address;
}
memset(cp->carp_key, 1, sizeof(cp->carp_key));
get_random_bytes(&cp->carp_adv_counter, 8);
get_random_bytes(&cp->hdr.carp_advskew, 1);
get_random_bytes(&cp->hdr.carp_advbase, 1);
dump_addr_info(cp);
init_timer(&cp->md_timer);
cp->md_timer.expires = jiffies + cp->md_timeout*HZ;
cp->md_timer.data = (unsigned long)cp;
cp->md_timer.function = carp_master_down;
init_timer(&cp->adv_timer);
cp->adv_timer.expires = jiffies + cp->adv_timeout*HZ;
cp->adv_timer.data = (unsigned long)cp;
cp->adv_timer.function = carp_advertise;
carp_dev->init = carp_init;
cp->tfm = crypto_alloc_tfm("sha1", 0);
if (!cp->tfm)
{
printk(KERN_ERR "Failed to allocate SHA1 tfm.\n");
err = -EINVAL;
goto err_out_del_protocol;
}
dump_hmac_params(cp);
err = carp_init_queues();
if (err)
goto err_out_crypto_free;
if ((err = register_netdev(carp_dev)))
goto err_out_fini_carp_queues;
add_timer(&cp->md_timer);
return err;
err_out_fini_carp_queues:
carp_fini_queues();
err_out_crypto_free:
crypto_free_tfm(cp->tfm);
err_out_del_protocol:
inet_del_protocol(&carp_protocol, IPPROTO_CARP);
err_out_mem_free:
free_netdev(carp_dev);
return err;
}
void device_carp_fini(void)
{
struct carp_priv *cp = carp_dev->priv;
carp_fini_queues();
if (inet_del_protocol(&carp_protocol, IPPROTO_CARP) < 0)
printk(KERN_INFO "Failed to remove CARP protocol handler.\n");
crypto_free_tfm(cp->tfm);
unregister_netdev(carp_dev);
}
module_init(device_carp_init);
module_exit(device_carp_fini);
MODULE_LICENSE("GPL");
[-- Attachment #1.3: carp.h --]
[-- Type: text/x-chdr, Size: 2283 bytes --]
/*
* carp.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_H
#define __CARP_H
#include <linux/netdevice.h>
#include <linux/if.h>
#include <linux/ip.h>
#include "carp_ioctl.h"
#define IPPROTO_CARP 112
#define CARP_VERSION 2
#define CARP_TTL 255
#define CARP_SIG_LEN 20
/*
* carp_priv->flags definitions.
*/
#define CARP_DATA_AVAIL (1<<0)
struct carp_header
{
#if defined(__LITTLE_ENDIAN_BITFIELD)
u8 carp_type:4,
carp_version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
u8 carp_version:4,
carp_type:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
u8 carp_vhid;
u8 carp_advskew;
u8 carp_authlen;
u8 carp_pad1;
u8 carp_advbase;
u16 carp_cksum;
u32 carp_counter[2];
u8 carp_md[CARP_SIG_LEN];
};
struct carp_stat
{
u32 crc_errors;
u32 ver_errors;
u32 vhid_errors;
u32 hmac_errors;
u32 counter_errors;
u32 mem_errors;
u32 xmit_errors;
u32 bytes_sent;
};
struct carp_priv
{
struct net_device_stats stat;
struct net_device *dev, *odev;
char name[IFNAMSIZ];
int link, mlink;
struct iphdr iph;
u32 md_timeout, adv_timeout;
struct timer_list md_timer, adv_timer;
enum carp_state state;
struct carp_header hdr;
struct carp_stat cstat;
u8 carp_key[CARP_KEY_LEN];
u8 carp_pad[CARP_HMAC_PAD_LEN];
struct crypto_tfm *tfm;
u64 carp_adv_counter;
spinlock_t lock;
u32 flags;
unsigned short oflags;
};
#endif /* __CARP_H */
[-- Attachment #1.4: carp_ioctl.h --]
[-- Type: text/x-chdr, Size: 1484 bytes --]
/*
* carp_ioctl.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_IOCTL_H
#define __CARP_IOCTL_H
#include <linux/sockios.h>
#define CARP_KEY_LEN 20
#define CARP_HMAC_PAD_LEN 64
#define MAX_MD_TIMEOUT 5
#define MAX_ADV_TIMEOUT 5
enum carp_state {INIT = 0, MASTER, BACKUP};
enum carp_ioctls
{
SIOC_SETCARPPARAMS = SIOCDEVPRIVATE,
SIOC_GETCARPPARAMS,
};
struct carp_ioctl_params
{
__u8 carp_advskew;
__u8 carp_advbase;
__u8 carp_vhid;
__u8 carp_key[CARP_KEY_LEN];
__u8 carp_pad[CARP_HMAC_PAD_LEN];
enum carp_state state;
char devname[IFNAMSIZ];
__u32 md_timeout;
__u32 adv_timeout;
};
#endif /* __CARP_IOCTL_H */
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
next parent reply other threads:[~2004-07-15 13:36 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <1089898303.6114.859.camel@uganda>
2004-07-15 13:36 ` Evgeniy Polyakov [this message]
2004-07-15 14:44 ` [1/2] CARP implementation. HA master's failover jamal
2004-07-15 15:27 ` Evgeniy Polyakov
2004-07-15 15:55 ` Evgeniy Polyakov
2004-07-15 16:28 ` jamal
2004-07-15 16:59 ` Evgeniy Polyakov
2004-07-15 17:30 ` jamal
2004-07-15 19:20 ` Evgeniy Polyakov
2004-07-16 12:34 ` jamal
2004-07-16 15:06 ` Evgeniy Polyakov
2004-07-17 11:52 ` jamal
2004-07-17 12:59 ` Evgeniy Polyakov
2004-07-17 15:47 ` jamal
2004-07-17 20:04 ` Evgeniy Polyakov
2004-07-15 16:07 ` jamal
2004-07-15 16:59 ` Evgeniy Polyakov
2004-07-15 17:24 ` jamal
2004-07-15 19:53 ` Evgeniy Polyakov
2004-07-16 13:04 ` jamal
2004-07-16 15:06 ` Evgeniy Polyakov
2004-07-17 12:47 ` jamal
2004-07-17 14:00 ` Evgeniy Polyakov
2004-07-17 16:29 ` jamal
2004-07-17 20:03 ` Evgeniy Polyakov
2004-07-17 20:32 ` jamal
2004-07-19 7:16 ` [nf-failover] " KOVACS Krisztian
2004-07-20 2:38 ` Harald Welte
2004-07-20 14:24 ` jamal
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1089898595.6114.866.camel@uganda \
--to=johnpol@2ka.mipt.ru \
--cc=netdev@oss.sgi.com \
--cc=netfilter-failover@lists.netfilter.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.