Bug #664 » carp-20070529BR01.patch
etc/protocols | ||
---|---|---|
snp 109 SNP # Sitara Networks Protocol
|
||
compaq-peer 110 Compaq-Peer # Compaq Peer Protocol
|
||
ipx-in-ip 111 IPX-in-IP # IPX in IP
|
||
vrrp 112 VRRP # Virtual Router Redundancy Protocol
|
||
carp 112 CARP vrrp # Common Address Redundancy Protocol
|
||
pgm 113 PGM # PGM Reliable Transport Protocol
|
||
# 114 # any 0-hop protocol
|
||
l2tp 115 L2TP # Layer Two Tunneling Protocol
|
sbin/ifconfig/Makefile | ||
---|---|---|
SRCS+= ifvlan.c # SIOC[GS]ETVLAN support
|
||
SRCS+= ifieee80211.c # SIOC[GS]IEEE80211 support
|
||
#SRCS+= ifcarp.c # SIOC[GS]VH support
|
||
SRCS+= ifcarp.c # SIOC[GS]VH support
|
||
#SRCS+= ifpfsync.c # pfsync(4) support
|
||
SRCS+= ifbridge.c # bridge support
|
sbin/ifconfig/ifcarp.c | ||
---|---|---|
/* $Id$ */
|
||
/* from $FreeBSD: src/sbin/ifconfig/ifcarp.c,v 1.2 2005/02/22 14:07:47 glebius Exp $ */
|
||
/* from $OpenBSD: ifconfig.c,v 1.82 2003/10/19 05:43:35 mcbride Exp $ */
|
||
/*
|
||
* Copyright (c) 2002 Michael Shalayeff. All rights reserved.
|
||
* Copyright (c) 2003 Ryan McBride. All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
|
||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
#include <sys/param.h>
|
||
#include <sys/ioctl.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/sockio.h>
|
||
#include <stdlib.h>
|
||
#include <unistd.h>
|
||
#include <net/ethernet.h>
|
||
#include <net/if.h>
|
||
#include <netinet/ip_carp.h>
|
||
#include <net/route.h>
|
||
#include <ctype.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <unistd.h>
|
||
#include <err.h>
|
||
#include <errno.h>
|
||
#include "ifconfig.h"
|
||
static const char *carp_states[] = { CARP_STATES };
|
||
void carp_status(int s);
|
||
void setcarp_advbase(const char *,int, int, const struct afswtch *rafp);
|
||
void setcarp_advskew(const char *, int, int, const struct afswtch *rafp);
|
||
void setcarp_passwd(const char *, int, int, const struct afswtch *rafp);
|
||
void setcarp_vhid(const char *, int, int, const struct afswtch *rafp);
|
||
void
|
||
carp_status(int s)
|
||
{
|
||
const char *state;
|
||
struct carpreq carpr;
|
||
memset((char *)&carpr, 0, sizeof(struct carpreq));
|
||
ifr.ifr_data = (caddr_t)&carpr;
|
||
if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
|
||
return;
|
||
if (carpr.carpr_vhid > 0) {
|
||
if (carpr.carpr_state > CARP_MAXSTATE)
|
||
state = "<UNKNOWN>";
|
||
else
|
||
state = carp_states[carpr.carpr_state];
|
||
printf("\tcarp: %s vhid %d advbase %d advskew %d\n",
|
||
state, carpr.carpr_vhid, carpr.carpr_advbase,
|
||
carpr.carpr_advskew);
|
||
}
|
||
return;
|
||
}
|
||
void
|
||
setcarp_passwd(const char *val, int d, int s, const struct afswtch *afp)
|
||
{
|
||
struct carpreq carpr;
|
||
memset((char *)&carpr, 0, sizeof(struct carpreq));
|
||
ifr.ifr_data = (caddr_t)&carpr;
|
||
if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
|
||
err(1, "SIOCGVH");
|
||
/* XXX Should hash the password into the key here, perhaps? */
|
||
strlcpy(carpr.carpr_key, val, CARP_KEY_LEN);
|
||
if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
|
||
err(1, "SIOCSVH");
|
||
return;
|
||
}
|
||
void
|
||
setcarp_vhid(const char *val, int d, int s, const struct afswtch *afp)
|
||
{
|
||
int vhid;
|
||
struct carpreq carpr;
|
||
vhid = atoi(val);
|
||
if (vhid <= 0)
|
||
errx(1, "vhid must be greater than 0");
|
||
memset((char *)&carpr, 0, sizeof(struct carpreq));
|
||
ifr.ifr_data = (caddr_t)&carpr;
|
||
if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
|
||
err(1, "SIOCGVH");
|
||
carpr.carpr_vhid = vhid;
|
||
if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
|
||
err(1, "SIOCSVH");
|
||
return;
|
||
}
|
||
void
|
||
setcarp_advskew(const char *val, int d, int s, const struct afswtch *afp)
|
||
{
|
||
int advskew;
|
||
struct carpreq carpr;
|
||
advskew = atoi(val);
|
||
memset((char *)&carpr, 0, sizeof(struct carpreq));
|
||
ifr.ifr_data = (caddr_t)&carpr;
|
||
if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
|
||
err(1, "SIOCGVH");
|
||
carpr.carpr_advskew = advskew;
|
||
if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
|
||
err(1, "SIOCSVH");
|
||
return;
|
||
}
|
||
void
|
||
setcarp_advbase(const char *val, int d, int s, const struct afswtch *afp)
|
||
{
|
||
int advbase;
|
||
struct carpreq carpr;
|
||
advbase = atoi(val);
|
||
memset((char *)&carpr, 0, sizeof(struct carpreq));
|
||
ifr.ifr_data = (caddr_t)&carpr;
|
||
if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
|
||
err(1, "SIOCGVH");
|
||
carpr.carpr_advbase = advbase;
|
||
if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
|
||
err(1, "SIOCSVH");
|
||
return;
|
||
}
|
||
static struct cmd carp_cmds[] = {
|
||
DEF_CMD_ARG("advbase", setcarp_advbase),
|
||
DEF_CMD_ARG("advskew", setcarp_advskew),
|
||
DEF_CMD_ARG("pass", setcarp_passwd),
|
||
DEF_CMD_ARG("vhid", setcarp_vhid),
|
||
};
|
||
static struct afswtch af_carp = {
|
||
.af_name = "af_carp",
|
||
.af_af = AF_UNSPEC,
|
||
.af_other_status = carp_status,
|
||
};
|
||
static __constructor void
|
||
carp_ctor(void)
|
||
{
|
||
#define N(a) (sizeof(a) / sizeof(a[0]))
|
||
int i;
|
||
for (i = 0; i < N(carp_cmds); i++)
|
||
cmd_register(&carp_cmds[i]);
|
||
af_register(&af_carp);
|
||
#undef N
|
||
}
|
sbin/ifconfig/ifconfig.8 | ||
---|---|---|
.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
|
||
.\" $FreeBSD: src/sbin/ifconfig/ifconfig.8,v 1.124 2006/10/10 09:44:08 ru Exp $
|
||
.\" $DragonFly: src/sbin/ifconfig/ifconfig.8,v 1.19 2007/04/09 21:20:37 swildner Exp $
|
||
.\"
|
||
.Dd November 19, 2006
|
||
.Dd April 8, 2007
|
||
.Dt IFCONFIG 8
|
||
.Os
|
||
.Sh NAME
|
||
... | ... | |
argument is useless and hence deprecated.
|
||
.El
|
||
.Pp
|
||
The following parameters are specific to
|
||
.Xr carp 4
|
||
interfaces:
|
||
.Bl -tag -width indent
|
||
.It Cm advbase Ar seconds
|
||
Specifies the base of the advertisement interval in seconds.
|
||
The acceptable values are 1 to 255.
|
||
The default value is 1.
|
||
.\" The default value is
|
||
.\" .Dv CARP_DFLTINTV .
|
||
.It Cm advskew Ar interval
|
||
Specifies the skew to add to the base advertisement interval to
|
||
make one host advertise slower than another host.
|
||
It is specified in 1/256 of seconds.
|
||
The acceptable values are 1 to 254.
|
||
The default value is 0.
|
||
.It Cm pass Ar phrase
|
||
Set the authentication key to
|
||
.Ar phrase .
|
||
.It Cm vhid Ar n
|
||
Set the virtual host ID.
|
||
This is a required setting.
|
||
Acceptable values are 1 to 255.
|
||
.El
|
||
.Pp
|
||
The
|
||
.Nm
|
||
utility displays the current configuration for a network interface
|
||
... | ... | |
tried to alter an interface's configuration.
|
||
.Sh SEE ALSO
|
||
.Xr netstat 1 ,
|
||
.Xr carp 4 ,
|
||
.Xr ifmedia 4 ,
|
||
.Xr netintro 4 ,
|
||
.Xr polling 4 ,
|
sbin/ifconfig/ifmedia.c | ||
---|---|---|
else
|
||
printf("no carrier");
|
||
break;
|
||
case IFM_CARP:
|
||
if (ifmr.ifm_status & IFM_ACTIVE)
|
||
printf("master");
|
||
else
|
||
printf("backup");
|
||
break;
|
||
}
|
||
putchar('\n');
|
||
}
|
share/man/man4/Makefile | ||
---|---|---|
bridge.4 \
|
||
bt.4 \
|
||
cardbus.4 \
|
||
carp.4 \
|
||
ccd.4 \
|
||
cd.4 \
|
||
ch.4 \
|
share/man/man4/carp.4 | ||
---|---|---|
.\" $OpenBSD: carp.4,v 1.16 2004/12/07 23:41:35 jmc Exp $
|
||
.\" $FreeBSD: src/share/man/man4/carp.4,v 1.10 2006/06/07 10:26:51 glebius Exp $
|
||
.\" $Id$
|
||
.\"
|
||
.\" Copyright (c) 2003, Ryan McBride. All rights reserved.
|
||
.\"
|
||
.\" Redistribution and use in source and binary forms, with or without
|
||
.\" modification, are permitted provided that the following conditions
|
||
.\" are met:
|
||
.\" 1. Redistributions of source code must retain the above copyright
|
||
.\" notice, this list of conditions and the following disclaimer.
|
||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||
.\" notice, this list of conditions and the following disclaimer in the
|
||
.\" documentation and/or other materials provided with the distribution.
|
||
.\"
|
||
.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
.\" SUCH DAMAGE.
|
||
.\"
|
||
.\"
|
||
.Dd April 9, 2007
|
||
.Dt CARP 4
|
||
.Os
|
||
.Sh NAME
|
||
.Nm carp
|
||
.Nd Common Address Redundancy Protocol
|
||
.Sh SYNOPSIS
|
||
.Cd "device carp"
|
||
.Sh DESCRIPTION
|
||
The
|
||
.Nm
|
||
interface is a pseudo-device that implements and controls the
|
||
CARP protocol.
|
||
CARP allows multiple hosts on the same local network to share a set of IP addresses.
|
||
Its primary purpose is to ensure that these
|
||
addresses are always available, but in some configurations
|
||
.Nm
|
||
can also provide load balancing functionality.
|
||
.Pp
|
||
A
|
||
.Nm
|
||
interface can be created at runtime using the
|
||
.Nm ifconfig Li carp Ns Ar N Cm create
|
||
command or by configuring
|
||
it via
|
||
.Va cloned_interfaces
|
||
in the
|
||
.Pa /etc/rc.conf
|
||
file.
|
||
.Pp
|
||
To use
|
||
.Nm ,
|
||
the administrator needs to configure at minimum a common virtual host ID (VHID)
|
||
and virtual host IP address on each machine which is to take part in the virtual
|
||
group.
|
||
Additional parameters can also be set on a per-interface basis:
|
||
.Cm advbase
|
||
and
|
||
.Cm advskew ,
|
||
which are used to control how frequently the host sends advertisements when it
|
||
is the master for a virtual host, and
|
||
.Cm pass
|
||
which is used to authenticate
|
||
.Nm
|
||
advertisements.
|
||
The
|
||
.Cm advbase
|
||
parameter stands for
|
||
.Dq "advertisement base" .
|
||
It is measured in seconds and specifies the base of the advertisement interval.
|
||
The
|
||
.Cm advskew
|
||
parameter stands for
|
||
.Dq "advertisement skew" .
|
||
It is measured in 1/256 of seconds.
|
||
It is added to the base advertisement interval to make one host advertise
|
||
a bit slower that the other does.
|
||
Both
|
||
.Cm advbase
|
||
and
|
||
.Cm advskew
|
||
are put inside CARP advertisements.
|
||
These configurations can be done using
|
||
.Xr ifconfig 8 ,
|
||
or through the
|
||
.Dv SIOCSVH
|
||
.Xr ioctl 2 .
|
||
.Pp
|
||
Additionally, there are a number of global parameters which can be set using
|
||
.Xr sysctl 8 :
|
||
.Bl -tag -width ".Va net.inet.carp.arpbalance"
|
||
.It Va net.inet.carp.allow
|
||
Accept incoming
|
||
.Nm
|
||
packets.
|
||
Enabled by default.
|
||
.It Va net.inet.carp.preempt
|
||
Allow virtual hosts to preempt each other.
|
||
It is also used to failover
|
||
.Nm
|
||
interfaces as a group.
|
||
When the option is enabled and one of the
|
||
.Nm
|
||
enabled physical interfaces
|
||
goes down,
|
||
.Cm advskew
|
||
is changed to 240 on all
|
||
.Nm
|
||
interfaces.
|
||
See also the first example.
|
||
Disabled by default.
|
||
.It Va net.inet.carp.log
|
||
Value of 0 disables any logging.
|
||
Value of 1 enables logging of bad
|
||
.Nm
|
||
packets.
|
||
Values above 1 enable logging state changes of
|
||
.Nm
|
||
interfaces.
|
||
Default value is 1.
|
||
.It Va net.inet.carp.arpbalance
|
||
Balance local traffic using ARP (see below).
|
||
Disabled by default.
|
||
.It Va net.inet.carp.suppress_preempt
|
||
A read only value showing the status of preemption suppression.
|
||
Preemption can be suppressed if link on an interface is down
|
||
or when
|
||
.Xr pfsync 4
|
||
interface is not synchronized.
|
||
Value of 0 means that preemption is not suppressed, since no
|
||
problems are detected.
|
||
Every problem increments suppression counter.
|
||
.El
|
||
.Sh ARP level load balancing
|
||
The
|
||
.Nm
|
||
has limited abilities for load balancing the incoming connections
|
||
between hosts in Ethernet network.
|
||
For load balancing operation, one needs several CARP interfaces that
|
||
are configured to the same IP address, but to a different VHIDs.
|
||
Once an ARP request is received, the CARP protocol will use a hashing
|
||
function against the source IP address in the ARP request to determine
|
||
which VHID should this request belong to.
|
||
If the corresponding CARP interface is in master state, the ARP request
|
||
will be replied, otherwise it will be ignored.
|
||
See the
|
||
.Sx EXAMPLES
|
||
section for a practical example of load balancing.
|
||
.Pp
|
||
The ARP load balancing has some limitations.
|
||
First, ARP balancing only works on the local network segment.
|
||
It cannot balance traffic that crosses a router, because the
|
||
router itself will always be balanced to the same virtual host.
|
||
Second, ARP load balancing can lead to asymmetric routing
|
||
of incoming and outgoing traffic, and thus combining it with
|
||
.Xr pfsync 4
|
||
is dangerous, because this creates a race condition between
|
||
balanced routers and a host they are serving.
|
||
Imagine an incoming packet creating state on the first router, being
|
||
forwarded to its destination, and destination replying faster
|
||
than the state information is packed and synced with the second router.
|
||
If the reply would be load balanced to second router, it will be
|
||
dropped due to no state.
|
||
.Sh EXAMPLES
|
||
For firewalls and routers with multiple interfaces, it is desirable to
|
||
failover all of the
|
||
.Nm
|
||
interfaces together, when one of the physical interfaces goes down.
|
||
This is achieved by the preempt option.
|
||
Enable it on both host A and B:
|
||
.Pp
|
||
.Dl sysctl net.inet.carp.preempt=1
|
||
.Pp
|
||
Assume that host A is the preferred master and 192.168.1.x/24 is
|
||
configured on one physical interface and 192.168.2.y/24 on another.
|
||
This is the setup for host A:
|
||
.Bd -literal -offset indent
|
||
ifconfig carp0 create
|
||
ifconfig carp0 vhid 1 pass mekmitasdigoat 192.168.1.1/24
|
||
ifconfig carp1 create
|
||
ifconfig carp1 vhid 2 pass mekmitasdigoat 192.168.2.1/24
|
||
.Ed
|
||
.Pp
|
||
The setup for host B is identical, but it has a higher
|
||
.Cm advskew :
|
||
.Bd -literal -offset indent
|
||
ifconfig carp0 create
|
||
ifconfig carp0 vhid 1 advskew 100 pass mekmitasdigoat 192.168.1.1/24
|
||
ifconfig carp1 create
|
||
ifconfig carp1 vhid 2 advskew 100 pass mekmitasdigoat 192.168.2.1/24
|
||
.Ed
|
||
.Pp
|
||
Because of the preempt option, when one of the physical interfaces of
|
||
host A fails,
|
||
.Cm advskew
|
||
is adjusted to 240 on all its
|
||
.Nm
|
||
interfaces.
|
||
This will cause host B to preempt on both interfaces instead of
|
||
just the failed one.
|
||
.Pp
|
||
In order to set up an ARP balanced virtual host, it is necessary to configure
|
||
one virtual host for each physical host which would respond to ARP requests
|
||
and thus handle the traffic.
|
||
In the following example, two virtual hosts are configured on two hosts to
|
||
provide balancing and failover for the IP address 192.168.1.10.
|
||
.Pp
|
||
First the
|
||
.Nm
|
||
interfaces on host A are configured.
|
||
The
|
||
.Cm advskew
|
||
of 100 on the second virtual host means that its advertisements will be sent
|
||
out slightly less frequently.
|
||
.Bd -literal -offset indent
|
||
ifconfig carp0 create
|
||
ifconfig carp0 vhid 1 pass mekmitasdigoat 192.168.1.10/24
|
||
ifconfig carp1 create
|
||
ifconfig carp1 vhid 2 advskew 100 pass mekmitasdigoat 192.168.1.10/24
|
||
.Ed
|
||
.Pp
|
||
The configuration for host B is identical, except the
|
||
.Cm advskew
|
||
is on virtual host 1 rather than virtual host 2.
|
||
.Bd -literal -offset indent
|
||
ifconfig carp0 create
|
||
ifconfig carp0 vhid 1 advskew 100 pass mekmitasdigoat 192.168.1.10/24
|
||
ifconfig carp1 create
|
||
ifconfig carp1 vhid 2 pass mekmitasdigoat 192.168.1.10/24
|
||
.Ed
|
||
.Pp
|
||
Finally, the ARP balancing feature must be enabled on both hosts:
|
||
.Pp
|
||
.Dl sysctl net.inet.carp.arpbalance=1
|
||
.Pp
|
||
When the hosts receive an ARP request for 192.168.1.10, the source IP address
|
||
of the request is used to compute which virtual host should answer the request.
|
||
The host which is master of the selected virtual host will reply to the
|
||
request, the other(s) will ignore it.
|
||
.Pp
|
||
This way, locally connected systems will receive different ARP replies and
|
||
subsequent IP traffic will be balanced among the hosts.
|
||
If one of the hosts fails, the other will take over the virtual MAC address,
|
||
and begin answering ARP requests on its behalf.
|
||
.Sh SEE ALSO
|
||
.Xr inet 4 ,
|
||
.Xr pfsync 4 ,
|
||
.Xr rc.conf 5 ,
|
||
.Xr ifconfig 8 ,
|
||
.Xr sysctl 8
|
||
.Sh HISTORY
|
||
The
|
||
.Nm
|
||
device first appeared in
|
||
.Ox 3.5 .
|
share/man/man4/pfsync.4 | ||
---|---|---|
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
.\"
|
||
.Dd November 29, 2002
|
||
.Dd April 9, 2007
|
||
.Dt PFSYNC 4
|
||
.Os
|
||
.Sh NAME
|
||
... | ... | |
# ifconfig pfsync0 up syncif fxp0 maxupd 64
|
||
# tcpdump -s1500 -evtni pfsync0
|
||
.Ed
|
||
.Sh USING PFSYNC WITH CARP
|
||
.Nm
|
||
and
|
||
.Xr carp 4
|
||
can be used together to provide automatic failover of a pair of firewalls
|
||
configured in parallel.
|
||
One firewall handles all traffic \- if it dies or
|
||
is shut down, the second firewall takes over automatically.
|
||
.Pp
|
||
Both firewalls in this example have three
|
||
.Xr sis 4
|
||
interfaces.
|
||
sis0 is the external interface, on the 10.0.0.0/24 subnet; sis1 is the
|
||
internal interface, on the 192.168.0.0/24 subnet; and sis2 is the
|
||
.Nm
|
||
interface, using the 192.168.254.0/24 subnet.
|
||
A crossover cable connects the two firewalls via their sis2 interfaces.
|
||
On all three interfaces, firewall A uses the .254 address, while firewall B
|
||
uses .253.
|
||
The interfaces are configured as follows (firewall A unless otherwise
|
||
indicated):
|
||
.Pp
|
||
Interfaces configuration in
|
||
.Pa /etc/rc.conf :
|
||
.Bd -literal -offset indent
|
||
network_interfaces="lo0 sis0 sis1 sis2"
|
||
cloned_interfaces="carp0 carp1"
|
||
ifconfig_sis0="10.0.0.254/24"
|
||
ifconfig_sis1="192.168.0.254/24"
|
||
ifconfig_sis2="192.168.254.254/24"
|
||
ifconfig_carp0="vhid 1 pass foo 10.0.0.1/24"
|
||
ifconfig_carp1="vhid 2 pass bar 192.168.0.1/24"
|
||
pfsync_enable="YES"
|
||
pfsync_syncdev="sis2"
|
||
.Ed
|
||
.Pp
|
||
.Xr pf 4
|
||
must also be configured to allow
|
||
.Nm
|
||
and
|
||
.Xr carp 4
|
||
traffic through.
|
||
The following should be added to the top of
|
||
.Pa /etc/pf.conf :
|
||
.Bd -literal -offset indent
|
||
pass quick on { sis2 } proto pfsync
|
||
pass quick on { sis0 sis1 } proto carp keep state
|
||
.Ed
|
||
.Pp
|
||
If it is preferable that one firewall handle the traffic,
|
||
the
|
||
.Ar advskew
|
||
on the backup firewall's
|
||
.Xr carp 4
|
||
interfaces should be set to something higher than
|
||
the primary's.
|
||
For example, if firewall B is the backup, its
|
||
carp1 configuration would look like this:
|
||
.Bd -literal -offset indent
|
||
ifconfig_carp1="vhid 2 pass bar advskew 100 192.168.0.1/24"
|
||
.Ed
|
||
.Pp
|
||
The following must also be added to
|
||
.Pa /etc/sysctl.conf :
|
||
.Bd -literal -offset indent
|
||
net.inet.carp.preempt=1
|
||
.Ed
|
||
.Sh SEE ALSO
|
||
.Xr tcpdump 1 ,
|
||
.Xr carp 4 ,
|
||
.Xr bpf 4 ,
|
||
.Xr inet 4 ,
|
||
.Xr inet6 4 ,
|
sys/conf/files | ||
---|---|---|
crypto/rijndael/rijndael-alg-fst.c optional ipsec ipsec_esp
|
||
crypto/rijndael/rijndael-api-fst.c optional ipsec ipsec_esp
|
||
crypto/sha1.c optional ipsec
|
||
crypto/sha1.c optional carp
|
||
crypto/sha2/sha2.c optional ipsec
|
||
ddb/db_access.c optional ddb
|
||
ddb/db_kld.c optional ddb
|
||
... | ... | |
netinet/igmp.c optional inet
|
||
netinet/in.c optional inet
|
||
netinet/in_cksum.c optional inet
|
||
netinet/ip_carp.c optional carp
|
||
netinet/ip_gre.c optional gre inet
|
||
netinet/ip_id.c optional inet
|
||
netinet/in_pcb.c optional inet
|
sys/conf/options | ||
---|---|---|
BOOTP_NFSROOT opt_bootp.h
|
||
BOOTP_NFSV3 opt_bootp.h
|
||
BOOTP_WIRED_TO opt_bootp.h
|
||
CARP opt_carp.h
|
||
ETHER_II opt_ef.h
|
||
ETHER_8023 opt_ef.h
|
||
ETHER_8022 opt_ef.h
|
sys/config/LINT | ||
---|---|---|
device pfsync
|
||
device pflog
|
||
#CARP
|
||
pseudo-device carp
|
||
options CARP
|
||
# The MBUF_STRESS_TEST option enables options which create
|
||
# various random failures / extreme cases related to mbuf
|
||
# functions. See the mbuf(9) manpage for a list of available
|
sys/config/VKERNEL | ||
---|---|---|
options DDB_TRACE
|
||
options INVARIANTS
|
||
options CARP
|
||
# Floating point support - do not disable.
|
||
device npx0 at nexus?
|
||
... | ... | |
pseudo-device gif # IPv6 and IPv4 tunneling
|
||
pseudo-device faith 1 # IPv6-to-IPv4 relaying (translation)
|
||
pseudo-device carp
|
||
# The `bpf' pseudo-device enables the Berkeley Packet Filter.
|
||
# Be aware of the administrative consequences of enabling this!
|
||
pseudo-device bpf #Berkeley packet filter
|
||
... | ... | |
device vn
|
||
device vkd
|
||
device vke
|
||
sys/net/if_ethersubr.c | ||
---|---|---|
#include "opt_inet6.h"
|
||
#include "opt_ipx.h"
|
||
#include "opt_netgraph.h"
|
||
#include "opt_carp.h"
|
||
#include <sys/param.h>
|
||
#include <sys/systm.h>
|
||
... | ... | |
#include <netinet6/nd6.h>
|
||
#endif
|
||
#ifdef CARP
|
||
#include <netinet/ip_carp.h>
|
||
#endif
|
||
#ifdef IPX
|
||
#include <netproto/ipx/ipx.h>
|
||
#include <netproto/ipx/ipx_if.h>
|
||
... | ... | |
}
|
||
}
|
||
#ifdef CARP
|
||
if (ifp->if_carp && (error = carp_output(ifp, m, dst, NULL)))
|
||
goto bad;
|
||
#endif
|
||
|
||
/* Handle ng_ether(4) processing, if any */
|
||
if (ng_ether_output_p != NULL) {
|
||
if ((error = (*ng_ether_output_p)(ifp, &m)) != 0)
|
||
... | ... | |
if (rule) /* packet is passing the second time */
|
||
goto post_stats;
|
||
#ifdef CARP
|
||
/*
|
||
* XXX: Okay, we need to call carp_forus() and - if it is for
|
||
* us jump over code that does the normal check
|
||
* "ac_enaddr == ether_dhost". The check sequence is a bit
|
||
* different from OpenBSD, so we jump over as few code as
|
||
* possible, to catch _all_ sanity checks. This needs
|
||
* evaluation, to see if the carp ether_dhost values break any
|
||
* of these checks!
|
||
*/
|
||
if (ifp->if_carp && carp_forus(ifp->if_carp, eh->ether_dhost))
|
||
goto pre_stats;
|
||
#endif
|
||
/*
|
||
* Discard packet if upper layers shouldn't see it because
|
||
* it was unicast to a different Ethernet address. If the
|
||
... | ... | |
m_freem(m);
|
||
return;
|
||
}
|
||
#ifdef CARP
|
||
pre_stats:
|
||
#endif
|
||
/* Discard packet if interface is not up */
|
||
if (!(ifp->if_flags & IFF_UP)) {
|
||
m_freem(m);
|
sys/net/if_media.h | ||
---|---|---|
#define IFM_ATM_UNASSIGNED 0x00000400 /* unassigned cells */
|
||
/*
|
||
* CARP Common Address Redundancy Protocol
|
||
*/
|
||
#define IFM_CARP 0x000000c0
|
||
/*
|
||
* Shared media sub-types
|
||
*/
|
||
#define IFM_AUTO 0 /* Autoselect best media */
|
||
... | ... | |
{ IFM_TOKEN, "Token ring" }, \
|
||
{ IFM_FDDI, "FDDI" }, \
|
||
{ IFM_IEEE80211, "IEEE 802.11 Wireless Ethernet" }, \
|
||
{ IFM_CARP, "Common Address Redundancy Protocol" }, \
|
||
{ 0, NULL }, \
|
||
}
|
||
sys/net/if_types.h | ||
---|---|---|
#define IFT_STF 0xf3
|
||
#define IFT_PFLOG 0xf5 /* Packet filter logging */
|
||
#define IFT_PFSYNC 0xf6 /* Packet filter state syncing */
|
||
#define IFT_CARP 0xf8 /* Common Address Redundancy Protocol */
|
||
#endif /* !_NET_IF_TYPES_H_ */
|
sys/net/if_var.h | ||
---|---|---|
struct rt_addrinfo;
|
||
struct socket;
|
||
struct ether_header;
|
||
struct carp_if;
|
||
struct ucred;
|
||
struct lwkt_serialize;
|
||
... | ... | |
int if_dunit; /* unit or IF_DUNIT_NONE */
|
||
struct ifaddrhead if_addrhead; /* linked list of addresses per if */
|
||
int if_pcount; /* number of promiscuous listeners */
|
||
struct carp_if *if_carp; /* carp interface structure */
|
||
struct bpf_if *if_bpf; /* packet filter structure */
|
||
u_short if_index; /* numeric abbreviation for this if */
|
||
short if_timer; /* time 'til if_watchdog called */
|
sys/netinet/if_ether.c | ||
---|---|---|
*/
|
||
#include "opt_inet.h"
|
||
#include "opt_carp.h"
|
||
#include <sys/param.h>
|
||
#include <sys/kernel.h>
|
||
... | ... | |
#include <net/if_arc.h>
|
||
#include <net/iso88025.h>
|
||
#ifdef CARP
|
||
#include <netinet/ip_carp.h>
|
||
#endif
|
||
#define SIN(s) ((struct sockaddr_in *)s)
|
||
#define SDL(s) ((struct sockaddr_dl *)s)
|
||
... | ... | |
SYSCTL_INT(_net_link_ether_inet, OID_AUTO, proxyall, CTLFLAG_RW,
|
||
&arp_proxyall, 0, "");
|
||
void arprequest_acces(struct ifnet *ifp, struct in_addr *sip, struct in_addr *tip, u_char *enaddr);
|
||
static void arp_rtrequest (int, struct rtentry *, struct rt_addrinfo *);
|
||
static void arprequest (struct ifnet *,
|
||
struct in_addr *, struct in_addr *, u_char *);
|
||
... | ... | |
#ifdef SMP
|
||
struct netmsg_arp_update msg;
|
||
#endif
|
||
u_int8_t *enaddr = NULL;
|
||
int op;
|
||
int req_len;
|
||
... | ... | |
if (ifp->if_bridge && ia->ia_ifp &&
|
||
ifp->if_bridge == ia->ia_ifp->if_bridge)
|
||
goto match;
|
||
|
||
#ifdef CARP
|
||
/*
|
||
* If the interface does not match, but the recieving interface
|
||
* is part of carp, we call carp_iamatch to see if this is a
|
||
* request for the virtual host ip.
|
||
* XXX: This is really ugly!
|
||
*/
|
||
if (ifp->if_carp != NULL &&
|
||
carp_iamatch(ifp->if_carp, ia, &isaddr, &enaddr) &&
|
||
itaddr.s_addr == ia->ia_addr.sin_addr.s_addr)
|
||
goto match;
|
||
#endif
|
||
}
|
||
LIST_FOREACH(ia, INADDR_HASH(isaddr.s_addr), ia_hash) {
|
||
/* Skip all ia's which don't match */
|
||
... | ... | |
return;
|
||
match:
|
||
if (!enaddr)
|
||
enaddr = (u_int8_t *)IF_LLADDR(ifp);
|
||
myaddr = ia->ia_addr.sin_addr;
|
||
if (!bcmp(ar_sha(ah), IF_LLADDR(ifp), ifp->if_addrlen)) {
|
||
if (!bcmp(ar_sha(ah), enaddr, ifp->if_addrlen)) {
|
||
m_freem(m); /* it's from me, ignore it. */
|
||
return;
|
||
}
|
||
... | ... | |
if (itaddr.s_addr == myaddr.s_addr) {
|
||
/* I am the target */
|
||
memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln);
|
||
memcpy(ar_sha(ah), IF_LLADDR(ifp), ah->ar_hln);
|
||
memcpy(ar_sha(ah), enaddr, ah->ar_hln);
|
||
} else {
|
||
struct llinfo_arp *la;
|
||
... | ... | |
return;
|
||
}
|
||
memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln);
|
||
memcpy(ar_sha(ah), IF_LLADDR(ifp), ah->ar_hln);
|
||
memcpy(ar_sha(ah), enaddr, ah->ar_hln);
|
||
#ifdef DEBUG_PROXY
|
||
kprintf("arp: proxying for %s\n", inet_ntoa(itaddr));
|
||
#endif
|
||
... | ... | |
ifa->ifa_flags |= RTF_CLONING;
|
||
}
|
||
void
|
||
arp_ifinit2(struct ifnet *ifp, struct ifaddr *ifa, u_char *enaddr)
|
||
{
|
||
if (IA_SIN(ifa)->sin_addr.s_addr != INADDR_ANY)
|
||
arprequest(ifp, &IA_SIN(ifa)->sin_addr, &IA_SIN(ifa)->sin_addr,
|
||
enaddr);
|
||
ifa->ifa_rtrequest = arp_rtrequest;
|
||
ifa->ifa_flags |= RTF_CLONING;
|
||
}
|
||
static void
|
||
arp_init(void)
|
||
{
|
sys/netinet/if_ether.h | ||
---|---|---|
int arpresolve (struct ifnet *, struct rtentry *, struct mbuf *,
|
||
struct sockaddr *, u_char *);
|
||
void arp_ifinit (struct ifnet *, struct ifaddr *);
|
||
void arp_ifinit2 (struct ifnet *, struct ifaddr *, u_char *);
|
||
#endif
|
||
#endif
|
sys/netinet/in.h | ||
---|---|---|
#define IPPROTO_IPCOMP 108 /* payload compression (IPComp) */
|
||
/* 101-254: Partly Unassigned */
|
||
#define IPPROTO_PIM 103 /* Protocol Independent Mcast */
|
||
#define IPPROTO_CARP 112 /* CARP */
|
||
#define IPPROTO_PGM 113 /* PGM */
|
||
#define IPPROTO_SCTP 132 /* SCTP */
|
||
#define IPPROTO_PFSYNC 240 /* PFSYNC */
|
||
... | ... | |
#define INADDR_UNSPEC_GROUP (u_int32_t)0xe0000000 /* 224.0.0.0 */
|
||
#define INADDR_ALLHOSTS_GROUP (u_int32_t)0xe0000001 /* 224.0.0.1 */
|
||
#define INADDR_ALLRTRS_GROUP (u_int32_t)0xe0000002 /* 224.0.0.2 */
|
||
#define INADDR_CARP_GROUP (u_int32_t)0xe0000012 /* 224.0.0.18 */
|
||
#define INADDR_PFSYNC_GROUP (u_int32_t)0xe00000f0 /* 224.0.0.240 */
|
||
#define INADDR_MAX_LOCAL_GROUP (u_int32_t)0xe00000ff /* 224.0.0.255 */
|
||
sys/netinet/in_proto.c | ||
---|---|---|
#include "opt_ipsec.h"
|
||
#include "opt_inet6.h"
|
||
#include "opt_sctp.h"
|
||
#include "opt_carp.h"
|
||
#include <sys/param.h>
|
||
#include <sys/kernel.h>
|
||
... | ... | |
#include <net/netisr.h> /* for cpu0_soport */
|
||
#ifdef CARP
|
||
#include <netinet/ip_carp.h>
|
||
#endif
|
||
extern struct domain inetdomain;
|
||
static struct pr_usrreqs nousrreqs;
|
||
... | ... | |
rip_init, 0, 0, 0,
|
||
&rip_usrreqs
|
||
},
|
||
#ifdef CARP
|
||
{ SOCK_RAW, &inetdomain, IPPROTO_CARP, PR_ATOMIC|PR_ADDR,
|
||
carp_input, rip_output, 0, rip_ctloutput,
|
||
0,
|
||
0, 0, 0, 0,
|
||
&rip_usrreqs
|
||
},
|
||
#endif
|
||
};
|
||
struct domain inetdomain = {
|
||
... | ... | |
#ifdef PIM
|
||
SYSCTL_NODE(_net_inet, IPPROTO_PIM, pim, CTLFLAG_RW, 0, "PIM");
|
||
#endif
|
||
#ifdef CARP
|
||
SYSCTL_NODE(_net_inet, IPPROTO_CARP, carp, CTLFLAG_RW, 0, "CARP");
|
||
#endif
|
sys/netinet/ip_carp.c | ||
---|---|---|
/* $Id$ */
|
||
/* from $FreeBSD: src/sys/netinet/ip_carp.c,v 1.48 2007/02/02 09:39:09 glebius Exp $ */
|
||
/*
|
||
* Copyright (c) 2002 Michael Shalayeff. All rights reserved.
|
||
* Copyright (c) 2003 Ryan McBride. All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions
|
||
* are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
|
||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
#include "opt_carp.h"
|
||
/*#include "opt_bpf.h"*/
|
||
#include "opt_inet.h"
|
||
#include "opt_inet6.h"
|
||
#include <sys/types.h>
|
||
#include <sys/param.h>
|
||
#include <sys/systm.h>
|
||
#include <sys/conf.h>
|
||
#include <sys/kernel.h>
|
||
#include <machine/limits.h>
|
||
#include <sys/malloc.h>
|
||
#include <sys/mbuf.h>
|
||
#include <sys/module.h>
|
||
#include <sys/time.h>
|
||
#include <sys/proc.h>
|
||
#include <sys/sysctl.h>
|
||
#include <sys/syslog.h>
|
||
#include <sys/signalvar.h>
|
||
#include <sys/filio.h>
|
||
#include <sys/sockio.h>
|
||
#include <sys/in_cksum.h>
|
||
#include <sys/socket.h>
|
||
#include <sys/vnode.h>
|
||
#include <machine/stdarg.h>
|
||
#include <net/bpf.h>
|
||
#include <net/ethernet.h>
|
||
#include <net/iso88025.h>
|
||
#include <net/if.h>
|
||
#include <net/if_dl.h>
|
||
#include <net/if_types.h>
|
||
#include <net/route.h>
|
||
#ifdef INET
|
||
#include <netinet/in.h>
|
||
#include <netinet/in_var.h>
|
||
#include <netinet/in_systm.h>
|
||
#include <netinet/ip.h>
|
||
#include <netinet/ip_var.h>
|
||
#include <netinet/if_ether.h>
|
||
#include <netinet/if_fddi.h>
|
||
#endif
|
||
#ifdef INET6
|
||
#include <netinet/icmp6.h>
|
||
#include <netinet/ip6.h>
|
||
#include <netinet6/ip6_var.h>
|
||
#include <netinet6/scope6_var.h>
|
||
#include <netinet6/nd6.h>
|
||
#endif
|
||
#include <crypto/sha1.h>
|
||
#include <netinet/ip_carp.h>
|
||
#include <sys/lock.h>
|
||
#define CARP_IFNAME "carp"
|
||
static MALLOC_DEFINE(M_CARP, "CARP", "CARP interfaces");
|
||
static MALLOC_DEFINE(M_IFNET, "IFNET", "IFNET CARP?");
|
||
SYSCTL_DECL(_net_inet_carp);
|
||
struct carp_softc {
|
||
struct ifnet *sc_ifp; /* Interface clue */
|
||
struct ifnet *sc_carpdev; /* Pointer to parent interface */
|
||
struct in_ifaddr *sc_ia; /* primary iface address */
|
||
struct ip_moptions sc_imo;
|
||
#ifdef INET6
|
||
struct in6_ifaddr *sc_ia6; /* primary iface address v6 */
|
||
struct ip6_moptions sc_im6o;
|
||
#endif /* INET6 */
|
||
TAILQ_ENTRY(carp_softc) sc_list;
|
||
enum { INIT = 0, BACKUP, MASTER } sc_state;
|
||
int sc_flags_backup;
|
||
int sc_suppress;
|
||
int sc_sendad_errors;
|
||
#define CARP_SENDAD_MAX_ERRORS 3
|
||
int sc_sendad_success;
|
||
#define CARP_SENDAD_MIN_SUCCESS 3
|
||
int sc_vhid;
|
||
int sc_advskew;
|
||
int sc_naddrs;
|
||
int sc_naddrs6;
|
||
int sc_advbase; /* seconds */
|
||
int sc_init_counter;
|
||
u_int64_t sc_counter;
|
||
/* authentication */
|
||
#define CARP_HMAC_PAD 64
|
||
unsigned char sc_key[CARP_KEY_LEN];
|
||
unsigned char sc_pad[CARP_HMAC_PAD];
|
||
SHA1_CTX sc_sha1;
|
||
struct callout sc_ad_tmo; /* advertisement timeout */
|
||
struct callout sc_md_tmo; /* master down timeout */
|
||
struct callout sc_md6_tmo; /* master down timeout */
|
||
|
||
LIST_ENTRY(carp_softc) sc_next; /* Interface clue */
|
||
};
|
||
#define SC2IFP(sc) ((sc)->sc_ifp)
|
||
int carp_suppress_preempt = 0;
|
||
int carp_opts[CARPCTL_MAXID] = { 0, 1, 0, 1, 0, 0 }; /* XXX for now */
|
||
SYSCTL_INT(_net_inet_carp, CARPCTL_ALLOW, allow, CTLFLAG_RW,
|
||
&carp_opts[CARPCTL_ALLOW], 0, "Accept incoming CARP packets");
|
||
SYSCTL_INT(_net_inet_carp, CARPCTL_PREEMPT, preempt, CTLFLAG_RW,
|
||
&carp_opts[CARPCTL_PREEMPT], 0, "high-priority backup preemption mode");
|
||
SYSCTL_INT(_net_inet_carp, CARPCTL_LOG, log, CTLFLAG_RW,
|
||
&carp_opts[CARPCTL_LOG], 0, "log bad carp packets");
|
||
SYSCTL_INT(_net_inet_carp, CARPCTL_ARPBALANCE, arpbalance, CTLFLAG_RW,
|
||
&carp_opts[CARPCTL_ARPBALANCE], 0, "balance arp responses");
|
||
SYSCTL_INT(_net_inet_carp, OID_AUTO, suppress_preempt, CTLFLAG_RD,
|
||
&carp_suppress_preempt, 0, "Preemption is suppressed");
|
||
struct carpstats carpstats;
|
||
SYSCTL_STRUCT(_net_inet_carp, CARPCTL_STATS, stats, CTLFLAG_RW,
|
||
&carpstats, carpstats,
|
||
"CARP statistics (struct carpstats, netinet/ip_carp.h)");
|
||
struct carp_if {
|
||
TAILQ_HEAD(, carp_softc) vhif_vrs;
|
||
int vhif_nvrs;
|
||
struct ifnet *vhif_ifp;
|
||
struct lock vhif_lock;
|
||
};
|
||
/* Get carp_if from softc. Valid after carp_set_addr{,6}. */
|
||
#define SC2CIF(sc) ((struct carp_if *)(sc)->sc_carpdev->if_carp)
|
||
#define CARP_LOCK_INIT(cif) lockinit(&(cif)->vhif_lock, "carp_if", 0, LK_NOWAIT);
|
||
#define CARP_LOCK_DESTROY(cif) ;
|
||
#define CARP_LOCK_ASSERT(cif) ;
|
||
#define CARP_LOCK(cif) lockmgr(&(cif)->vhif_lock, LK_EXCLUSIVE);
|
||
#define CARP_UNLOCK(cif) lockmgr(&(cif)->vhif_lock, LK_RELEASE);
|
||
#define CARP_SCLOCK(sc) lockmgr(&SC2CIF(sc)->vhif_lock, LK_EXCLUSIVE);
|
||
#define CARP_SCUNLOCK(sc) lockmgr(&SC2CIF(sc)->vhif_lock, LK_RELEASE);
|
||
#define CARP_SCLOCK_ASSERT(sc) ;
|
||
#define CARP_LOG(...) do { \
|
||
if (carp_opts[CARPCTL_LOG] > 0) \
|
||
log(LOG_INFO, __VA_ARGS__); \
|
||
} while (0)
|
||
#define CARP_DEBUG(...) do { \
|
||
if (carp_opts[CARPCTL_LOG] > 1) \
|
||
log(LOG_DEBUG, __VA_ARGS__); \
|
||
} while (0)
|
||
static void carp_hmac_prepare(struct carp_softc *);
|
||
static void carp_hmac_generate(struct carp_softc *, u_int32_t *,
|
||
unsigned char *);
|
||
static int carp_hmac_verify(struct carp_softc *, u_int32_t *,
|
||
unsigned char *);
|
||
static void carp_setroute(struct carp_softc *, int);
|
||
static void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t);
|
||
static int carp_clone_create(struct if_clone *, int);
|
||
static void carp_clone_destroy(struct ifnet *);
|
||
static void carpdetach(struct carp_softc *, int);
|
||
static int carp_prepare_ad(struct mbuf *, struct carp_softc *,
|
||
struct carp_header *);
|
||
static void carp_send_ad_all(void);
|
||
static void carp_send_ad(void *);
|
||
static void carp_send_ad_locked(struct carp_softc *);
|
||
static void carp_send_arp(struct carp_softc *);
|
||
static void carp_master_down(void *);
|
||
static void carp_master_down_locked(struct carp_softc *);
|
||
static int carp_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
|
||
static int carp_looutput(struct ifnet *, struct mbuf *, struct sockaddr *,
|
||
struct rtentry *);
|
||
static void carp_start(struct ifnet *);
|
||
static void carp_setrun(struct carp_softc *, sa_family_t);
|
||
static void carp_set_state(struct carp_softc *, int);
|
||
static int carp_addrcount(struct carp_if *, struct in_ifaddr *, int);
|
||
enum { CARP_COUNT_MASTER, CARP_COUNT_RUNNING };
|
||
static void carp_multicast_cleanup(struct carp_softc *);
|
||
static int carp_set_addr(struct carp_softc *, struct sockaddr_in *);
|
||
static int carp_del_addr(struct carp_softc *, struct sockaddr_in *);
|
||
static void carp_carpdev_state_locked(struct carp_if *);
|
||
static void carp_sc_state_locked(struct carp_softc *);
|
||
#ifdef INET6
|
||
static void carp_send_na(struct carp_softc *);
|
||
static int carp_set_addr6(struct carp_softc *, struct sockaddr_in6 *);
|
||
static int carp_del_addr6(struct carp_softc *, struct sockaddr_in6 *);
|
||
static void carp_multicast6_cleanup(struct carp_softc *);
|
||
#endif
|
||
static LIST_HEAD(, carp_softc) carpif_list;
|
||
struct if_clone carp_cloner = IF_CLONE_INITIALIZER(CARP_IFNAME, carp_clone_create, carp_clone_destroy, 0, IF_MAXUNIT);
|
||
static eventhandler_tag if_detach_event_tag;
|
||
static __inline u_int16_t
|
||
carp_cksum(struct mbuf *m, int len)
|
||
{
|
||
return (in_cksum(m, len));
|
||
}
|
||
static void
|
||
carp_hmac_prepare(struct carp_softc *sc)
|
||
{
|
||
u_int8_t version = CARP_VERSION, type = CARP_ADVERTISEMENT;
|
||
u_int8_t vhid = sc->sc_vhid & 0xff;
|
||
struct ifaddr *ifa;
|
||
int i;
|
||
#ifdef INET6
|
||
struct in6_addr in6;
|
||
#endif
|
||
if (sc->sc_carpdev)
|
||
CARP_SCLOCK(sc);
|
||
/* XXX: possible race here */
|
||
/* compute ipad from key */
|
||
bzero(sc->sc_pad, sizeof(sc->sc_pad));
|
||
bcopy(sc->sc_key, sc->sc_pad, sizeof(sc->sc_key));
|
||
for (i = 0; i < sizeof(sc->sc_pad); i++)
|
||
sc->sc_pad[i] ^= 0x36;
|
||
/* precompute first part of inner hash */
|
||
SHA1Init(&sc->sc_sha1);
|
||
SHA1Update(&sc->sc_sha1, sc->sc_pad, sizeof(sc->sc_pad));
|
||
SHA1Update(&sc->sc_sha1, (void *)&version, sizeof(version));
|
||
SHA1Update(&sc->sc_sha1, (void *)&type, sizeof(type));
|
||
SHA1Update(&sc->sc_sha1, (void *)&vhid, sizeof(vhid));
|
||
#ifdef INET
|
||
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
||
if (ifa->ifa_addr->sa_family == AF_INET)
|
||
SHA1Update(&sc->sc_sha1,
|
||
(void *)&ifatoia(ifa)->ia_addr.sin_addr.s_addr,
|
||
sizeof(struct in_addr));
|
||
}
|
||
#endif /* INET */
|
||
#ifdef INET6
|
||
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
||
if (ifa->ifa_addr->sa_family == AF_INET6) {
|
||
in6 = ifatoia6(ifa)->ia_addr.sin6_addr;
|
||
in6_clearscope(&in6);
|
||
SHA1Update(&sc->sc_sha1, (void *)&in6, sizeof(in6));
|
||
}
|
||
}
|
||
#endif /* INET6 */
|
||
/* convert ipad to opad */
|
||
for (i = 0; i < sizeof(sc->sc_pad); i++)
|
||
sc->sc_pad[i] ^= 0x36 ^ 0x5c;
|
||
if (sc->sc_carpdev)
|
||
CARP_SCUNLOCK(sc);
|
||
}
|
||
static void
|
||
carp_hmac_generate(struct carp_softc *sc, u_int32_t counter[2],
|
||
unsigned char md[20])
|
||
{
|
||
SHA1_CTX sha1ctx;
|
||
/* fetch first half of inner hash */
|
||
bcopy(&sc->sc_sha1, &sha1ctx, sizeof(sha1ctx));
|
||
SHA1Update(&sha1ctx, (void *)counter, sizeof(sc->sc_counter));
|
||
SHA1Final(md, &sha1ctx);
|
||
/* outer hash */
|
||
SHA1Init(&sha1ctx);
|
||
SHA1Update(&sha1ctx, sc->sc_pad, sizeof(sc->sc_pad));
|
||
SHA1Update(&sha1ctx, md, 20);
|
||
SHA1Final(md, &sha1ctx);
|
||
}
|
||
static int
|
||
carp_hmac_verify(struct carp_softc *sc, u_int32_t counter[2],
|
||
unsigned char md[20])
|
||
{
|
||
unsigned char md2[20];
|
||
CARP_SCLOCK_ASSERT(sc);
|
||
carp_hmac_generate(sc, counter, md2);
|
||
return (bcmp(md, md2, sizeof(md2)));
|
||
}
|
||
static void
|
||
carp_setroute(struct carp_softc *sc, int cmd)
|
||
{
|
||
struct ifaddr *ifa;
|
||
if (sc->sc_carpdev)
|
||
CARP_SCLOCK_ASSERT(sc);
|
||
crit_enter();
|
||
TAILQ_FOREACH(ifa, &SC2IFP(sc)->if_addrlist, ifa_list) {
|
||
if (ifa->ifa_addr->sa_family == AF_INET &&
|
||
sc->sc_carpdev != NULL) {
|
||
int count = carp_addrcount(
|
||
(struct carp_if *)sc->sc_carpdev->if_carp,
|
||
ifatoia(ifa), CARP_COUNT_MASTER);
|
||
if ((cmd == RTM_ADD && count == 1) ||
|
||
(cmd == RTM_DELETE && count == 0))
|
||
rtinit(ifa, cmd, RTF_UP | RTF_HOST);
|
||
}
|
||
#ifdef INET6
|
||
if (ifa->ifa_addr->sa_family == AF_INET6) {
|
||
if (cmd == RTM_ADD)
|
||
in6_ifaddloop(ifa);
|
||
else
|
||
in6_ifremloop(ifa);
|
||
}
|
||
#endif /* INET6 */
|
||
}
|
||
crit_exit();
|
||
}
|
||
static int
|
||
carp_clone_create(struct if_clone *ifc, int unit)
|
||
{
|
||
struct carp_softc *sc;
|
||
struct ifnet *ifp;
|
||
|
||
MALLOC(sc, struct carp_softc *, sizeof(*sc), M_CARP, M_WAITOK|M_ZERO);
|
||
ifp = SC2IFP(sc) = kmalloc(sizeof(struct ifnet), M_IFNET, M_WAITOK|M_ZERO);
|
||
if (ifp == NULL) {
|
||
FREE(sc, M_CARP);
|
||
return (ENOSPC);
|
||
}
|
||
|
||
sc->sc_flags_backup = 0;
|
||
sc->sc_suppress = 0;
|
||
sc->sc_advbase = CARP_DFLTINTV;
|
||
sc->sc_vhid = -1; /* required setting */
|
||
sc->sc_advskew = 0;
|
||
sc->sc_init_counter = 1;
|
||
sc->sc_naddrs = sc->sc_naddrs6 = 0; /* M_ZERO? */
|
||
#ifdef INET6
|
||
sc->sc_im6o.im6o_multicast_hlim = CARP_DFLTTL;
|
||
#endif
|
||
/* sc->sc_imo.imo_membership = kmalloc((sizeof(struct in_multi) * IP_MAX_MEMBERSHIPS), M_CARP,M_WAITOK);*/
|
||
/*
|
||
sc->sc_imo.imo_max_memberships = IP_MAX_MEMBERSHIPS;
|
||
sc->sc_imo.imo_multicast_vif = -1;
|
||
*/
|
||
callout_init(&sc->sc_ad_tmo);
|
||
callout_init(&sc->sc_md_tmo);
|
||
callout_init(&sc->sc_md6_tmo);
|
||
ifp->if_softc = sc;
|
||
if_initname(ifp, CARP_IFNAME, unit);
|
||
ifp->if_mtu = ETHERMTU;
|
||
ifp->if_flags = IFF_LOOPBACK;
|
||
ifp->if_ioctl = carp_ioctl;
|
||
ifp->if_output = carp_looutput;
|
||
ifp->if_start = carp_start;
|
||
ifp->if_type = IFT_CARP;
|
||
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
||
ifp->if_hdrlen = 0;
|
||
if_attach(ifp, NULL);
|
||
bpfattach(ifp, DLT_NULL, sizeof(u_int));
|
||
crit_enter();
|
||
LIST_INSERT_HEAD(&carpif_list, sc, sc_next);
|
||
crit_exit();
|
||
return (0);
|
||
}
|
||
static void
|
||
carp_clone_destroy(struct ifnet *ifp)
|
||
{
|
||
struct carp_softc *sc = ifp->if_softc;
|
||
if (sc->sc_carpdev)
|
||
CARP_SCLOCK(sc);
|
||
carpdetach(sc, 1); /* Returns unlocked. */
|
||
crit_enter();
|
||
LIST_REMOVE(sc, sc_next);
|
||
crit_exit();
|
||
bpfdetach(ifp);
|
||
if_detach(ifp);
|
||
/* if_free_type(ifp, IFT_ETHER);*/
|
||
/* kfree(sc->sc_imo.imo_membership, M_CARP); */
|
||
kfree(sc, M_CARP);
|
||
}
|
||
/*
|
||
* This function can be called on CARP interface destroy path,
|
||
* and in case of the removal of the underlying interface as
|
||
* well. We differentiate these two cases. In the latter case
|
||
* we do not cleanup our multicast memberships, since they
|
||
* are already freed. Also, in the latter case we do not
|
||
* release the lock on return, because the function will be
|
||
* called once more, for another CARP instance on the same
|
||
* interface.
|
||
*/
|