diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2008-06-14 21:46:23 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2008-06-14 21:46:23 +0000 |
commit | 9dabaf5247648b588a451d1bd8f993af74668845 (patch) | |
tree | e9469bb3e950733a47fa5e03cb294ec255b36d12 | |
parent | 4c898c225e004ea449b31aa3f7a94e4da6f7a4dc (diff) |
add carppeer; an option to specify a different multicast address or
even the unicast address of the remote carp peer. this especially
helps when the multicast carp advertisements are causing problems in
the network (some crappy switches don't do well with multicast), there
are conflicts with VRRP, or the policy of the network does not allow
multicast (most Internet eXchange points didn't allow carped OpenBGP
routers because of the multicast advertisements).
discussed with many
ok mpf@
-rw-r--r-- | sbin/ifconfig/ifconfig.8 | 25 | ||||
-rw-r--r-- | sbin/ifconfig/ifconfig.c | 73 | ||||
-rw-r--r-- | sys/net/if_bridge.c | 6 | ||||
-rw-r--r-- | sys/netinet/ip_carp.c | 82 | ||||
-rw-r--r-- | sys/netinet/ip_carp.h | 5 |
5 files changed, 153 insertions, 38 deletions
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 23c84a820b8..7dc23e5bce0 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ifconfig.8,v 1.160 2008/05/07 11:57:19 claudio Exp $ +.\" $OpenBSD: ifconfig.8,v 1.161 2008/06/14 21:46:22 reyk Exp $ .\" $NetBSD: ifconfig.8,v 1.11 1996/01/04 21:27:29 pk Exp $ .\" $FreeBSD: ifconfig.8,v 1.16 1998/02/01 07:03:29 steve Exp $ .\" @@ -31,7 +31,7 @@ .\" .\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94 .\" -.Dd $Mdocdate: May 7 2008 $ +.Dd $Mdocdate: June 14 2008 $ .Dt IFCONFIG 8 .Os .Sh NAME @@ -454,6 +454,7 @@ and .Op Cm pass Ar passphrase .Op Cm state Ar state .Op Cm vhid Ar host-id +.Op Oo Fl Oc Cm carppeer Ar peer_address .Ek .Pp The options are as follows: @@ -517,6 +518,26 @@ If the driver is a pseudo-device, set the virtual host ID to .Ar n . Acceptable values are 1 to 255. +.It Cm carppeer Ar peer_address +If the driver is a +.Xr carp 4 +pseudo-device, send the carp advertisements to a specified +point-to-point peer or multicast group instead of sending the messages +to the default carp multicast group. +The +.Ar peer_address +is the IP address of the other host taking part in the carp cluster. +With this option, +.Xr carp 4 +traffic can be protected using +.Xr ipsec 4 +and it may be desired in networks that do not allow or have problems +with IPv4 multicast traffic. +.It Fl carppeer +If the driver is a +.Xr carp 4 +pseudo-device, send the advertisements to the default carp multicast +group. .El .Pp Taken together, the diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c index a3b8995236b..35ff1961498 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ifconfig.c,v 1.198 2008/06/13 06:58:20 reyk Exp $ */ +/* $OpenBSD: ifconfig.c,v 1.199 2008/06/14 21:46:22 reyk Exp $ */ /* $NetBSD: ifconfig.c,v 1.40 1997/10/01 02:19:43 enami Exp $ */ /* @@ -202,6 +202,8 @@ void getifgroups(void); void carp_status(void); void setcarp_advbase(const char *,int); void setcarp_advskew(const char *, int); +void setcarppeer(const char *, int); +void unsetcarppeer(const char *, int); void setcarp_passwd(const char *, int); void setcarp_vhid(const char *, int); void setcarp_state(const char *, int); @@ -333,6 +335,8 @@ const struct cmd { { "-vlandev", 1, 0, unsetvlandev }, { "advbase", NEXTARG, 0, setcarp_advbase }, { "advskew", NEXTARG, 0, setcarp_advskew }, + { "carppeer", NEXTARG, 0, setcarppeer }, + { "-carppeer", 1, 0, unsetcarppeer }, { "pass", NEXTARG, 0, setcarp_passwd }, { "vhid", NEXTARG, 0, setcarp_vhid }, { "state", NEXTARG, 0, setcarp_state }, @@ -3196,6 +3200,7 @@ carp_status(void) { const char *state, *balmode; struct carpreq carpr; + char peer[32]; int i; memset((char *)&carpr, 0, sizeof(struct carpreq)); @@ -3212,6 +3217,12 @@ carp_status(void) else balmode = carp_bal_modes[carpr.carpr_balancing]; + if (carpr.carpr_peer.s_addr != htonl(INADDR_CARP_GROUP)) + snprintf(peer, sizeof(peer), + " carppeer %s", inet_ntoa(carpr.carpr_peer)); + else + peer[0] = '\0'; + for (i = 0; carpr.carpr_vhids[i]; i++) { if (carpr.carpr_states[i] > CARP_MAXSTATE) state = "<UNKNOWN>"; @@ -3219,17 +3230,18 @@ carp_status(void) state = carp_states[carpr.carpr_states[i]]; if (carpr.carpr_vhids[1] == 0) { printf("\tcarp: %s carpdev %s vhid %u advbase %d " - "advskew %u\n", state, + "advskew %u%s\n", state, carpr.carpr_carpdev[0] != '\0' ? carpr.carpr_carpdev : "none", carpr.carpr_vhids[0], - carpr.carpr_advbase, carpr.carpr_advskews[0]); + carpr.carpr_advbase, carpr.carpr_advskews[0], + peer); } else { if (i == 0) { printf("\tcarp: carpdev %s advbase %d" - " balancing %s\n", + " balancing %s%s\n", carpr.carpr_carpdev[0] != '\0' ? carpr.carpr_carpdev : "none", - carpr.carpr_advbase, balmode); + carpr.carpr_advbase, balmode, peer); } printf("\t\tstate %s vhid %u advskew %u\n", state, carpr.carpr_vhids[i], carpr.carpr_advskews[i]); @@ -3331,6 +3343,57 @@ setcarp_advbase(const char *val, int d) /* ARGSUSED */ void +setcarppeer(const char *val, int d) +{ + struct carpreq carpr; + struct addrinfo hints, *peerres; + int ecode; + + memset((char *)&carpr, 0, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(1, "SIOCGVH"); + + bzero(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if ((ecode = getaddrinfo(val, NULL, &hints, &peerres)) != 0) + errx(1, "error in parsing address string: %s", + gai_strerror(ecode)); + + if (peerres->ai_addr->sa_family != AF_INET) + errx(1, "only IPv4 addresses supported for the carppeer"); + + carpr.carpr_peer.s_addr = ((struct sockaddr_in *) + peerres->ai_addr)->sin_addr.s_addr; + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(1, "SIOCSVH"); + + freeaddrinfo(peerres); +} + +void +unsetcarppeer(const char *val, int d) +{ + struct carpreq carpr; + + bzero((char *)&carpr, sizeof(struct carpreq)); + ifr.ifr_data = (caddr_t)&carpr; + + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) + err(1, "SIOCGVH"); + + bzero((char *)&carpr.carpr_peer, sizeof(carpr.carpr_peer)); + + if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1) + err(1, "SIOCSVH"); +} + +/* ARGSUSED */ +void setcarp_state(const char *val, int d) { struct carpreq carpr; diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 2b82b221dd4..21496b9e964 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.c,v 1.169 2008/05/21 21:12:07 mk Exp $ */ +/* $OpenBSD: if_bridge.c,v 1.170 2008/06/14 21:46:22 reyk Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -1470,7 +1470,7 @@ bridge_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m) if (bcmp(ac->ac_enaddr, eh->ether_dhost, ETHER_ADDR_LEN) == 0 #if NCARP > 0 || (ifl->ifp->if_carp && carp_ourether(ifl->ifp->if_carp, - eh, IFT_ETHER, 0) != NULL) + eh, 0) != NULL) #endif ) { if (srcifl->bif_flags & IFBIF_LEARNING) @@ -1493,7 +1493,7 @@ bridge_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m) if (bcmp(ac->ac_enaddr, eh->ether_shost, ETHER_ADDR_LEN) == 0 #if NCARP > 0 || (ifl->ifp->if_carp && carp_ourether(ifl->ifp->if_carp, - eh, IFT_ETHER, 1) != NULL) + eh, 1) != NULL) #endif ) { m_freem(m); diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index ca82bcf5c9f..67868b7d243 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_carp.c,v 1.166 2008/06/13 23:29:31 mpf Exp $ */ +/* $OpenBSD: ip_carp.c,v 1.167 2008/06/14 21:46:22 reyk Exp $ */ /* * Copyright (c) 2002 Michael Shalayeff. All rights reserved. @@ -167,6 +167,8 @@ struct carp_softc { int sc_lscount; /* # load sharing interfaces (max 32) */ int sc_delayed_arp; /* delayed ARP request countdown */ + struct in_addr sc_peer; + LIST_HEAD(__carp_mchead, carp_mc_entry) carp_mc_listhead; struct carp_vhost_entry *cur_vhe; /* current active vhe */ }; @@ -201,7 +203,8 @@ void carp_hmac_generate(struct carp_vhost_entry *, u_int32_t *, int carp_hmac_verify(struct carp_vhost_entry *, u_int32_t *, unsigned char *); void carp_setroute(struct carp_softc *, int); -void carp_proto_input_c(struct mbuf *, struct carp_header *, sa_family_t); +void carp_proto_input_c(struct mbuf *, struct carp_header *, int, + sa_family_t); void carpattach(int); void carpdetach(struct carp_softc *); int carp_prepare_ad(struct mbuf *, struct carp_vhost_entry *, @@ -527,9 +530,10 @@ void carp_proto_input(struct mbuf *m, ...) { struct ip *ip = mtod(m, struct ip *); + struct ifnet *ifp = m->m_pkthdr.rcvif; struct carp_softc *sc = NULL; struct carp_header *ch; - int iplen, len, hlen; + int iplen, len, hlen, ismulti; va_list ap; va_start(ap, m); @@ -543,8 +547,11 @@ carp_proto_input(struct mbuf *m, ...) return; } + ismulti = IN_MULTICAST(ip->ip_dst.s_addr); + /* check if received on a valid carp interface */ - if (m->m_pkthdr.rcvif->if_type != IFT_CARP) { + if (!((ifp->if_type == IFT_CARP && ismulti) || + (ifp->if_type != IFT_CARP && !ismulti && ifp->if_carp != NULL))) { carpstats.carps_badif++; CARP_LOG(LOG_INFO, sc, ("packet received on non-carp interface: %s", m->m_pkthdr.rcvif->if_xname)); @@ -593,7 +600,7 @@ carp_proto_input(struct mbuf *m, ...) } m->m_data -= iplen; - carp_proto_input_c(m, ch, AF_INET); + carp_proto_input_c(m, ch, ismulti, AF_INET); } #ifdef INET6 @@ -652,20 +659,30 @@ carp6_proto_input(struct mbuf **mp, int *offp, int proto) } m->m_data -= *offp; - carp_proto_input_c(m, ch, AF_INET6); + carp_proto_input_c(m, ch, 1, AF_INET6); return (IPPROTO_DONE); } #endif /* INET6 */ void -carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) +carp_proto_input_c(struct mbuf *m, struct carp_header *ch, int ismulti, + sa_family_t af) { + struct ifnet *ifp = m->m_pkthdr.rcvif; struct carp_softc *sc; struct carp_vhost_entry *vhe; struct timeval sc_tv, ch_tv; + struct carp_if *cif; + + if (ifp->if_type == IFT_CARP) + cif = (struct carp_if *)ifp->if_carpdev->if_carp; + else + cif = (struct carp_if *)ifp->if_carp; - TAILQ_FOREACH(sc, &((struct carp_if *) - m->m_pkthdr.rcvif->if_carpdev->if_carp)->vhif_vrs, sc_list) { + TAILQ_FOREACH(sc, &cif->vhif_vrs, sc_list) { + if (af == AF_INET && + ismulti != IN_MULTICAST(sc->sc_peer.s_addr)) + continue; LIST_FOREACH(vhe, &sc->carp_vhosts, vhost_entries) { if (vhe->vhid == ch->carp_vhid) goto found; @@ -1119,7 +1136,6 @@ carp_send_ad(void *v) m->m_pkthdr.rcvif = NULL; m->m_len = len; MH_ALIGN(m, m->m_len); - m->m_flags |= M_MCAST; ip = mtod(m, struct ip *); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(*ip) >> 2; @@ -1139,7 +1155,9 @@ carp_send_ad(void *v) else ip->ip_src.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr; - ip->ip_dst.s_addr = INADDR_CARP_GROUP; + ip->ip_dst.s_addr = sc->sc_peer.s_addr; + if (IN_MULTICAST(ip->ip_dst.s_addr)) + m->m_flags |= M_MCAST; ch_ptr = (void *)ip + sizeof(*ip); bcopy(&ch, ch_ptr, sizeof(ch)); @@ -1466,7 +1484,7 @@ carp_iamatch6(struct ifnet *ifp, u_char *src, struct sockaddr_dl **sdl) #endif /* INET6 */ struct ifnet * -carp_ourether(void *v, struct ether_header *eh, u_char iftype, int src) +carp_ourether(void *v, struct ether_header *eh, int src) { struct carp_if *cif = (struct carp_if *)v; struct carp_softc *vh; @@ -1537,7 +1555,7 @@ carp_input(struct mbuf *m, u_int8_t *shost, u_int8_t *dhost, u_int16_t etype) bcopy(dhost, &eh.ether_dhost, sizeof(eh.ether_dhost)); eh.ether_type = etype; - if ((ifp = carp_ourether(cif, &eh, m->m_pkthdr.rcvif->if_type, 0))) + if ((ifp = carp_ourether(cif, &eh, 0))) ; else if (m->m_flags & (M_BCAST|M_MCAST)) { struct carp_softc *vh; @@ -1925,18 +1943,22 @@ carp_addr_updated(void *v) sc->sc_naddrs6 = new_naddrs6; /* Re-establish multicast membership removed by in_control */ - mc_addr.s_addr = INADDR_CARP_GROUP; - IN_LOOKUP_MULTI(mc_addr, &sc->sc_if, inm); - if (inm == NULL) { - struct in_multi **imm = sc->sc_imo.imo_membership; - u_int16_t maxmem = sc->sc_imo.imo_max_memberships; - - bzero(&sc->sc_imo, sizeof(sc->sc_imo)); - sc->sc_imo.imo_membership = imm; - sc->sc_imo.imo_max_memberships = maxmem; - - if (sc->sc_carpdev != NULL && sc->sc_naddrs > 0) - carp_join_multicast(sc); + if (IN_MULTICAST(sc->sc_peer.s_addr)) { + mc_addr.s_addr = sc->sc_peer.s_addr; + IN_LOOKUP_MULTI(mc_addr, &sc->sc_if, inm); + if (inm == NULL) { + struct in_multi **imm = + sc->sc_imo.imo_membership; + u_int16_t maxmem = + sc->sc_imo.imo_max_memberships; + + bzero(&sc->sc_imo, sizeof(sc->sc_imo)); + sc->sc_imo.imo_membership = imm; + sc->sc_imo.imo_max_memberships = maxmem; + + if (sc->sc_carpdev != NULL && sc->sc_naddrs > 0) + carp_join_multicast(sc); + } } if (sc->sc_naddrs == 0 && sc->sc_naddrs6 == 0) { @@ -2024,7 +2046,10 @@ carp_join_multicast(struct carp_softc *sc) struct in_multi *imm; struct in_addr addr; - addr.s_addr = INADDR_CARP_GROUP; + if (!IN_MULTICAST(sc->sc_peer.s_addr)) + return (0); + + addr.s_addr = sc->sc_peer.s_addr; if ((imm = in_addmulti(&addr, &sc->sc_if)) == NULL) return (ENOBUFS); @@ -2222,6 +2247,10 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) if (carpr.carpr_carpdev[0] != '\0' && (cdev = ifunit(carpr.carpr_carpdev)) == NULL) return (EINVAL); + if (carpr.carpr_peer.s_addr == 0) + sc->sc_peer.s_addr = INADDR_CARP_GROUP; + else + sc->sc_peer.s_addr = carpr.carpr_peer.s_addr; if ((error = carp_set_ifp(sc, cdev))) return (error); if (vhe->state != INIT && carpr.carpr_state != vhe->state) { @@ -2294,6 +2323,7 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) if (suser(p, p->p_acflag) == 0) bcopy(sc->sc_key, carpr.carpr_key, sizeof(carpr.carpr_key)); + carpr.carpr_peer.s_addr = sc->sc_peer.s_addr; error = copyout(&carpr, ifr->ifr_data, sizeof(carpr)); break; diff --git a/sys/netinet/ip_carp.h b/sys/netinet/ip_carp.h index 857d8563c2b..145bc9d4023 100644 --- a/sys/netinet/ip_carp.h +++ b/sys/netinet/ip_carp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_carp.h,v 1.26 2008/02/05 22:57:31 mpf Exp $ */ +/* $OpenBSD: ip_carp.h,v 1.27 2008/06/14 21:46:22 reyk Exp $ */ /* * Copyright (c) 2002 Michael Shalayeff. All rights reserved. @@ -142,6 +142,7 @@ struct carpreq { u_int8_t carpr_balancing; int carpr_advbase; unsigned char carpr_key[CARP_KEY_LEN]; + struct in_addr carpr_peer; }; /* @@ -170,7 +171,7 @@ int carp6_proto_input(struct mbuf **, int *, int); int carp_iamatch(struct in_ifaddr *, u_char *, u_int8_t **, u_int8_t **); int carp_iamatch6(struct ifnet *, u_char *, struct sockaddr_dl **); -struct ifnet *carp_ourether(void *, struct ether_header *, u_char, int); +struct ifnet *carp_ourether(void *, struct ether_header *, int); int carp_input(struct mbuf *, u_int8_t *, u_int8_t *, u_int16_t); int carp_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); |