Search
j0ke.net Open Build Service
>
Projects
>
home:jg
:
ha
>
keepalived
> keepalived-unicast_single_peer-1.2.7.diff
Sign Up
|
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File keepalived-unicast_single_peer-1.2.7.diff of Package keepalived (Revision 3)
Currently displaying revision
3
,
show latest
diff -rupN keepalived-1.2.7/keepalived/include/vrrp.h keepalived-1.2.7-unicast/keepalived/include/vrrp.h --- keepalived-1.2.7/keepalived/include/vrrp.h 2012-08-29 00:05:12.000000000 +0200 +++ keepalived-1.2.7-unicast/keepalived/include/vrrp.h 2013-05-24 09:39:17.614831300 +0200 @@ -100,6 +100,8 @@ typedef struct _vrrp_rt { list track_ifp; /* Interface state we monitor */ list track_script; /* Script state we monitor */ uint32_t mcast_saddr; /* Src IP address to use in VRRP IP header */ + uint32_t unicast_bind; /* listen to this IP if mcast is not possible */ + uint32_t unicast_peer; /* send to this IP if mcast is not possible */ char *lvs_syncd_if; /* handle LVS sync daemon state using this * instance FSM & running on specific interface * => eth0 for example. @@ -216,8 +218,8 @@ typedef struct _vrrp_rt { /* prototypes */ extern vrrp_pkt *vrrp_get_header(sa_family_t, char *, int *, uint32_t *); -extern int open_vrrp_send_socket(sa_family_t, int, int); -extern int open_vrrp_socket(sa_family_t, int, int); +extern int open_vrrp_send_socket(sa_family_t, int, int, int); +extern int open_vrrp_socket(sa_family_t, int, int, int); extern int new_vrrp_socket(vrrp_rt *); extern void close_vrrp_socket(vrrp_rt *); extern void vrrp_send_link_update(vrrp_rt *); diff -rupN keepalived-1.2.7/keepalived/include/vrrp.h.orig keepalived-1.2.7-unicast/keepalived/include/vrrp.h.orig --- keepalived-1.2.7/keepalived/include/vrrp.h.orig 1970-01-01 01:00:00.000000000 +0100 +++ keepalived-1.2.7-unicast/keepalived/include/vrrp.h.orig 2012-08-29 00:05:12.000000000 +0200 @@ -0,0 +1,238 @@ +/* + * Soft: Vrrpd is an implementation of VRRPv2 as specified in rfc2338. + * VRRP is a protocol which elect a master server on a LAN. If the + * master fails, a backup server takes over. + * The original implementation has been made by jerome etienne. + * + * Part: vrrp.c program include file. + * + * Author: Alexandre Cassen, <acassen@linux-vs.org> + * + * 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. + * + * 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. + * + * Copyright (C) 2001-2012 Alexandre Cassen, <acassen@gmail.com> + */ + +#ifndef _VRRP_H +#define _VRRP_H + +/* system include */ +#include <unistd.h> + +/* local include */ +#include "vrrp_ipaddress.h" +#include "vrrp_iproute.h" +#include "vrrp_ipsecah.h" +#include "vrrp_if.h" +#include "vrrp_track.h" +#include "timer.h" +#include "utils.h" +#include "vector.h" +#include "list.h" + +typedef struct { /* rfc2338.5.1 */ + uint8_t vers_type; /* 0-3=type, 4-7=version */ + uint8_t vrid; /* virtual router id */ + uint8_t priority; /* router priority */ + uint8_t naddr; /* address counter */ + uint8_t auth_type; /* authentification type */ + uint8_t adver_int; /* advertissement interval(in sec) */ + uint16_t chksum; /* checksum (ip-like one) */ +/* here <naddr> ip addresses */ +/* here authentification infos */ +} vrrp_pkt; + +/* protocol constants */ +#define INADDR_VRRP_GROUP 0xe0000012 /* multicast addr - rfc2338.5.2.2 */ +#define VRRP_IP_TTL 255 /* in and out pkt ttl -- rfc2338.5.2.3 */ +#define IPPROTO_VRRP 112 /* IP protocol number -- rfc2338.5.2.4 */ +#define VRRP_VERSION 2 /* current version -- rfc2338.5.3.1 */ +#define VRRP_PKT_ADVERT 1 /* packet type -- rfc2338.5.3.2 */ +#define VRRP_PRIO_OWNER 255 /* priority of the ip owner -- rfc2338.5.3.4 */ +#define VRRP_PRIO_DFL 100 /* default priority -- rfc2338.5.3.4 */ +#define VRRP_PRIO_STOP 0 /* priority to stop -- rfc2338.5.3.4 */ +#define VRRP_AUTH_NONE 0 /* no authentification -- rfc2338.5.3.6 */ +#define VRRP_AUTH_PASS 1 /* password authentification -- rfc2338.5.3.6 */ +#define VRRP_AUTH_AH 2 /* AH(IPSec) authentification - rfc2338.5.3.6 */ +#define VRRP_ADVER_DFL 1 /* advert. interval (in sec) -- rfc2338.5.3.7 */ +#define VRRP_GARP_DELAY (5 * TIMER_HZ) /* Default delay to launch gratuitous arp */ + +/* + * parameters per vrrp sync group. A vrrp_sync_group is a set + * of VRRP instances that need to be state sync together. + */ +typedef struct _vrrp_sgroup { + char *gname; /* Group name */ + vector_t *iname; /* Set of VRRP instances in this group */ + list index_list; /* List of VRRP instances */ + int state; /* current stable state */ + int global_tracking; /* Use floating priority and scripts + * All VRRP must share same tracking conf + */ + + /* State transition notification */ + int notify_exec; + char *script_backup; + char *script_master; + char *script_fault; + char *script; + int smtp_alert; +} vrrp_sgroup; + +/* parameters per virtual router -- rfc2338.6.1.2 */ +typedef struct _vrrp_rt { + sa_family_t family; /* AF_INET|AF_INET6 */ + char *iname; /* Instance Name */ + vrrp_sgroup *sync; /* Sync group we belong to */ + interface *ifp; /* Interface we belong to */ + int dont_track_primary; /* If set ignores ifp faults */ + int vmac; /* If set try to set VRRP VMAC */ + char vmac_ifname[IFNAMSIZ]; /* Name of VRRP VMAC interface */ + unsigned int vmac_ifindex; /* ifindex of vmac interface */ + list track_ifp; /* Interface state we monitor */ + list track_script; /* Script state we monitor */ + uint32_t mcast_saddr; /* Src IP address to use in VRRP IP header */ + char *lvs_syncd_if; /* handle LVS sync daemon state using this + * instance FSM & running on specific interface + * => eth0 for example. + */ + int garp_delay; /* Delay to launch gratuitous ARP */ + int vrid; /* virtual id. from 1(!) to 255 */ + int base_priority; /* configured priority value */ + int effective_priority; /* effective priority value */ + int vipset; /* All the vips are set ? */ + list vip; /* list of virtual ip addresses */ + list evip; /* list of protocol excluded VIPs. + * Those VIPs will not be presents into the + * VRRP adverts + */ + list vroutes; /* list of virtual routes */ + int adver_int; /* delay between advertisements(in sec) */ + int nopreempt; /* true if higher prio does not preempt lower */ + long preempt_delay; /* Seconds*TIMER_HZ after startup until + * preemption based on higher prio over lower + * prio is allowed. 0 means no delay. + */ + timeval_t preempt_time; /* Time after which preemption can happen */ + int state; /* internal state (init/backup/master) */ + int init_state; /* the initial state of the instance */ + int wantstate; /* user explicitly wants a state (back/mast) */ + int fd_in; /* IN socket descriptor */ + int fd_out; /* OUT socket descriptor */ + + int debug; /* Debug level 0-4 */ + + /* State transition notification */ + int smtp_alert; + int notify_exec; + char *script_backup; + char *script_master; + char *script_fault; + char *script_stop; + char *script; + + /* rfc2336.6.2 */ + uint32_t ms_down_timer; + struct timeval sands; + + /* Sending buffer */ + char *send_buffer; /* Allocated send buffer */ + int send_buffer_size; + + /* Authentication data */ + int auth_type; /* authentification type. VRRP_AUTH_* */ + uint8_t auth_data[8]; /* authentification data */ + + /* + * To have my own ip_id creates collision with kernel ip->id + * but it should be ok because the packets are unlikely to be + * fragmented (they are non routable and small) + * This packet isnt routed, i can check the outgoing MTU + * to warn the user only if the outoing mtu is too small + */ + int ip_id; + + /* IPSEC AH counter def --rfc2402.3.3.2 */ + seq_counter *ipsecah_counter; +} vrrp_rt; + +/* VRRP state machine -- rfc2338.6.4 */ +#define VRRP_STATE_INIT 0 /* rfc2338.6.4.1 */ +#define VRRP_STATE_BACK 1 /* rfc2338.6.4.2 */ +#define VRRP_STATE_MAST 2 /* rfc2338.6.4.3 */ +#define VRRP_STATE_FAULT 3 /* internal */ +#define VRRP_STATE_GOTO_MASTER 4 /* internal */ +#define VRRP_STATE_LEAVE_MASTER 5 /* internal */ +#define VRRP_STATE_GOTO_FAULT 98 /* internal */ +#define VRRP_DISPATCHER 99 /* internal */ +#define VRRP_MCAST_RETRY 10 /* internal */ +#define VRRP_MAX_FSM_STATE 4 /* internal */ + +/* VRRP packet handling */ +#define VRRP_PACKET_OK 0 +#define VRRP_PACKET_KO 1 +#define VRRP_PACKET_DROP 2 +#define VRRP_PACKET_NULL 3 +#define VRRP_PACKET_OTHER 4 /* Muliple VRRP on LAN, Identify "other" VRRP */ + +/* VRRP Packet fixed lenght */ +#define VRRP_MAX_VIP 20 +#define VRRP_PACKET_TEMP_LEN 1024 +#define VRRP_AUTH_LEN 8 +#define VRRP_VIP_TYPE (1 << 0) +#define VRRP_EVIP_TYPE (1 << 1) + +/* VRRP macro */ +#define VRRP_IS_BAD_VID(id) ((id)<1 || (id)>255) /* rfc2338.6.1.vrid */ +#define VRRP_IS_BAD_PRIORITY(p) ((p)<1 || (p)>255) /* rfc2338.6.1.prio */ +#define VRRP_IS_BAD_ADVERT_INT(d) ((d)<1) +#define VRRP_IS_BAD_DEBUG_INT(d) ((d)<0 || (d)>4) +#define VRRP_IS_BAD_PREEMPT_DELAY(d) ((d)<0 || (d)>TIMER_MAX_SEC) +#define VRRP_SEND_BUFFER(V) ((V)->send_buffer) +#define VRRP_SEND_BUFFER_SIZE(V) ((V)->send_buffer_size) + +#define VRRP_TIMER_SKEW(svr) ((256-(svr)->base_priority)*TIMER_HZ/256) +#define VRRP_VIP_ISSET(V) ((V)->vipset) + +#define VRRP_MIN(a, b) ((a) < (b)?(a):(b)) +#define VRRP_MAX(a, b) ((a) > (b)?(a):(b)) + +#define VRRP_PKT_SADDR(V) (((V)->mcast_saddr) ? (V)->mcast_saddr : IF_ADDR((V)->ifp)) + +#define VRRP_IF_ISUP(V) ((IF_ISUP((V)->ifp) || (V)->dont_track_primary) & \ + ((!LIST_ISEMPTY((V)->track_ifp)) ? TRACK_ISUP((V)->track_ifp) : 1)) + +#define VRRP_SCRIPT_ISUP(V) ((!LIST_ISEMPTY((V)->track_script)) ? SCRIPT_ISUP((V)->track_script) : 1) + +#define VRRP_ISUP(V) (VRRP_IF_ISUP(V) && VRRP_SCRIPT_ISUP(V)) + +/* prototypes */ +extern vrrp_pkt *vrrp_get_header(sa_family_t, char *, int *, uint32_t *); +extern int open_vrrp_send_socket(sa_family_t, int, int); +extern int open_vrrp_socket(sa_family_t, int, int); +extern int new_vrrp_socket(vrrp_rt *); +extern void close_vrrp_socket(vrrp_rt *); +extern void vrrp_send_link_update(vrrp_rt *); +extern int vrrp_send_adv(vrrp_rt *, int); +extern int vrrp_state_fault_rx(vrrp_rt *, char *, int); +extern int vrrp_state_master_rx(vrrp_rt *, char *, int); +extern int vrrp_state_master_tx(vrrp_rt *, const int); +extern void vrrp_state_backup(vrrp_rt *, char *, int); +extern void vrrp_state_goto_master(vrrp_rt *); +extern void vrrp_state_leave_master(vrrp_rt *); +extern int vrrp_ipsecah_len(void); +extern int vrrp_complete_init(void); +extern void shutdown_vrrp_instances(void); +extern void clear_diff_vrrp(void); +extern void clear_diff_script(void); +extern void vrrp_restore_interface(vrrp_rt *, int); + +#endif diff -rupN keepalived-1.2.7/keepalived/include/vrrp_if.h keepalived-1.2.7-unicast/keepalived/include/vrrp_if.h --- keepalived-1.2.7/keepalived/include/vrrp_if.h 2012-08-13 20:50:25.000000000 +0200 +++ keepalived-1.2.7-unicast/keepalived/include/vrrp_if.h 2013-05-24 09:39:17.618831600 +0200 @@ -117,7 +117,7 @@ extern void init_interface_linkbeat(void extern void free_interface_queue(void); extern void dump_if(void *); extern int if_join_vrrp_group(sa_family_t, int *, interface *, int); -extern int if_leave_vrrp_group(sa_family_t, int, interface *); +extern int if_leave_vrrp_group(sa_family_t, int, interface *, int); extern int if_setsockopt_bindtodevice(int *, interface *); extern int if_setsockopt_hdrincl(int *); extern int if_setsockopt_mcast_loop(sa_family_t, int *); diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp.c keepalived-1.2.7-unicast/keepalived/vrrp/vrrp.c --- keepalived-1.2.7/keepalived/vrrp/vrrp.c 2012-08-17 01:21:28.000000000 +0200 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp.c 2013-05-24 09:41:33.101580700 +0200 @@ -387,8 +387,8 @@ vrrp_build_ip(vrrp_rt * vrrp, char *buff /* fill protocol type --rfc2402.2 */ ip->protocol = (vrrp->auth_type == VRRP_AUTH_AH) ? IPPROTO_IPSEC_AH : IPPROTO_VRRP; - ip->saddr = VRRP_PKT_SADDR(vrrp); - ip->daddr = htonl(INADDR_VRRP_GROUP); + ip->saddr = vrrp->unicast_bind ? vrrp->unicast_bind : VRRP_PKT_SADDR(vrrp); + ip->daddr = vrrp->unicast_peer ? vrrp->unicast_peer : htonl(INADDR_VRRP_GROUP); /* checksum must be done last */ ip->check = in_csum((u_short *) ip, ip->ihl * 4, 0); @@ -584,7 +584,7 @@ vrrp_send_pkt(vrrp_rt * vrrp) if (vrrp->family == AF_INET) { memset(&dst4, 0, sizeof(dst4)); dst4.sin_family = AF_INET; - dst4.sin_addr.s_addr = htonl(INADDR_VRRP_GROUP); + dst4.sin_addr.s_addr = vrrp->unicast_peer ? vrrp->unicast_peer : htonl(INADDR_VRRP_GROUP); msg.msg_name = &dst4; msg.msg_namelen = sizeof(dst4); @@ -998,7 +998,7 @@ chk_min_cfg(vrrp_rt * vrrp) /* open a VRRP sending socket */ int -open_vrrp_send_socket(sa_family_t family, int proto, int idx) +open_vrrp_send_socket(sa_family_t family, int proto, int idx, int unicast) { interface *ifp; int fd = -1; @@ -1017,18 +1017,18 @@ open_vrrp_send_socket(sa_family_t family /* Set v4 related */ if_setsockopt_hdrincl(&fd); if_setsockopt_bindtodevice(&fd, ifp); - if_setsockopt_mcast_loop(family, &fd); - if_setsockopt_priority(&fd); - if (fd < 0) - return -1; +// if_setsockopt_mcast_loop(family, &fd); +// if_setsockopt_priority(&fd); +// if (fd < 0) +// return -1; } else if (family == AF_INET6) { /* Set v6 related */ if_setsockopt_mcast_hops(family, &fd); if_setsockopt_mcast_if(family, &fd, ifp); - if_setsockopt_mcast_loop(family, &fd); - if_setsockopt_priority(&fd); - if (fd < 0) - return -1; +// if_setsockopt_mcast_loop(family, &fd); +// if_setsockopt_priority(&fd); +// if (fd < 0) +// return -1; } else { log_message(LOG_INFO, "cant open raw socket. unknow family=%d" , family); @@ -1036,12 +1036,17 @@ open_vrrp_send_socket(sa_family_t family return -1; } + if (!unicast) + if_setsockopt_mcast_loop(family, &fd); + if (fd < 0) + return -1; + return fd; } /* open a VRRP socket and join the multicast group. */ int -open_vrrp_socket(sa_family_t family, int proto, int idx) +open_vrrp_socket(sa_family_t family, int proto, int idx, int unicast) { interface *ifp; int fd = -1; @@ -1058,7 +1063,8 @@ open_vrrp_socket(sa_family_t family, int } /* Join the VRRP MCAST group */ - if_join_vrrp_group(family, &fd, ifp, proto); + if (!unicast) + if_join_vrrp_group(family, &fd, ifp, proto); if (fd < 0) return -1; @@ -1073,7 +1079,7 @@ open_vrrp_socket(sa_family_t family, int void close_vrrp_socket(vrrp_rt * vrrp) { - if_leave_vrrp_group(vrrp->family, vrrp->fd_in, vrrp->ifp); + if_leave_vrrp_group(vrrp->family, vrrp->fd_in, vrrp->ifp, !vrrp->unicast_peer); close(vrrp->fd_out); } @@ -1087,8 +1093,8 @@ new_vrrp_socket(vrrp_rt * vrrp) close_vrrp_socket(vrrp); remove_vrrp_fd_bucket(vrrp); proto = (vrrp->auth_type == VRRP_AUTH_AH) ? IPPROTO_IPSEC_AH : IPPROTO_VRRP; - vrrp->fd_in = open_vrrp_socket(vrrp->family, proto, IF_INDEX(vrrp->ifp)); - vrrp->fd_out = open_vrrp_send_socket(vrrp->family, proto, IF_INDEX(vrrp->ifp)); + vrrp->fd_in = open_vrrp_socket(vrrp->family, proto, IF_INDEX(vrrp->ifp), !vrrp->unicast_peer); + vrrp->fd_out = open_vrrp_send_socket(vrrp->family, proto, IF_INDEX(vrrp->ifp), !vrrp->unicast_peer); alloc_vrrp_fd_bucket(vrrp); /* Sync the other desc */ diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp.c.orig keepalived-1.2.7-unicast/keepalived/vrrp/vrrp.c.orig --- keepalived-1.2.7/keepalived/vrrp/vrrp.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp.c.orig 2012-08-17 01:21:28.000000000 +0200 @@ -0,0 +1,1327 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: VRRP implementation of VRRPv2 as specified in rfc2338. + * VRRP is a protocol which elect a master server on a LAN. If the + * master fails, a backup server takes over. + * The original implementation has been made by jerome etienne. + * + * Author: Alexandre Cassen, <acassen@linux-vs.org> + * + * 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. + * + * 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. + * + * Copyright (C) 2001-2012 Alexandre Cassen, <acassen@gmail.com> + */ + +/* local include */ +#include <ctype.h> +#include <sys/uio.h> +#include "vrrp_arp.h" +#include "vrrp_ndisc.h" +#include "vrrp_scheduler.h" +#include "vrrp_notify.h" +#include "ipvswrapper.h" +#include "vrrp.h" +#include "vrrp_data.h" +#include "vrrp_sync.h" +#include "vrrp_index.h" +#include "vrrp_vmac.h" +#ifdef _WITH_SNMP_ +#include "vrrp_snmp.h" +#endif +#include "memory.h" +#include "list.h" +#include "logger.h" +#include "main.h" +#include "utils.h" +#include "notify.h" + +/* add/remove Virtual IP addresses */ +static int +vrrp_handle_ipaddress(vrrp_rt * vrrp, int cmd, int type) +{ + if (debug & 32) + log_message(LOG_INFO, "VRRP_Instance(%s) %s protocol %s", vrrp->iname, + (cmd == IPADDRESS_ADD) ? "setting" : "removing", + (type == VRRP_VIP_TYPE) ? "VIPs." : "E-VIPs."); + netlink_iplist((type == VRRP_VIP_TYPE) ? vrrp->vip : vrrp->evip, cmd); + return 1; +} + +/* add/remove Virtual routes */ +static int +vrrp_handle_iproutes(vrrp_rt * vrrp, int cmd) +{ + if (debug & 32) + log_message(LOG_INFO, "VRRP_Instance(%s) %s protocol Virtual Routes", + vrrp->iname, + (cmd == IPROUTE_ADD) ? "setting" : "removing"); + netlink_rtlist_ipv4(vrrp->vroutes, cmd); + return 1; +} + +/* IP header length */ +static int +vrrp_iphdr_len(vrrp_rt * vrrp) +{ + return sizeof (struct iphdr); +} + +/* IPSEC AH header length */ +int +vrrp_ipsecah_len(void) +{ + return sizeof (ipsec_ah); +} + +/* VRRP header length */ +static int +vrrp_hd_len(vrrp_rt * vrrp) +{ + int len = sizeof (vrrp_pkt); + if (vrrp->family == AF_INET) + len += VRRP_AUTH_LEN + ((!LIST_ISEMPTY(vrrp->vip)) ? LIST_SIZE(vrrp->vip) * sizeof (uint32_t) : 0); + return len; +} + +/* VRRP header pointer from buffer */ +vrrp_pkt * +vrrp_get_header(sa_family_t family, char *buf, int *proto, uint32_t *saddr) +{ + struct iphdr *iph; + vrrp_pkt *hd = NULL; + + if (family == AF_INET) { + iph = (struct iphdr *) buf; + *saddr = iph->saddr; + + /* Fill the VRRP header */ + switch (iph->protocol) { + case IPPROTO_IPSEC_AH: + *proto = IPPROTO_IPSEC_AH; + hd = (vrrp_pkt *) ((char *) iph + (iph->ihl << 2) + + vrrp_ipsecah_len()); + break; + case IPPROTO_VRRP: + *proto = IPPROTO_VRRP; + hd = (vrrp_pkt *) ((char *) iph + (iph->ihl << 2)); + break; + } + } else if (family == AF_INET6) { + *proto = IPPROTO_VRRP; + *saddr = 0; + hd = (vrrp_pkt *) buf; + } + + return hd; +} + +/* + * IPSEC AH incoming packet check. + * return 0 for a valid pkt, != 0 otherwise. + */ +static int +vrrp_in_chk_ipsecah(vrrp_rt * vrrp, char *buffer) +{ + struct iphdr *ip = (struct iphdr *) (buffer); + ipsec_ah *ah = (ipsec_ah *) ((char *) ip + (ip->ihl << 2)); + unsigned char *digest; + uint32_t backup_auth_data[3]; + + /* first verify that the SPI value is equal to src IP */ + if (ah->spi != ip->saddr) { + log_message(LOG_INFO, + "IPSEC AH : invalid IPSEC SPI value. %d and expect %d", + ip->saddr, ah->spi); + return 1; + } + + /* + * then proceed with the sequence number to prevent against replay attack. + * For inbound processing, we increment seq_number counter to audit + * sender counter. + */ + vrrp->ipsecah_counter->seq_number++; + if (ntohl(ah->seq_number) >= vrrp->ipsecah_counter->seq_number || vrrp->sync) { + vrrp->ipsecah_counter->seq_number = ntohl(ah->seq_number); + } else { + log_message(LOG_INFO, + "VRRP_Instance(%s) IPSEC-AH : sequence number %d" + " already proceeded. Packet dropped. Local(%d)", vrrp->iname + , ntohl(ah->seq_number), vrrp->ipsecah_counter->seq_number); + return 1; + } + + /* + * then compute a ICV to compare with the one present in AH pkt. + * alloc a temp memory space to stock the ip mutable fields + */ + digest = (unsigned char *) MALLOC(16 * sizeof (unsigned char *)); + + /* zero the ip mutable fields */ + ip->tos = 0; + ip->frag_off = 0; + ip->check = 0; + memcpy(backup_auth_data, ah->auth_data, sizeof (ah->auth_data)); + memset(ah->auth_data, 0, sizeof (ah->auth_data)); + + /* Compute the ICV */ + hmac_md5((unsigned char *) buffer, + vrrp_iphdr_len(vrrp) + vrrp_ipsecah_len() + vrrp_hd_len(vrrp) + , vrrp->auth_data, sizeof (vrrp->auth_data) + , digest); + + if (memcmp(backup_auth_data, digest, HMAC_MD5_TRUNC) != 0) { + log_message(LOG_INFO, "VRRP_Instance(%s) IPSEC-AH : invalid" + " IPSEC HMAC-MD5 value. Due to fields mutation" + " or bad password !", vrrp->iname); + return 1; + } + + FREE(digest); + return 0; +} + +/* check if ipaddr is present in VIP buffer */ +static int +vrrp_in_chk_vips(vrrp_rt * vrrp, ip_address *ipaddress, unsigned char *buffer) +{ + int i; + uint32_t ipbuf; + + /* Just skip IPv6 address, when we are using a mixed v4/v6 vips + * set inside se same VRRP instance. + */ + if (IP_IS6(ipaddress)) + return 1; + + for (i = 0; i < LIST_SIZE(vrrp->vip); i++) { + bcopy(buffer + i * sizeof (uint32_t), &ipbuf, + sizeof (uint32_t)); + if (ipaddress->u.sin.sin_addr.s_addr == ipbuf) + return 1; + } + + return 0; +} + +/* + * VRRP incoming packet check. + * return 0 if the pkt is valid, != 0 otherwise. + */ +static int +vrrp_in_chk(vrrp_rt * vrrp, char *buffer) +{ + struct iphdr *ip; + int ihl, vrrp_pkt_len; + ipsec_ah *ah; + vrrp_pkt *hd; + unsigned char *vips; + ip_address *ipaddress; + element e; + + /* IPv4 related */ + if (vrrp->family == AF_INET) { + + ip = (struct iphdr *) (buffer); + ihl = ip->ihl << 2; + + if (vrrp->auth_type == VRRP_AUTH_AH) { + ah = (ipsec_ah *) (buffer + ihl); + hd = (vrrp_pkt *) (ah + vrrp_ipsecah_len()); + } else { + hd = (vrrp_pkt *) (buffer + ihl); + } + + /* pointer to vrrp vips pkt zone */ + vips = (unsigned char *) ((char *) hd + sizeof (vrrp_pkt)); + + /* MUST verify that the IP TTL is 255 */ + if (ip->ttl != VRRP_IP_TTL) { + log_message(LOG_INFO, "invalid ttl. %d and expect %d", ip->ttl, + VRRP_IP_TTL); + return VRRP_PACKET_KO; + } + + /* + * MUST verify that the received packet length is greater than or + * equal to the VRRP header + */ + if ((ntohs(ip->tot_len) - ihl) <= sizeof (vrrp_pkt)) { + log_message(LOG_INFO, + "ip payload too short. %d and expect at least %d", + ntohs(ip->tot_len) - ihl, sizeof (vrrp_pkt)); + return VRRP_PACKET_KO; + } + + if (!LIST_ISEMPTY(vrrp->vip)) { + /* + * MAY verify that the IP address(es) associated with the + * VRID are valid + */ + if (hd->naddr != LIST_SIZE(vrrp->vip)) { + log_message(LOG_INFO, + "receive an invalid ip number count associated with VRID!"); + return VRRP_PACKET_KO; + } + + for (e = LIST_HEAD(vrrp->vip); e; ELEMENT_NEXT(e)) { + ipaddress = ELEMENT_DATA(e); + if (!vrrp_in_chk_vips(vrrp, ipaddress, vips)) { + log_message(LOG_INFO, "ip address associated with VRID" + " not present in received packet : %s", + inet_ntop2(ipaddress->u.sin.sin_addr.s_addr)); + log_message(LOG_INFO, + "one or more VIP associated with" + " VRID mismatch actual MASTER advert"); + return VRRP_PACKET_KO; + } + } + } + + /* check the authentication if it is a passwd */ + if (hd->auth_type == VRRP_AUTH_PASS) { + char *pw = (char *) ip + ntohs(ip->tot_len) + - sizeof (vrrp->auth_data); + if (memcmp(pw, vrrp->auth_data, sizeof(vrrp->auth_data)) != 0) { + log_message(LOG_INFO, "receive an invalid passwd!"); + return VRRP_PACKET_KO; + } + } + + /* check the authenicaion if it is ipsec ah */ + if (hd->auth_type == VRRP_AUTH_AH) + return (vrrp_in_chk_ipsecah(vrrp, buffer)); + + /* Set expected vrrp packet lenght */ + vrrp_pkt_len = sizeof(vrrp_pkt) + VRRP_AUTH_LEN + hd->naddr * sizeof(uint32_t); + + } else if (vrrp->family == AF_INET6) { /* IPv6 related */ + + hd = (vrrp_pkt *) buffer; + vrrp_pkt_len = sizeof(vrrp_pkt); + + } else { + return VRRP_PACKET_KO; + } + + /* MUST verify the VRRP version */ + if ((hd->vers_type >> 4) != VRRP_VERSION) { + log_message(LOG_INFO, "invalid version. %d and expect %d", + (hd->vers_type >> 4), VRRP_VERSION); + return VRRP_PACKET_KO; + } + + /* MUST verify the VRRP checksum */ + if (in_csum((u_short *) hd, vrrp_pkt_len, 0)) { + log_message(LOG_INFO, "Invalid vrrp checksum"); + return VRRP_PACKET_KO; + } + + /* + * MUST perform authentication specified by Auth Type + * check the authentication type + */ + if (vrrp->auth_type != hd->auth_type) { + log_message(LOG_INFO, "receive a %d auth, expecting %d!", + hd->auth_type, vrrp->auth_type); + return VRRP_PACKET_KO; + } + + /* MUST verify that the VRID is valid on the receiving interface */ + if (vrrp->vrid != hd->vrid) { + log_message(LOG_INFO, + "received VRID mismatch. Received %d, Expected %d", + hd->vrid, vrrp->vrid); + return VRRP_PACKET_DROP; + } + + if (LIST_ISEMPTY(vrrp->vip) && hd->naddr > 0) { + log_message(LOG_INFO, "receive an invalid ip number count associated with VRID!"); + return VRRP_PACKET_KO; + } + + /* + * MUST verify that the Adver Interval in the packet is the same as + * the locally configured for this virtual router + */ + if (vrrp->adver_int / TIMER_HZ != hd->adver_int) { + log_message(LOG_INFO, "advertissement interval mismatch mine=%d rcved=%d", + vrrp->adver_int, hd->adver_int); + /* to prevent concurent VRID running => multiple master in 1 VRID */ + return VRRP_PACKET_DROP; + } + + return VRRP_PACKET_OK; +} + +/* build IP header */ +static void +vrrp_build_ip(vrrp_rt * vrrp, char *buffer, int buflen) +{ + struct iphdr *ip = (struct iphdr *) (buffer); + + ip->ihl = 5; + ip->version = 4; + /* set tos to internet network control */ + ip->tos = 0xc0; + ip->tot_len = ip->ihl * 4 + vrrp_hd_len(vrrp); + ip->tot_len = htons(ip->tot_len); + ip->id = htons(++vrrp->ip_id); + /* kernel will fill in ID if left to 0, so we overflow to 1 */ + if (vrrp->ip_id == 65535) + vrrp->ip_id = 1; + ip->frag_off = 0; + ip->ttl = VRRP_IP_TTL; + + /* fill protocol type --rfc2402.2 */ + ip->protocol = + (vrrp->auth_type == VRRP_AUTH_AH) ? IPPROTO_IPSEC_AH : IPPROTO_VRRP; + ip->saddr = VRRP_PKT_SADDR(vrrp); + ip->daddr = htonl(INADDR_VRRP_GROUP); + + /* checksum must be done last */ + ip->check = in_csum((u_short *) ip, ip->ihl * 4, 0); +} + +/* build IPSEC AH header */ +static void +vrrp_build_ipsecah(vrrp_rt * vrrp, char *buffer, int buflen) +{ + ICV_mutable_fields *ip_mutable_fields; + unsigned char *digest; + struct iphdr *ip = (struct iphdr *) (buffer); + ipsec_ah *ah = (ipsec_ah *) (buffer + sizeof (struct iphdr)); + + /* alloc a temp memory space to stock the ip mutable fields */ + ip_mutable_fields = (ICV_mutable_fields *) MALLOC(sizeof (ICV_mutable_fields)); + + /* fill in next header filed --rfc2402.2.1 */ + ah->next_header = IPPROTO_VRRP; + + /* update IP header total length value */ + ip->tot_len = ip->ihl * 4 + vrrp_ipsecah_len() + vrrp_hd_len(vrrp); + ip->tot_len = htons(ip->tot_len); + + /* update ip checksum */ + ip->check = 0; + ip->check = in_csum((u_short *) ip, ip->ihl * 4, 0); + + /* backup the ip mutable fields */ + ip_mutable_fields->tos = ip->tos; + ip_mutable_fields->frag_off = ip->frag_off; + ip_mutable_fields->check = ip->check; + + /* zero the ip mutable fields */ + ip->tos = 0; + ip->frag_off = 0; + ip->check = 0; + + /* fill in the Payload len field */ + ah->payload_len = IPSEC_AH_PLEN; + + /* The SPI value is filled with the ip header source address. + SPI uniquely identify the Security Association (SA). This value + is chosen by the recipient itself when setting up the SA. In a + multicast environment, this becomes unfeasible. + + If left to the sender, the choice of the SPI value should be done + so by the sender that it cannot possibly conflict with SPI values + chosen by other entities sending IPSEC traffic to any of the receivers. + To overpass this problem, the rule I have chosen to implement here is + that the SPI value chosen by the sender is based on unique information + such as its IP address. + -- INTERNET draft : <draft-paridaens-xcast-sec-framework-01.txt> + */ + ah->spi = ip->saddr; + + /* Processing sequence number. + Cycled assumed if 0xFFFFFFFD reached. So the MASTER state is free for another srv. + Here can result a flapping MASTER state owner when max seq_number value reached. + => Much work needed here. + In the current implementation if counter has cycled, we stop sending adverts and + become BACKUP. If all the master are down we reset the counter for becoming MASTER. + */ + if (vrrp->ipsecah_counter->seq_number > 0xFFFFFFFD) { + vrrp->ipsecah_counter->cycle = 1; + } else { + vrrp->ipsecah_counter->seq_number++; + } + + ah->seq_number = htonl(vrrp->ipsecah_counter->seq_number); + + /* Compute the ICV & trunc the digest to 96bits + => No padding needed. + -- rfc2402.3.3.3.1.1.1 & rfc2401.5 + */ + digest = (unsigned char *) MALLOC(16 * sizeof (unsigned char *)); + hmac_md5((unsigned char *) buffer, buflen, vrrp->auth_data, sizeof (vrrp->auth_data) + , digest); + memcpy(ah->auth_data, digest, HMAC_MD5_TRUNC); + + /* Restore the ip mutable fields */ + ip->tos = ip_mutable_fields->tos; + ip->frag_off = ip_mutable_fields->frag_off; + ip->check = ip_mutable_fields->check; + + FREE(ip_mutable_fields); + FREE(digest); +} + +/* build VRRP header */ +static int +vrrp_build_vrrp(vrrp_rt * vrrp, int prio, char *buffer) +{ + int i = 0; + vrrp_pkt *hd = (vrrp_pkt *) buffer; + uint32_t *iparr; + element e; + ip_address *ip_addr; + + /* Family independant */ + hd->vers_type = (VRRP_VERSION << 4) | VRRP_PKT_ADVERT; + hd->vrid = vrrp->vrid; + hd->priority = prio; + hd->naddr = (!LIST_ISEMPTY(vrrp->vip)) ? LIST_SIZE(vrrp->vip) : 0; + hd->auth_type = vrrp->auth_type; + hd->adver_int = vrrp->adver_int / TIMER_HZ; + + /* Family specific */ + if (vrrp->family == AF_INET) { + /* copy the ip addresses */ + iparr = (uint32_t *) ((char *) hd + sizeof (*hd)); + if (!LIST_ISEMPTY(vrrp->vip)) { + for (e = LIST_HEAD(vrrp->vip); e; ELEMENT_NEXT(e)) { + ip_addr = ELEMENT_DATA(e); + if (IP_IS6(ip_addr)) + continue; + else + iparr[i++] = ip_addr->u.sin.sin_addr.s_addr; + } + } + + /* copy the passwd if the authentication is VRRP_AH_PASS */ + if (vrrp->auth_type == VRRP_AUTH_PASS) { + int vip_count = (!LIST_ISEMPTY(vrrp->vip)) ? LIST_SIZE(vrrp->vip) : 0; + char *pw = (char *) hd + sizeof (*hd) + vip_count * 4; + memcpy(pw, vrrp->auth_data, sizeof (vrrp->auth_data)); + } + } + + /* finaly compute vrrp checksum */ + hd->chksum = in_csum((u_short *) hd, vrrp_hd_len(vrrp), 0); + + return 0; +} + +/* build VRRP packet */ +static void +vrrp_build_pkt(vrrp_rt * vrrp, int prio) +{ + char *bufptr; + int len; + + /* save reference values */ + bufptr = VRRP_SEND_BUFFER(vrrp); + len = VRRP_SEND_BUFFER_SIZE(vrrp); + + if (vrrp->family == AF_INET) { + /* build the ip header */ + vrrp_build_ip(vrrp, VRRP_SEND_BUFFER(vrrp), VRRP_SEND_BUFFER_SIZE(vrrp)); + + /* build the vrrp header */ + vrrp->send_buffer += vrrp_iphdr_len(vrrp); + + if (vrrp->auth_type == VRRP_AUTH_AH) + vrrp->send_buffer += vrrp_ipsecah_len(); + vrrp->send_buffer_size -= vrrp_iphdr_len(vrrp); + + if (vrrp->auth_type == VRRP_AUTH_AH) + vrrp->send_buffer_size -= vrrp_ipsecah_len(); + vrrp_build_vrrp(vrrp, prio, vrrp->send_buffer); + + /* build the IPSEC AH header */ + if (vrrp->auth_type == VRRP_AUTH_AH) { + vrrp->send_buffer_size += vrrp_iphdr_len(vrrp) + vrrp_ipsecah_len(); + vrrp_build_ipsecah(vrrp, bufptr, VRRP_SEND_BUFFER_SIZE(vrrp)); + } + } else if (vrrp->family == AF_INET6) { + vrrp_build_vrrp(vrrp, prio, VRRP_SEND_BUFFER(vrrp)); + } + + /* restore reference values */ + vrrp->send_buffer = bufptr; + vrrp->send_buffer_size = len; +} + +/* send VRRP packet */ +static int +vrrp_send_pkt(vrrp_rt * vrrp) +{ + struct sockaddr_in6 dst6; + struct sockaddr_in dst4; + struct msghdr msg; + struct iovec iov; + + /* Build the message data */ + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + iov.iov_base = VRRP_SEND_BUFFER(vrrp); + iov.iov_len = VRRP_SEND_BUFFER_SIZE(vrrp); + + /* Sending path */ + if (vrrp->family == AF_INET) { + memset(&dst4, 0, sizeof(dst4)); + dst4.sin_family = AF_INET; + dst4.sin_addr.s_addr = htonl(INADDR_VRRP_GROUP); + + msg.msg_name = &dst4; + msg.msg_namelen = sizeof(dst4); + } else if (vrrp->family == AF_INET6) { + memset(&dst6, 0, sizeof(dst6)); + dst6.sin6_family = AF_INET6; + dst6.sin6_port = htons(IPPROTO_VRRP); + dst6.sin6_addr.s6_addr16[0] = htons(0xff02); + dst6.sin6_addr.s6_addr16[7] = htons(0x12); + + msg.msg_name = &dst6; + msg.msg_namelen = sizeof(dst6); + } + + /* Send the packet */ + return sendmsg(vrrp->fd_out, &msg, MSG_DONTROUTE); +} + +/* Allocate the sending buffer */ +static void +vrrp_alloc_send_buffer(vrrp_rt * vrrp) +{ + vrrp->send_buffer_size = vrrp_hd_len(vrrp); + + if (vrrp->family == AF_INET) { + vrrp->send_buffer_size = vrrp_iphdr_len(vrrp) + vrrp_hd_len(vrrp); + if (vrrp->auth_type == VRRP_AUTH_AH) + vrrp->send_buffer_size += vrrp_ipsecah_len(); + } + + vrrp->send_buffer = MALLOC(VRRP_SEND_BUFFER_SIZE(vrrp)); +} + +/* send VRRP advertissement */ +int +vrrp_send_adv(vrrp_rt * vrrp, int prio) +{ + /* alloc send buffer */ + if (!vrrp->send_buffer) + vrrp_alloc_send_buffer(vrrp); + else + memset(vrrp->send_buffer, 0, VRRP_SEND_BUFFER_SIZE(vrrp)); + + /* build the packet */ + vrrp_build_pkt(vrrp, prio); + + /* send it */ + return vrrp_send_pkt(vrrp); +} + +/* Received packet processing */ +int +vrrp_check_packet(vrrp_rt * vrrp, char *buf, int buflen) +{ + int ret; + + if (buflen > 0) { + ret = vrrp_in_chk(vrrp, buf); + + if (ret == VRRP_PACKET_DROP) { + log_message(LOG_INFO, "Sync instance needed on %s !!!", + IF_NAME(vrrp->ifp)); + } + + if (ret == VRRP_PACKET_KO) + log_message(LOG_INFO, "bogus VRRP packet received on %s !!!", + IF_NAME(vrrp->ifp)); + return ret; + } + + return VRRP_PACKET_NULL; +} + +/* Gratuitous ARP on each VIP */ +static void +vrrp_send_update(vrrp_rt * vrrp, ip_address * ipaddress, int idx) +{ + char *msg; + char addr_str[41]; + + if (!IP_IS6(ipaddress)) { + msg = "gratuitous ARPs"; + inet_ntop(AF_INET, &ipaddress->u.sin.sin_addr, addr_str, 41); + send_gratuitous_arp(ipaddress); + } else { + msg = "Unsolicited Neighbour Adverts"; + inet_ntop(AF_INET6, &ipaddress->u.sin6_addr, addr_str, 41); + ndisc_send_unsolicited_na(ipaddress); + } + + if (0 == idx && debug & 32) { + log_message(LOG_INFO, "VRRP_Instance(%s) Sending %s on %s for %s", + vrrp->iname, msg, IF_NAME(ipaddress->ifp), addr_str); + } +} + +void +vrrp_send_link_update(vrrp_rt * vrrp) +{ + int j; + ip_address *ipaddress; + element e; + + /* Only send gratuitous ARP if VIP are set */ + if (!VRRP_VIP_ISSET(vrrp)) + return; + + /* send gratuitous arp for each virtual ip */ + for (j = 0; j < 5; j++) { + if (!LIST_ISEMPTY(vrrp->vip)) { + for (e = LIST_HEAD(vrrp->vip); e; ELEMENT_NEXT(e)) { + ipaddress = ELEMENT_DATA(e); + vrrp_send_update(vrrp, ipaddress, j); + } + } + + if (!LIST_ISEMPTY(vrrp->evip)) { + for (e = LIST_HEAD(vrrp->evip); e; ELEMENT_NEXT(e)) { + ipaddress = ELEMENT_DATA(e); + vrrp_send_update(vrrp, ipaddress, j); + } + } + } +} + +/* becoming master */ +void +vrrp_state_become_master(vrrp_rt * vrrp) +{ + /* add the ip addresses */ + if (!LIST_ISEMPTY(vrrp->vip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_VIP_TYPE); + if (!LIST_ISEMPTY(vrrp->evip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_EVIP_TYPE); + vrrp->vipset = 1; + + /* add virtual routes */ + if (!LIST_ISEMPTY(vrrp->vroutes)) + vrrp_handle_iproutes(vrrp, IPROUTE_ADD); + + /* remotes neighbour update */ + vrrp_send_link_update(vrrp); + + /* Check if notify is needed */ + notify_instance_exec(vrrp, VRRP_STATE_MAST); + +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + +#ifdef _HAVE_IPVS_SYNCD_ + /* Check if sync daemon handling is needed */ + if (vrrp->lvs_syncd_if) + ipvs_syncd_master(vrrp->lvs_syncd_if, vrrp->vrid); +#endif +} + +void +vrrp_state_goto_master(vrrp_rt * vrrp) +{ + /* + * Send an advertisement. To force a new master + * election. + */ + if (vrrp->sync && !vrrp_sync_goto_master(vrrp)) + return; + vrrp_send_adv(vrrp, vrrp->effective_priority); + + vrrp->state = VRRP_STATE_MAST; + log_message(LOG_INFO, "VRRP_Instance(%s) Transition to MASTER STATE", + vrrp->iname); +} + +/* leaving master state */ +void +vrrp_restore_interface(vrrp_rt * vrrp, int advF) +{ + /* if we stop vrrp, warn the other routers to speed up the recovery */ + if (advF) { + syslog(LOG_INFO, "VRRP_Instance(%s) sending 0 priority", + vrrp->iname); + vrrp_send_adv(vrrp, VRRP_PRIO_STOP); + } + + /* remove virtual routes */ + if (!LIST_ISEMPTY(vrrp->vroutes)) + vrrp_handle_iproutes(vrrp, IPROUTE_DEL); + + /* + * Remove the ip addresses. + * + * If started with "--dont-release-vrrp" (debug & 8) then try to remove + * addresses even if we didn't add them during this run. + */ + if (debug & 8 || VRRP_VIP_ISSET(vrrp)) { + if (!LIST_ISEMPTY(vrrp->vip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_DEL, + VRRP_VIP_TYPE); + if (!LIST_ISEMPTY(vrrp->evip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_DEL, + VRRP_EVIP_TYPE); + vrrp->vipset = 0; + } + +} + +void +vrrp_state_leave_master(vrrp_rt * vrrp) +{ + if (VRRP_VIP_ISSET(vrrp)) { +#ifdef _HAVE_IPVS_SYNCD_ + /* Check if sync daemon handling is needed */ + if (vrrp->lvs_syncd_if) + ipvs_syncd_backup(vrrp->lvs_syncd_if, vrrp->vrid); +#endif + } + + /* set the new vrrp state */ + switch (vrrp->wantstate) { + case VRRP_STATE_BACK: + log_message(LOG_INFO, "VRRP_Instance(%s) Entering BACKUP STATE", vrrp->iname); + vrrp_restore_interface(vrrp, 0); + vrrp->state = vrrp->wantstate; + notify_instance_exec(vrrp, VRRP_STATE_BACK); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + break; + case VRRP_STATE_GOTO_FAULT: + log_message(LOG_INFO, "VRRP_Instance(%s) Entering FAULT STATE", vrrp->iname); + vrrp_restore_interface(vrrp, 0); + vrrp->state = VRRP_STATE_FAULT; + notify_instance_exec(vrrp, VRRP_STATE_FAULT); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + break; + } + + /* Set the down timer */ + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); +} + +/* BACKUP state processing */ +void +vrrp_state_backup(vrrp_rt * vrrp, char *buf, int buflen) +{ + vrrp_pkt *hd; + uint32_t saddr; + int ret = 0, proto; + + /* Process the incoming packet */ + hd = vrrp_get_header(vrrp->family, buf, &proto, &saddr); + ret = vrrp_check_packet(vrrp, buf, buflen); + + if (ret == VRRP_PACKET_KO || ret == VRRP_PACKET_NULL) { + log_message(LOG_INFO, "VRRP_Instance(%s) ignoring received advertisment..." + , vrrp->iname); + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + } else if (hd->priority == 0) { + vrrp->ms_down_timer = VRRP_TIMER_SKEW(vrrp); + } else if (vrrp->nopreempt || hd->priority >= vrrp->effective_priority || + timer_cmp(vrrp->preempt_time, timer_now()) > 0) { + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + } else if (hd->priority < vrrp->effective_priority) { + log_message(LOG_INFO, "VRRP_Instance(%s) forcing a new MASTER election" + , vrrp->iname); + vrrp->wantstate = VRRP_STATE_GOTO_MASTER; + vrrp_send_adv(vrrp, vrrp->effective_priority); + } +} + +/* MASTER state processing */ +int +vrrp_state_master_tx(vrrp_rt * vrrp, const int prio) +{ + int ret = 0; + + if (!VRRP_VIP_ISSET(vrrp)) { + log_message(LOG_INFO, "VRRP_Instance(%s) Entering MASTER STATE" + , vrrp->iname); + vrrp_state_become_master(vrrp); + ret = 1; + } + + vrrp_send_adv(vrrp, + (prio == VRRP_PRIO_OWNER) ? VRRP_PRIO_OWNER : + vrrp->effective_priority); + return ret; +} + +int +vrrp_state_master_rx(vrrp_rt * vrrp, char *buf, int buflen) +{ + vrrp_pkt *hd = NULL; + int ret = 0, proto = 0; + uint32_t saddr = 0; + ipsec_ah *ah; + + /* return on link failure */ + if (vrrp->wantstate == VRRP_STATE_GOTO_FAULT) { + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + vrrp->state = VRRP_STATE_FAULT; + notify_instance_exec(vrrp, VRRP_STATE_FAULT); + return 1; + } + + /* Process the incoming packet */ + hd = vrrp_get_header(vrrp->family, buf, &proto, &saddr); + ret = vrrp_check_packet(vrrp, buf, buflen); + + if (ret == VRRP_PACKET_KO || + ret == VRRP_PACKET_NULL || ret == VRRP_PACKET_DROP) { + log_message(LOG_INFO, + "VRRP_Instance(%s) Dropping received VRRP packet...", + vrrp->iname); + return 0; + } else if (hd->priority < vrrp->effective_priority) { + /* We receive a lower prio adv we just refresh remote ARP cache */ + log_message(LOG_INFO, "VRRP_Instance(%s) Received lower prio advert" + ", forcing new election", vrrp->iname); + if (proto == IPPROTO_IPSEC_AH) { + ah = (ipsec_ah *) (buf + sizeof(struct iphdr)); + log_message(LOG_INFO, "VRRP_Instance(%s) IPSEC-AH : Syncing seq_num" + " - Increment seq" + , vrrp->iname); + vrrp->ipsecah_counter->seq_number = ntohl(ah->seq_number) + 1; + vrrp->ipsecah_counter->cycle = 0; + } + vrrp_send_adv(vrrp, vrrp->effective_priority); + vrrp_send_link_update(vrrp); + return 0; + } else if (hd->priority == 0) { + vrrp_send_adv(vrrp, vrrp->effective_priority); + return 0; + } else if (vrrp->family == AF_INET) { + if (hd->priority > vrrp->effective_priority || + (hd->priority == vrrp->effective_priority && + ntohl(saddr) > VRRP_PKT_SADDR(vrrp))) { + log_message(LOG_INFO, "VRRP_Instance(%s) Received higher prio advert" + , vrrp->iname); + if (proto == IPPROTO_IPSEC_AH) { + ah = (ipsec_ah *) (buf + sizeof(struct iphdr)); + log_message(LOG_INFO, "VRRP_Instance(%s) IPSEC-AH : Syncing seq_num" + " - Decrement seq" + , vrrp->iname); + vrrp->ipsecah_counter->seq_number = ntohl(ah->seq_number) - 1; + vrrp->ipsecah_counter->cycle = 0; + } + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + vrrp->wantstate = VRRP_STATE_BACK; + vrrp->state = VRRP_STATE_BACK; + return 1; + } + } else if (vrrp->family == AF_INET6) { + /* FIXME: compare v6 saddr to link local when prio are equal !!! */ + if (hd->priority > vrrp->effective_priority) { + log_message(LOG_INFO, "VRRP_Instance(%s) Received higher prio advert" + , vrrp->iname); + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + vrrp->wantstate = VRRP_STATE_BACK; + vrrp->state = VRRP_STATE_BACK; + return 1; + } + } + + return 0; +} + +int +vrrp_state_fault_rx(vrrp_rt * vrrp, char *buf, int buflen) +{ + vrrp_pkt *hd; + uint32_t saddr; + int ret = 0, proto; + + /* Process the incoming packet */ + hd = vrrp_get_header(vrrp->family, buf, &proto, &saddr); + ret = vrrp_check_packet(vrrp, buf, buflen); + + if (ret == VRRP_PACKET_KO || ret == VRRP_PACKET_NULL || ret == VRRP_PACKET_DROP) { + log_message(LOG_INFO, "VRRP_Instance(%s) Dropping received VRRP packet..." + , vrrp->iname); + return 0; + } else if (vrrp->effective_priority > hd->priority || + hd->priority == VRRP_PRIO_OWNER) { + if (!vrrp->nopreempt) + return 1; + } + + return 0; +} + +/* check for minimum configuration requirements */ +static int +chk_min_cfg(vrrp_rt * vrrp) +{ + if (vrrp->vrid == 0) { + log_message(LOG_INFO, "VRRP_Instance(%s) the virtual id must be set!", + vrrp->iname); + return 0; + } + if (!vrrp->ifp) { + log_message(LOG_INFO, "VRRP_Instance(%s) Unknown interface !", + vrrp->iname); + return 0; + } + + return 1; +} + +/* open a VRRP sending socket */ +int +open_vrrp_send_socket(sa_family_t family, int proto, int idx) +{ + interface *ifp; + int fd = -1; + + /* Retreive interface */ + ifp = if_get_by_ifindex(idx); + + /* Create and init socket descriptor */ + fd = socket(family, SOCK_RAW, proto); + if (fd < 0) { + log_message(LOG_INFO, "cant open raw socket. errno=%d", errno); + return -1; + } + + if (family == AF_INET) { + /* Set v4 related */ + if_setsockopt_hdrincl(&fd); + if_setsockopt_bindtodevice(&fd, ifp); + if_setsockopt_mcast_loop(family, &fd); + if_setsockopt_priority(&fd); + if (fd < 0) + return -1; + } else if (family == AF_INET6) { + /* Set v6 related */ + if_setsockopt_mcast_hops(family, &fd); + if_setsockopt_mcast_if(family, &fd, ifp); + if_setsockopt_mcast_loop(family, &fd); + if_setsockopt_priority(&fd); + if (fd < 0) + return -1; + } else { + log_message(LOG_INFO, "cant open raw socket. unknow family=%d" + , family); + close(fd); + return -1; + } + + return fd; +} + +/* open a VRRP socket and join the multicast group. */ +int +open_vrrp_socket(sa_family_t family, int proto, int idx) +{ + interface *ifp; + int fd = -1; + + /* Retreive interface */ + ifp = if_get_by_ifindex(idx); + + /* open the socket */ + fd = socket(family, SOCK_RAW, proto); + if (fd < 0) { + int err = errno; + log_message(LOG_INFO, "cant open raw socket. errno=%d", err); + return -1; + } + + /* Join the VRRP MCAST group */ + if_join_vrrp_group(family, &fd, ifp, proto); + if (fd < 0) + return -1; + + if (family == AF_INET) { + /* Bind inbound stream */ + if_setsockopt_bindtodevice(&fd, ifp); + } + + return fd; +} + +void +close_vrrp_socket(vrrp_rt * vrrp) +{ + if_leave_vrrp_group(vrrp->family, vrrp->fd_in, vrrp->ifp); + close(vrrp->fd_out); +} + +int +new_vrrp_socket(vrrp_rt * vrrp) +{ + int old_fd = vrrp->fd_in; + int proto; + + /* close the desc & open a new one */ + close_vrrp_socket(vrrp); + remove_vrrp_fd_bucket(vrrp); + proto = (vrrp->auth_type == VRRP_AUTH_AH) ? IPPROTO_IPSEC_AH : IPPROTO_VRRP; + vrrp->fd_in = open_vrrp_socket(vrrp->family, proto, IF_INDEX(vrrp->ifp)); + vrrp->fd_out = open_vrrp_send_socket(vrrp->family, proto, IF_INDEX(vrrp->ifp)); + alloc_vrrp_fd_bucket(vrrp); + + /* Sync the other desc */ + set_vrrp_fd_bucket(old_fd, vrrp); + + return vrrp->fd_in; +} + +/* handle terminate state */ +void +shutdown_vrrp_instances(void) +{ + list l = vrrp_data->vrrp; + element e; + vrrp_rt *vrrp; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + + /* Remove VIPs/VROUTEs */ + if (vrrp->state == VRRP_STATE_MAST) + vrrp_restore_interface(vrrp, 1); + + /* Remove VMAC */ + if (vrrp->vmac) + netlink_link_del_vmac(vrrp); + + /* Run stop script */ + if (vrrp->script_stop) + notify_exec(vrrp->script_stop); + +#ifdef _HAVE_IPVS_SYNCD_ + /* + * Stop stalled syncd. IPVS syncd state is the + * same as VRRP instance one. We need here to + * stop stalled syncd thread according to last + * VRRP instance state. + */ + if (vrrp->lvs_syncd_if) + ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, + (vrrp->state == VRRP_STATE_MAST) ? IPVS_MASTER: + IPVS_BACKUP, + vrrp->vrid); +#endif + } +} + +/* complete vrrp structure */ +static int +vrrp_complete_instance(vrrp_rt * vrrp) +{ + vrrp->state = VRRP_STATE_INIT; + if (!vrrp->adver_int) + vrrp->adver_int = VRRP_ADVER_DFL * TIMER_HZ; + if (!vrrp->effective_priority) + vrrp->effective_priority = VRRP_PRIO_DFL; + + return (chk_min_cfg(vrrp)); +} + +int +vrrp_complete_init(void) +{ + list l; + element e; + vrrp_rt *vrrp; + vrrp_sgroup *sgroup; + + /* Complete VRRP instance initialization */ + l = vrrp_data->vrrp; + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + if (!vrrp_complete_instance(vrrp)) + return 0; + } + + /* Build synchronization group index */ + l = vrrp_data->vrrp_sync_group; + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + sgroup = ELEMENT_DATA(e); + vrrp_sync_set_group(sgroup); + } + + return 1; +} + +/* Try to find a VRRP instance */ +static vrrp_rt * +vrrp_exist(vrrp_rt * old_vrrp) +{ + element e; + list l = vrrp_data->vrrp; + vrrp_rt *vrrp; + + if (LIST_ISEMPTY(l)) + return NULL; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + if (!strcmp(vrrp->iname, old_vrrp->iname)) + return vrrp; + } + + return NULL; +} + +/* Clear VIP|EVIP not present into the new data */ +static void +clear_diff_vrrp_vip(vrrp_rt * old_vrrp, int type) +{ + vrrp_rt *vrrp = vrrp_exist(old_vrrp); + list l = (type == VRRP_VIP_TYPE) ? old_vrrp->vip : old_vrrp->evip; + list n = (type == VRRP_VIP_TYPE) ? vrrp->vip : vrrp->evip; + clear_diff_address(l, n); +} + +/* Clear virtual routes not present in the new data */ +static void +clear_diff_vrrp_vroutes(vrrp_rt * old_vrrp) +{ + vrrp_rt *vrrp = vrrp_exist(old_vrrp); + clear_diff_routes(old_vrrp->vroutes, vrrp->vroutes); +} + +/* Keep the state from before reload */ +static void +reset_vrrp_state(vrrp_rt * old_vrrp) +{ + /* Keep VRRP state, ipsec AH seq_number */ + vrrp_rt *vrrp = vrrp_exist(old_vrrp); + vrrp->state = old_vrrp->state; + vrrp->init_state = old_vrrp->state; + vrrp->wantstate = old_vrrp->state; + if (!old_vrrp->sync) + vrrp->effective_priority = old_vrrp->effective_priority; + memcpy(vrrp->ipsecah_counter, old_vrrp->ipsecah_counter, sizeof(seq_counter)); + +#ifdef _HAVE_IPVS_SYNCD_ + if (old_vrrp->lvs_syncd_if) + ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, + (old_vrrp->state == VRRP_STATE_MAST) ? IPVS_MASTER: + IPVS_BACKUP, + old_vrrp->vrid); + if (vrrp->lvs_syncd_if) + ipvs_syncd_cmd(IPVS_STARTDAEMON, NULL, + (vrrp->state == VRRP_STATE_MAST) ? IPVS_MASTER: + IPVS_BACKUP, + vrrp->vrid); +#endif + + /* Remember if we had vips up and add new ones if needed */ + vrrp->vipset = old_vrrp->vipset; + if (vrrp->vipset) { + if (!LIST_ISEMPTY(vrrp->vip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_VIP_TYPE); + if (!LIST_ISEMPTY(vrrp->evip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_EVIP_TYPE); + if (!LIST_ISEMPTY(vrrp->vroutes)) + vrrp_handle_iproutes(vrrp, IPROUTE_ADD); + } +} + +/* Diff when reloading configuration */ +void +clear_diff_vrrp(void) +{ + element e; + list l = old_vrrp_data->vrrp; + vrrp_rt *vrrp; + + if (LIST_ISEMPTY(l)) + return; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + vrrp_rt *new_vrrp; + + /* + * Try to find this vrrp into the new conf data + * reloaded. + */ + new_vrrp = vrrp_exist(vrrp); + if (!new_vrrp) { + vrrp_restore_interface(vrrp, 1); + + /* Remove VMAC if one was created */ + if (vrrp->vmac) + netlink_link_del_vmac(vrrp); + } else { + /* + * If this vrrp instance exist in new + * data, then perform a VIP|EVIP diff. + */ + clear_diff_vrrp_vip(vrrp, VRRP_VIP_TYPE); + clear_diff_vrrp_vip(vrrp, VRRP_EVIP_TYPE); + + /* virtual routes diff */ + clear_diff_vrrp_vroutes(vrrp); + + /* + * Remove VMAC if it existed in old vrrp instance, + * but not the new one. + */ + if (vrrp->vmac && !new_vrrp->vmac) { + netlink_link_del_vmac(vrrp); + } + + /* reset the state */ + reset_vrrp_state(vrrp); + } + } +} + +/* Set script status to a sensible value on reload */ +void +clear_diff_script(void) +{ + element e; + list l = old_vrrp_data->vrrp_script; + vrrp_script *vscript, *nvscript; + + if (LIST_ISEMPTY(l)) + return; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vscript = ELEMENT_DATA(e); + if (vscript->result >= vscript->rise) { + nvscript = find_script_by_name(vscript->sname); + if (nvscript) { + log_message(LOG_INFO, "VRRP_Script(%s) considered successful on reload", + nvscript->sname); + nvscript->result = VRRP_SCRIPT_STATUS_INIT_GOOD; + } + } + } +} diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp.c~ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp.c~ --- keepalived-1.2.7/keepalived/vrrp/vrrp.c~ 1970-01-01 01:00:00.000000000 +0100 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp.c~ 2013-05-24 09:39:17.000000000 +0200 @@ -0,0 +1,1333 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: VRRP implementation of VRRPv2 as specified in rfc2338. + * VRRP is a protocol which elect a master server on a LAN. If the + * master fails, a backup server takes over. + * The original implementation has been made by jerome etienne. + * + * Author: Alexandre Cassen, <acassen@linux-vs.org> + * + * 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. + * + * 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. + * + * Copyright (C) 2001-2012 Alexandre Cassen, <acassen@gmail.com> + */ + +/* local include */ +#include <ctype.h> +#include <sys/uio.h> +#include "vrrp_arp.h" +#include "vrrp_ndisc.h" +#include "vrrp_scheduler.h" +#include "vrrp_notify.h" +#include "ipvswrapper.h" +#include "vrrp.h" +#include "vrrp_data.h" +#include "vrrp_sync.h" +#include "vrrp_index.h" +#include "vrrp_vmac.h" +#ifdef _WITH_SNMP_ +#include "vrrp_snmp.h" +#endif +#include "memory.h" +#include "list.h" +#include "logger.h" +#include "main.h" +#include "utils.h" +#include "notify.h" + +/* add/remove Virtual IP addresses */ +static int +vrrp_handle_ipaddress(vrrp_rt * vrrp, int cmd, int type) +{ + if (debug & 32) + log_message(LOG_INFO, "VRRP_Instance(%s) %s protocol %s", vrrp->iname, + (cmd == IPADDRESS_ADD) ? "setting" : "removing", + (type == VRRP_VIP_TYPE) ? "VIPs." : "E-VIPs."); + netlink_iplist((type == VRRP_VIP_TYPE) ? vrrp->vip : vrrp->evip, cmd); + return 1; +} + +/* add/remove Virtual routes */ +static int +vrrp_handle_iproutes(vrrp_rt * vrrp, int cmd) +{ + if (debug & 32) + log_message(LOG_INFO, "VRRP_Instance(%s) %s protocol Virtual Routes", + vrrp->iname, + (cmd == IPROUTE_ADD) ? "setting" : "removing"); + netlink_rtlist_ipv4(vrrp->vroutes, cmd); + return 1; +} + +/* IP header length */ +static int +vrrp_iphdr_len(vrrp_rt * vrrp) +{ + return sizeof (struct iphdr); +} + +/* IPSEC AH header length */ +int +vrrp_ipsecah_len(void) +{ + return sizeof (ipsec_ah); +} + +/* VRRP header length */ +static int +vrrp_hd_len(vrrp_rt * vrrp) +{ + int len = sizeof (vrrp_pkt); + if (vrrp->family == AF_INET) + len += VRRP_AUTH_LEN + ((!LIST_ISEMPTY(vrrp->vip)) ? LIST_SIZE(vrrp->vip) * sizeof (uint32_t) : 0); + return len; +} + +/* VRRP header pointer from buffer */ +vrrp_pkt * +vrrp_get_header(sa_family_t family, char *buf, int *proto, uint32_t *saddr) +{ + struct iphdr *iph; + vrrp_pkt *hd = NULL; + + if (family == AF_INET) { + iph = (struct iphdr *) buf; + *saddr = iph->saddr; + + /* Fill the VRRP header */ + switch (iph->protocol) { + case IPPROTO_IPSEC_AH: + *proto = IPPROTO_IPSEC_AH; + hd = (vrrp_pkt *) ((char *) iph + (iph->ihl << 2) + + vrrp_ipsecah_len()); + break; + case IPPROTO_VRRP: + *proto = IPPROTO_VRRP; + hd = (vrrp_pkt *) ((char *) iph + (iph->ihl << 2)); + break; + } + } else if (family == AF_INET6) { + *proto = IPPROTO_VRRP; + *saddr = 0; + hd = (vrrp_pkt *) buf; + } + + return hd; +} + +/* + * IPSEC AH incoming packet check. + * return 0 for a valid pkt, != 0 otherwise. + */ +static int +vrrp_in_chk_ipsecah(vrrp_rt * vrrp, char *buffer) +{ + struct iphdr *ip = (struct iphdr *) (buffer); + ipsec_ah *ah = (ipsec_ah *) ((char *) ip + (ip->ihl << 2)); + unsigned char *digest; + uint32_t backup_auth_data[3]; + + /* first verify that the SPI value is equal to src IP */ + if (ah->spi != ip->saddr) { + log_message(LOG_INFO, + "IPSEC AH : invalid IPSEC SPI value. %d and expect %d", + ip->saddr, ah->spi); + return 1; + } + + /* + * then proceed with the sequence number to prevent against replay attack. + * For inbound processing, we increment seq_number counter to audit + * sender counter. + */ + vrrp->ipsecah_counter->seq_number++; + if (ntohl(ah->seq_number) >= vrrp->ipsecah_counter->seq_number || vrrp->sync) { + vrrp->ipsecah_counter->seq_number = ntohl(ah->seq_number); + } else { + log_message(LOG_INFO, + "VRRP_Instance(%s) IPSEC-AH : sequence number %d" + " already proceeded. Packet dropped. Local(%d)", vrrp->iname + , ntohl(ah->seq_number), vrrp->ipsecah_counter->seq_number); + return 1; + } + + /* + * then compute a ICV to compare with the one present in AH pkt. + * alloc a temp memory space to stock the ip mutable fields + */ + digest = (unsigned char *) MALLOC(16 * sizeof (unsigned char *)); + + /* zero the ip mutable fields */ + ip->tos = 0; + ip->frag_off = 0; + ip->check = 0; + memcpy(backup_auth_data, ah->auth_data, sizeof (ah->auth_data)); + memset(ah->auth_data, 0, sizeof (ah->auth_data)); + + /* Compute the ICV */ + hmac_md5((unsigned char *) buffer, + vrrp_iphdr_len(vrrp) + vrrp_ipsecah_len() + vrrp_hd_len(vrrp) + , vrrp->auth_data, sizeof (vrrp->auth_data) + , digest); + + if (memcmp(backup_auth_data, digest, HMAC_MD5_TRUNC) != 0) { + log_message(LOG_INFO, "VRRP_Instance(%s) IPSEC-AH : invalid" + " IPSEC HMAC-MD5 value. Due to fields mutation" + " or bad password !", vrrp->iname); + return 1; + } + + FREE(digest); + return 0; +} + +/* check if ipaddr is present in VIP buffer */ +static int +vrrp_in_chk_vips(vrrp_rt * vrrp, ip_address *ipaddress, unsigned char *buffer) +{ + int i; + uint32_t ipbuf; + + /* Just skip IPv6 address, when we are using a mixed v4/v6 vips + * set inside se same VRRP instance. + */ + if (IP_IS6(ipaddress)) + return 1; + + for (i = 0; i < LIST_SIZE(vrrp->vip); i++) { + bcopy(buffer + i * sizeof (uint32_t), &ipbuf, + sizeof (uint32_t)); + if (ipaddress->u.sin.sin_addr.s_addr == ipbuf) + return 1; + } + + return 0; +} + +/* + * VRRP incoming packet check. + * return 0 if the pkt is valid, != 0 otherwise. + */ +static int +vrrp_in_chk(vrrp_rt * vrrp, char *buffer) +{ + struct iphdr *ip; + int ihl, vrrp_pkt_len; + ipsec_ah *ah; + vrrp_pkt *hd; + unsigned char *vips; + ip_address *ipaddress; + element e; + + /* IPv4 related */ + if (vrrp->family == AF_INET) { + + ip = (struct iphdr *) (buffer); + ihl = ip->ihl << 2; + + if (vrrp->auth_type == VRRP_AUTH_AH) { + ah = (ipsec_ah *) (buffer + ihl); + hd = (vrrp_pkt *) (ah + vrrp_ipsecah_len()); + } else { + hd = (vrrp_pkt *) (buffer + ihl); + } + + /* pointer to vrrp vips pkt zone */ + vips = (unsigned char *) ((char *) hd + sizeof (vrrp_pkt)); + + /* MUST verify that the IP TTL is 255 */ + if (ip->ttl != VRRP_IP_TTL) { + log_message(LOG_INFO, "invalid ttl. %d and expect %d", ip->ttl, + VRRP_IP_TTL); + return VRRP_PACKET_KO; + } + + /* + * MUST verify that the received packet length is greater than or + * equal to the VRRP header + */ + if ((ntohs(ip->tot_len) - ihl) <= sizeof (vrrp_pkt)) { + log_message(LOG_INFO, + "ip payload too short. %d and expect at least %d", + ntohs(ip->tot_len) - ihl, sizeof (vrrp_pkt)); + return VRRP_PACKET_KO; + } + + if (!LIST_ISEMPTY(vrrp->vip)) { + /* + * MAY verify that the IP address(es) associated with the + * VRID are valid + */ + if (hd->naddr != LIST_SIZE(vrrp->vip)) { + log_message(LOG_INFO, + "receive an invalid ip number count associated with VRID!"); + return VRRP_PACKET_KO; + } + + for (e = LIST_HEAD(vrrp->vip); e; ELEMENT_NEXT(e)) { + ipaddress = ELEMENT_DATA(e); + if (!vrrp_in_chk_vips(vrrp, ipaddress, vips)) { + log_message(LOG_INFO, "ip address associated with VRID" + " not present in received packet : %s", + inet_ntop2(ipaddress->u.sin.sin_addr.s_addr)); + log_message(LOG_INFO, + "one or more VIP associated with" + " VRID mismatch actual MASTER advert"); + return VRRP_PACKET_KO; + } + } + } + + /* check the authentication if it is a passwd */ + if (hd->auth_type == VRRP_AUTH_PASS) { + char *pw = (char *) ip + ntohs(ip->tot_len) + - sizeof (vrrp->auth_data); + if (memcmp(pw, vrrp->auth_data, sizeof(vrrp->auth_data)) != 0) { + log_message(LOG_INFO, "receive an invalid passwd!"); + return VRRP_PACKET_KO; + } + } + + /* check the authenicaion if it is ipsec ah */ + if (hd->auth_type == VRRP_AUTH_AH) + return (vrrp_in_chk_ipsecah(vrrp, buffer)); + + /* Set expected vrrp packet lenght */ + vrrp_pkt_len = sizeof(vrrp_pkt) + VRRP_AUTH_LEN + hd->naddr * sizeof(uint32_t); + + } else if (vrrp->family == AF_INET6) { /* IPv6 related */ + + hd = (vrrp_pkt *) buffer; + vrrp_pkt_len = sizeof(vrrp_pkt); + + } else { + return VRRP_PACKET_KO; + } + + /* MUST verify the VRRP version */ + if ((hd->vers_type >> 4) != VRRP_VERSION) { + log_message(LOG_INFO, "invalid version. %d and expect %d", + (hd->vers_type >> 4), VRRP_VERSION); + return VRRP_PACKET_KO; + } + + /* MUST verify the VRRP checksum */ + if (in_csum((u_short *) hd, vrrp_pkt_len, 0)) { + log_message(LOG_INFO, "Invalid vrrp checksum"); + return VRRP_PACKET_KO; + } + + /* + * MUST perform authentication specified by Auth Type + * check the authentication type + */ + if (vrrp->auth_type != hd->auth_type) { + log_message(LOG_INFO, "receive a %d auth, expecting %d!", + hd->auth_type, vrrp->auth_type); + return VRRP_PACKET_KO; + } + + /* MUST verify that the VRID is valid on the receiving interface */ + if (vrrp->vrid != hd->vrid) { + log_message(LOG_INFO, + "received VRID mismatch. Received %d, Expected %d", + hd->vrid, vrrp->vrid); + return VRRP_PACKET_DROP; + } + + if (LIST_ISEMPTY(vrrp->vip) && hd->naddr > 0) { + log_message(LOG_INFO, "receive an invalid ip number count associated with VRID!"); + return VRRP_PACKET_KO; + } + + /* + * MUST verify that the Adver Interval in the packet is the same as + * the locally configured for this virtual router + */ + if (vrrp->adver_int / TIMER_HZ != hd->adver_int) { + log_message(LOG_INFO, "advertissement interval mismatch mine=%d rcved=%d", + vrrp->adver_int, hd->adver_int); + /* to prevent concurent VRID running => multiple master in 1 VRID */ + return VRRP_PACKET_DROP; + } + + return VRRP_PACKET_OK; +} + +/* build IP header */ +static void +vrrp_build_ip(vrrp_rt * vrrp, char *buffer, int buflen) +{ + struct iphdr *ip = (struct iphdr *) (buffer); + + ip->ihl = 5; + ip->version = 4; + /* set tos to internet network control */ + ip->tos = 0xc0; + ip->tot_len = ip->ihl * 4 + vrrp_hd_len(vrrp); + ip->tot_len = htons(ip->tot_len); + ip->id = htons(++vrrp->ip_id); + /* kernel will fill in ID if left to 0, so we overflow to 1 */ + if (vrrp->ip_id == 65535) + vrrp->ip_id = 1; + ip->frag_off = 0; + ip->ttl = VRRP_IP_TTL; + + /* fill protocol type --rfc2402.2 */ + ip->protocol = + (vrrp->auth_type == VRRP_AUTH_AH) ? IPPROTO_IPSEC_AH : IPPROTO_VRRP; + ip->saddr = vrrp->unicast_bind ? vrrp->unicast_bind : VRRP_PKT_SADDR(vrrp); + ip->daddr = vrrp->unicast_peer ? vrrp->unicast_peer : htonl(INADDR_VRRP_GROUP); + + /* checksum must be done last */ + ip->check = in_csum((u_short *) ip, ip->ihl * 4, 0); +} + +/* build IPSEC AH header */ +static void +vrrp_build_ipsecah(vrrp_rt * vrrp, char *buffer, int buflen) +{ + ICV_mutable_fields *ip_mutable_fields; + unsigned char *digest; + struct iphdr *ip = (struct iphdr *) (buffer); + ipsec_ah *ah = (ipsec_ah *) (buffer + sizeof (struct iphdr)); + + /* alloc a temp memory space to stock the ip mutable fields */ + ip_mutable_fields = (ICV_mutable_fields *) MALLOC(sizeof (ICV_mutable_fields)); + + /* fill in next header filed --rfc2402.2.1 */ + ah->next_header = IPPROTO_VRRP; + + /* update IP header total length value */ + ip->tot_len = ip->ihl * 4 + vrrp_ipsecah_len() + vrrp_hd_len(vrrp); + ip->tot_len = htons(ip->tot_len); + + /* update ip checksum */ + ip->check = 0; + ip->check = in_csum((u_short *) ip, ip->ihl * 4, 0); + + /* backup the ip mutable fields */ + ip_mutable_fields->tos = ip->tos; + ip_mutable_fields->frag_off = ip->frag_off; + ip_mutable_fields->check = ip->check; + + /* zero the ip mutable fields */ + ip->tos = 0; + ip->frag_off = 0; + ip->check = 0; + + /* fill in the Payload len field */ + ah->payload_len = IPSEC_AH_PLEN; + + /* The SPI value is filled with the ip header source address. + SPI uniquely identify the Security Association (SA). This value + is chosen by the recipient itself when setting up the SA. In a + multicast environment, this becomes unfeasible. + + If left to the sender, the choice of the SPI value should be done + so by the sender that it cannot possibly conflict with SPI values + chosen by other entities sending IPSEC traffic to any of the receivers. + To overpass this problem, the rule I have chosen to implement here is + that the SPI value chosen by the sender is based on unique information + such as its IP address. + -- INTERNET draft : <draft-paridaens-xcast-sec-framework-01.txt> + */ + ah->spi = ip->saddr; + + /* Processing sequence number. + Cycled assumed if 0xFFFFFFFD reached. So the MASTER state is free for another srv. + Here can result a flapping MASTER state owner when max seq_number value reached. + => Much work needed here. + In the current implementation if counter has cycled, we stop sending adverts and + become BACKUP. If all the master are down we reset the counter for becoming MASTER. + */ + if (vrrp->ipsecah_counter->seq_number > 0xFFFFFFFD) { + vrrp->ipsecah_counter->cycle = 1; + } else { + vrrp->ipsecah_counter->seq_number++; + } + + ah->seq_number = htonl(vrrp->ipsecah_counter->seq_number); + + /* Compute the ICV & trunc the digest to 96bits + => No padding needed. + -- rfc2402.3.3.3.1.1.1 & rfc2401.5 + */ + digest = (unsigned char *) MALLOC(16 * sizeof (unsigned char *)); + hmac_md5((unsigned char *) buffer, buflen, vrrp->auth_data, sizeof (vrrp->auth_data) + , digest); + memcpy(ah->auth_data, digest, HMAC_MD5_TRUNC); + + /* Restore the ip mutable fields */ + ip->tos = ip_mutable_fields->tos; + ip->frag_off = ip_mutable_fields->frag_off; + ip->check = ip_mutable_fields->check; + + FREE(ip_mutable_fields); + FREE(digest); +} + +/* build VRRP header */ +static int +vrrp_build_vrrp(vrrp_rt * vrrp, int prio, char *buffer) +{ + int i = 0; + vrrp_pkt *hd = (vrrp_pkt *) buffer; + uint32_t *iparr; + element e; + ip_address *ip_addr; + + /* Family independant */ + hd->vers_type = (VRRP_VERSION << 4) | VRRP_PKT_ADVERT; + hd->vrid = vrrp->vrid; + hd->priority = prio; + hd->naddr = (!LIST_ISEMPTY(vrrp->vip)) ? LIST_SIZE(vrrp->vip) : 0; + hd->auth_type = vrrp->auth_type; + hd->adver_int = vrrp->adver_int / TIMER_HZ; + + /* Family specific */ + if (vrrp->family == AF_INET) { + /* copy the ip addresses */ + iparr = (uint32_t *) ((char *) hd + sizeof (*hd)); + if (!LIST_ISEMPTY(vrrp->vip)) { + for (e = LIST_HEAD(vrrp->vip); e; ELEMENT_NEXT(e)) { + ip_addr = ELEMENT_DATA(e); + if (IP_IS6(ip_addr)) + continue; + else + iparr[i++] = ip_addr->u.sin.sin_addr.s_addr; + } + } + + /* copy the passwd if the authentication is VRRP_AH_PASS */ + if (vrrp->auth_type == VRRP_AUTH_PASS) { + int vip_count = (!LIST_ISEMPTY(vrrp->vip)) ? LIST_SIZE(vrrp->vip) : 0; + char *pw = (char *) hd + sizeof (*hd) + vip_count * 4; + memcpy(pw, vrrp->auth_data, sizeof (vrrp->auth_data)); + } + } + + /* finaly compute vrrp checksum */ + hd->chksum = in_csum((u_short *) hd, vrrp_hd_len(vrrp), 0); + + return 0; +} + +/* build VRRP packet */ +static void +vrrp_build_pkt(vrrp_rt * vrrp, int prio) +{ + char *bufptr; + int len; + + /* save reference values */ + bufptr = VRRP_SEND_BUFFER(vrrp); + len = VRRP_SEND_BUFFER_SIZE(vrrp); + + if (vrrp->family == AF_INET) { + /* build the ip header */ + vrrp_build_ip(vrrp, VRRP_SEND_BUFFER(vrrp), VRRP_SEND_BUFFER_SIZE(vrrp)); + + /* build the vrrp header */ + vrrp->send_buffer += vrrp_iphdr_len(vrrp); + + if (vrrp->auth_type == VRRP_AUTH_AH) + vrrp->send_buffer += vrrp_ipsecah_len(); + vrrp->send_buffer_size -= vrrp_iphdr_len(vrrp); + + if (vrrp->auth_type == VRRP_AUTH_AH) + vrrp->send_buffer_size -= vrrp_ipsecah_len(); + vrrp_build_vrrp(vrrp, prio, vrrp->send_buffer); + + /* build the IPSEC AH header */ + if (vrrp->auth_type == VRRP_AUTH_AH) { + vrrp->send_buffer_size += vrrp_iphdr_len(vrrp) + vrrp_ipsecah_len(); + vrrp_build_ipsecah(vrrp, bufptr, VRRP_SEND_BUFFER_SIZE(vrrp)); + } + } else if (vrrp->family == AF_INET6) { + vrrp_build_vrrp(vrrp, prio, VRRP_SEND_BUFFER(vrrp)); + } + + /* restore reference values */ + vrrp->send_buffer = bufptr; + vrrp->send_buffer_size = len; +} + +/* send VRRP packet */ +static int +vrrp_send_pkt(vrrp_rt * vrrp) +{ + struct sockaddr_in6 dst6; + struct sockaddr_in dst4; + struct msghdr msg; + struct iovec iov; + + /* Build the message data */ + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + iov.iov_base = VRRP_SEND_BUFFER(vrrp); + iov.iov_len = VRRP_SEND_BUFFER_SIZE(vrrp); + + /* Sending path */ + if (vrrp->family == AF_INET) { + memset(&dst4, 0, sizeof(dst4)); + dst4.sin_family = AF_INET; + dst4.sin_addr.s_addr = vrrp->unicast_peer ? vrrp->unicast_peer : htonl(INADDR_VRRP_GROUP); + + msg.msg_name = &dst4; + msg.msg_namelen = sizeof(dst4); + } else if (vrrp->family == AF_INET6) { + memset(&dst6, 0, sizeof(dst6)); + dst6.sin6_family = AF_INET6; + dst6.sin6_port = htons(IPPROTO_VRRP); + dst6.sin6_addr.s6_addr16[0] = htons(0xff02); + dst6.sin6_addr.s6_addr16[7] = htons(0x12); + + msg.msg_name = &dst6; + msg.msg_namelen = sizeof(dst6); + } + + /* Send the packet */ + return sendmsg(vrrp->fd_out, &msg, MSG_DONTROUTE); +} + +/* Allocate the sending buffer */ +static void +vrrp_alloc_send_buffer(vrrp_rt * vrrp) +{ + vrrp->send_buffer_size = vrrp_hd_len(vrrp); + + if (vrrp->family == AF_INET) { + vrrp->send_buffer_size = vrrp_iphdr_len(vrrp) + vrrp_hd_len(vrrp); + if (vrrp->auth_type == VRRP_AUTH_AH) + vrrp->send_buffer_size += vrrp_ipsecah_len(); + } + + vrrp->send_buffer = MALLOC(VRRP_SEND_BUFFER_SIZE(vrrp)); +} + +/* send VRRP advertissement */ +int +vrrp_send_adv(vrrp_rt * vrrp, int prio) +{ + /* alloc send buffer */ + if (!vrrp->send_buffer) + vrrp_alloc_send_buffer(vrrp); + else + memset(vrrp->send_buffer, 0, VRRP_SEND_BUFFER_SIZE(vrrp)); + + /* build the packet */ + vrrp_build_pkt(vrrp, prio); + + /* send it */ + return vrrp_send_pkt(vrrp); +} + +/* Received packet processing */ +int +vrrp_check_packet(vrrp_rt * vrrp, char *buf, int buflen) +{ + int ret; + + if (buflen > 0) { + ret = vrrp_in_chk(vrrp, buf); + + if (ret == VRRP_PACKET_DROP) { + log_message(LOG_INFO, "Sync instance needed on %s !!!", + IF_NAME(vrrp->ifp)); + } + + if (ret == VRRP_PACKET_KO) + log_message(LOG_INFO, "bogus VRRP packet received on %s !!!", + IF_NAME(vrrp->ifp)); + return ret; + } + + return VRRP_PACKET_NULL; +} + +/* Gratuitous ARP on each VIP */ +static void +vrrp_send_update(vrrp_rt * vrrp, ip_address * ipaddress, int idx) +{ + char *msg; + char addr_str[41]; + + if (!IP_IS6(ipaddress)) { + msg = "gratuitous ARPs"; + inet_ntop(AF_INET, &ipaddress->u.sin.sin_addr, addr_str, 41); + send_gratuitous_arp(ipaddress); + } else { + msg = "Unsolicited Neighbour Adverts"; + inet_ntop(AF_INET6, &ipaddress->u.sin6_addr, addr_str, 41); + ndisc_send_unsolicited_na(ipaddress); + } + + if (0 == idx && debug & 32) { + log_message(LOG_INFO, "VRRP_Instance(%s) Sending %s on %s for %s", + vrrp->iname, msg, IF_NAME(ipaddress->ifp), addr_str); + } +} + +void +vrrp_send_link_update(vrrp_rt * vrrp) +{ + int j; + ip_address *ipaddress; + element e; + + /* Only send gratuitous ARP if VIP are set */ + if (!VRRP_VIP_ISSET(vrrp)) + return; + + /* send gratuitous arp for each virtual ip */ + for (j = 0; j < 5; j++) { + if (!LIST_ISEMPTY(vrrp->vip)) { + for (e = LIST_HEAD(vrrp->vip); e; ELEMENT_NEXT(e)) { + ipaddress = ELEMENT_DATA(e); + vrrp_send_update(vrrp, ipaddress, j); + } + } + + if (!LIST_ISEMPTY(vrrp->evip)) { + for (e = LIST_HEAD(vrrp->evip); e; ELEMENT_NEXT(e)) { + ipaddress = ELEMENT_DATA(e); + vrrp_send_update(vrrp, ipaddress, j); + } + } + } +} + +/* becoming master */ +void +vrrp_state_become_master(vrrp_rt * vrrp) +{ + /* add the ip addresses */ + if (!LIST_ISEMPTY(vrrp->vip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_VIP_TYPE); + if (!LIST_ISEMPTY(vrrp->evip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_EVIP_TYPE); + vrrp->vipset = 1; + + /* add virtual routes */ + if (!LIST_ISEMPTY(vrrp->vroutes)) + vrrp_handle_iproutes(vrrp, IPROUTE_ADD); + + /* remotes neighbour update */ + vrrp_send_link_update(vrrp); + + /* Check if notify is needed */ + notify_instance_exec(vrrp, VRRP_STATE_MAST); + +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + +#ifdef _HAVE_IPVS_SYNCD_ + /* Check if sync daemon handling is needed */ + if (vrrp->lvs_syncd_if) + ipvs_syncd_master(vrrp->lvs_syncd_if, vrrp->vrid); +#endif +} + +void +vrrp_state_goto_master(vrrp_rt * vrrp) +{ + /* + * Send an advertisement. To force a new master + * election. + */ + if (vrrp->sync && !vrrp_sync_goto_master(vrrp)) + return; + vrrp_send_adv(vrrp, vrrp->effective_priority); + + vrrp->state = VRRP_STATE_MAST; + log_message(LOG_INFO, "VRRP_Instance(%s) Transition to MASTER STATE", + vrrp->iname); +} + +/* leaving master state */ +void +vrrp_restore_interface(vrrp_rt * vrrp, int advF) +{ + /* if we stop vrrp, warn the other routers to speed up the recovery */ + if (advF) { + syslog(LOG_INFO, "VRRP_Instance(%s) sending 0 priority", + vrrp->iname); + vrrp_send_adv(vrrp, VRRP_PRIO_STOP); + } + + /* remove virtual routes */ + if (!LIST_ISEMPTY(vrrp->vroutes)) + vrrp_handle_iproutes(vrrp, IPROUTE_DEL); + + /* + * Remove the ip addresses. + * + * If started with "--dont-release-vrrp" (debug & 8) then try to remove + * addresses even if we didn't add them during this run. + */ + if (debug & 8 || VRRP_VIP_ISSET(vrrp)) { + if (!LIST_ISEMPTY(vrrp->vip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_DEL, + VRRP_VIP_TYPE); + if (!LIST_ISEMPTY(vrrp->evip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_DEL, + VRRP_EVIP_TYPE); + vrrp->vipset = 0; + } + +} + +void +vrrp_state_leave_master(vrrp_rt * vrrp) +{ + if (VRRP_VIP_ISSET(vrrp)) { +#ifdef _HAVE_IPVS_SYNCD_ + /* Check if sync daemon handling is needed */ + if (vrrp->lvs_syncd_if) + ipvs_syncd_backup(vrrp->lvs_syncd_if, vrrp->vrid); +#endif + } + + /* set the new vrrp state */ + switch (vrrp->wantstate) { + case VRRP_STATE_BACK: + log_message(LOG_INFO, "VRRP_Instance(%s) Entering BACKUP STATE", vrrp->iname); + vrrp_restore_interface(vrrp, 0); + vrrp->state = vrrp->wantstate; + notify_instance_exec(vrrp, VRRP_STATE_BACK); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + break; + case VRRP_STATE_GOTO_FAULT: + log_message(LOG_INFO, "VRRP_Instance(%s) Entering FAULT STATE", vrrp->iname); + vrrp_restore_interface(vrrp, 0); + vrrp->state = VRRP_STATE_FAULT; + notify_instance_exec(vrrp, VRRP_STATE_FAULT); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + break; + } + + /* Set the down timer */ + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); +} + +/* BACKUP state processing */ +void +vrrp_state_backup(vrrp_rt * vrrp, char *buf, int buflen) +{ + vrrp_pkt *hd; + uint32_t saddr; + int ret = 0, proto; + + /* Process the incoming packet */ + hd = vrrp_get_header(vrrp->family, buf, &proto, &saddr); + ret = vrrp_check_packet(vrrp, buf, buflen); + + if (ret == VRRP_PACKET_KO || ret == VRRP_PACKET_NULL) { + log_message(LOG_INFO, "VRRP_Instance(%s) ignoring received advertisment..." + , vrrp->iname); + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + } else if (hd->priority == 0) { + vrrp->ms_down_timer = VRRP_TIMER_SKEW(vrrp); + } else if (vrrp->nopreempt || hd->priority >= vrrp->effective_priority || + timer_cmp(vrrp->preempt_time, timer_now()) > 0) { + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + } else if (hd->priority < vrrp->effective_priority) { + log_message(LOG_INFO, "VRRP_Instance(%s) forcing a new MASTER election" + , vrrp->iname); + vrrp->wantstate = VRRP_STATE_GOTO_MASTER; + vrrp_send_adv(vrrp, vrrp->effective_priority); + } +} + +/* MASTER state processing */ +int +vrrp_state_master_tx(vrrp_rt * vrrp, const int prio) +{ + int ret = 0; + + if (!VRRP_VIP_ISSET(vrrp)) { + log_message(LOG_INFO, "VRRP_Instance(%s) Entering MASTER STATE" + , vrrp->iname); + vrrp_state_become_master(vrrp); + ret = 1; + } + + vrrp_send_adv(vrrp, + (prio == VRRP_PRIO_OWNER) ? VRRP_PRIO_OWNER : + vrrp->effective_priority); + return ret; +} + +int +vrrp_state_master_rx(vrrp_rt * vrrp, char *buf, int buflen) +{ + vrrp_pkt *hd = NULL; + int ret = 0, proto = 0; + uint32_t saddr = 0; + ipsec_ah *ah; + + /* return on link failure */ + if (vrrp->wantstate == VRRP_STATE_GOTO_FAULT) { + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + vrrp->state = VRRP_STATE_FAULT; + notify_instance_exec(vrrp, VRRP_STATE_FAULT); + return 1; + } + + /* Process the incoming packet */ + hd = vrrp_get_header(vrrp->family, buf, &proto, &saddr); + ret = vrrp_check_packet(vrrp, buf, buflen); + + if (ret == VRRP_PACKET_KO || + ret == VRRP_PACKET_NULL || ret == VRRP_PACKET_DROP) { + log_message(LOG_INFO, + "VRRP_Instance(%s) Dropping received VRRP packet...", + vrrp->iname); + return 0; + } else if (hd->priority < vrrp->effective_priority) { + /* We receive a lower prio adv we just refresh remote ARP cache */ + log_message(LOG_INFO, "VRRP_Instance(%s) Received lower prio advert" + ", forcing new election", vrrp->iname); + if (proto == IPPROTO_IPSEC_AH) { + ah = (ipsec_ah *) (buf + sizeof(struct iphdr)); + log_message(LOG_INFO, "VRRP_Instance(%s) IPSEC-AH : Syncing seq_num" + " - Increment seq" + , vrrp->iname); + vrrp->ipsecah_counter->seq_number = ntohl(ah->seq_number) + 1; + vrrp->ipsecah_counter->cycle = 0; + } + vrrp_send_adv(vrrp, vrrp->effective_priority); + vrrp_send_link_update(vrrp); + return 0; + } else if (hd->priority == 0) { + vrrp_send_adv(vrrp, vrrp->effective_priority); + return 0; + } else if (vrrp->family == AF_INET) { + if (hd->priority > vrrp->effective_priority || + (hd->priority == vrrp->effective_priority && + ntohl(saddr) > VRRP_PKT_SADDR(vrrp))) { + log_message(LOG_INFO, "VRRP_Instance(%s) Received higher prio advert" + , vrrp->iname); + if (proto == IPPROTO_IPSEC_AH) { + ah = (ipsec_ah *) (buf + sizeof(struct iphdr)); + log_message(LOG_INFO, "VRRP_Instance(%s) IPSEC-AH : Syncing seq_num" + " - Decrement seq" + , vrrp->iname); + vrrp->ipsecah_counter->seq_number = ntohl(ah->seq_number) - 1; + vrrp->ipsecah_counter->cycle = 0; + } + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + vrrp->wantstate = VRRP_STATE_BACK; + vrrp->state = VRRP_STATE_BACK; + return 1; + } + } else if (vrrp->family == AF_INET6) { + /* FIXME: compare v6 saddr to link local when prio are equal !!! */ + if (hd->priority > vrrp->effective_priority) { + log_message(LOG_INFO, "VRRP_Instance(%s) Received higher prio advert" + , vrrp->iname); + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + vrrp->wantstate = VRRP_STATE_BACK; + vrrp->state = VRRP_STATE_BACK; + return 1; + } + } + + return 0; +} + +int +vrrp_state_fault_rx(vrrp_rt * vrrp, char *buf, int buflen) +{ + vrrp_pkt *hd; + uint32_t saddr; + int ret = 0, proto; + + /* Process the incoming packet */ + hd = vrrp_get_header(vrrp->family, buf, &proto, &saddr); + ret = vrrp_check_packet(vrrp, buf, buflen); + + if (ret == VRRP_PACKET_KO || ret == VRRP_PACKET_NULL || ret == VRRP_PACKET_DROP) { + log_message(LOG_INFO, "VRRP_Instance(%s) Dropping received VRRP packet..." + , vrrp->iname); + return 0; + } else if (vrrp->effective_priority > hd->priority || + hd->priority == VRRP_PRIO_OWNER) { + if (!vrrp->nopreempt) + return 1; + } + + return 0; +} + +/* check for minimum configuration requirements */ +static int +chk_min_cfg(vrrp_rt * vrrp) +{ + if (vrrp->vrid == 0) { + log_message(LOG_INFO, "VRRP_Instance(%s) the virtual id must be set!", + vrrp->iname); + return 0; + } + if (!vrrp->ifp) { + log_message(LOG_INFO, "VRRP_Instance(%s) Unknown interface !", + vrrp->iname); + return 0; + } + + return 1; +} + +/* open a VRRP sending socket */ +int +open_vrrp_send_socket(sa_family_t family, int proto, int idx, int unicast) +{ + interface *ifp; + int fd = -1; + + /* Retreive interface */ + ifp = if_get_by_ifindex(idx); + + /* Create and init socket descriptor */ + fd = socket(family, SOCK_RAW, proto); + if (fd < 0) { + log_message(LOG_INFO, "cant open raw socket. errno=%d", errno); + return -1; + } + + if (family == AF_INET) { + /* Set v4 related */ + if_setsockopt_hdrincl(&fd); + if_setsockopt_bindtodevice(&fd, ifp); + if_setsockopt_mcast_loop(family, &fd); + if_setsockopt_priority(&fd); + if (fd < 0) + return -1; + } else if (family == AF_INET6) { + /* Set v6 related */ + if_setsockopt_mcast_hops(family, &fd); + if_setsockopt_mcast_if(family, &fd, ifp); + if_setsockopt_mcast_loop(family, &fd); + if_setsockopt_priority(&fd); + if (fd < 0) + return -1; + } else { + log_message(LOG_INFO, "cant open raw socket. unknow family=%d" + , family); + close(fd); + return -1; + } + + if (!unicast) + if_setsockopt_mcast_loop(family, &fd); + if (fd < 0) + return -1; + + return fd; +} + +/* open a VRRP socket and join the multicast group. */ +int +open_vrrp_socket(sa_family_t family, int proto, int idx, int unicast) +{ + interface *ifp; + int fd = -1; + + /* Retreive interface */ + ifp = if_get_by_ifindex(idx); + + /* open the socket */ + fd = socket(family, SOCK_RAW, proto); + if (fd < 0) { + int err = errno; + log_message(LOG_INFO, "cant open raw socket. errno=%d", err); + return -1; + } + + /* Join the VRRP MCAST group */ + if (!unicast) + if_join_vrrp_group(family, &fd, ifp, proto); + if (fd < 0) + return -1; + + if (family == AF_INET) { + /* Bind inbound stream */ + if_setsockopt_bindtodevice(&fd, ifp); + } + + return fd; +} + +void +close_vrrp_socket(vrrp_rt * vrrp) +{ + if_leave_vrrp_group(vrrp->family, vrrp->fd_in, vrrp->ifp, !vrrp->unicast_peer); + close(vrrp->fd_out); +} + +int +new_vrrp_socket(vrrp_rt * vrrp) +{ + int old_fd = vrrp->fd_in; + int proto; + + /* close the desc & open a new one */ + close_vrrp_socket(vrrp); + remove_vrrp_fd_bucket(vrrp); + proto = (vrrp->auth_type == VRRP_AUTH_AH) ? IPPROTO_IPSEC_AH : IPPROTO_VRRP; + vrrp->fd_in = open_vrrp_socket(vrrp->family, proto, IF_INDEX(vrrp->ifp), !vrrp->unicast_peer); + vrrp->fd_out = open_vrrp_send_socket(vrrp->family, proto, IF_INDEX(vrrp->ifp), !vrrp->unicast_peer); + alloc_vrrp_fd_bucket(vrrp); + + /* Sync the other desc */ + set_vrrp_fd_bucket(old_fd, vrrp); + + return vrrp->fd_in; +} + +/* handle terminate state */ +void +shutdown_vrrp_instances(void) +{ + list l = vrrp_data->vrrp; + element e; + vrrp_rt *vrrp; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + + /* Remove VIPs/VROUTEs */ + if (vrrp->state == VRRP_STATE_MAST) + vrrp_restore_interface(vrrp, 1); + + /* Remove VMAC */ + if (vrrp->vmac) + netlink_link_del_vmac(vrrp); + + /* Run stop script */ + if (vrrp->script_stop) + notify_exec(vrrp->script_stop); + +#ifdef _HAVE_IPVS_SYNCD_ + /* + * Stop stalled syncd. IPVS syncd state is the + * same as VRRP instance one. We need here to + * stop stalled syncd thread according to last + * VRRP instance state. + */ + if (vrrp->lvs_syncd_if) + ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, + (vrrp->state == VRRP_STATE_MAST) ? IPVS_MASTER: + IPVS_BACKUP, + vrrp->vrid); +#endif + } +} + +/* complete vrrp structure */ +static int +vrrp_complete_instance(vrrp_rt * vrrp) +{ + vrrp->state = VRRP_STATE_INIT; + if (!vrrp->adver_int) + vrrp->adver_int = VRRP_ADVER_DFL * TIMER_HZ; + if (!vrrp->effective_priority) + vrrp->effective_priority = VRRP_PRIO_DFL; + + return (chk_min_cfg(vrrp)); +} + +int +vrrp_complete_init(void) +{ + list l; + element e; + vrrp_rt *vrrp; + vrrp_sgroup *sgroup; + + /* Complete VRRP instance initialization */ + l = vrrp_data->vrrp; + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + if (!vrrp_complete_instance(vrrp)) + return 0; + } + + /* Build synchronization group index */ + l = vrrp_data->vrrp_sync_group; + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + sgroup = ELEMENT_DATA(e); + vrrp_sync_set_group(sgroup); + } + + return 1; +} + +/* Try to find a VRRP instance */ +static vrrp_rt * +vrrp_exist(vrrp_rt * old_vrrp) +{ + element e; + list l = vrrp_data->vrrp; + vrrp_rt *vrrp; + + if (LIST_ISEMPTY(l)) + return NULL; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + if (!strcmp(vrrp->iname, old_vrrp->iname)) + return vrrp; + } + + return NULL; +} + +/* Clear VIP|EVIP not present into the new data */ +static void +clear_diff_vrrp_vip(vrrp_rt * old_vrrp, int type) +{ + vrrp_rt *vrrp = vrrp_exist(old_vrrp); + list l = (type == VRRP_VIP_TYPE) ? old_vrrp->vip : old_vrrp->evip; + list n = (type == VRRP_VIP_TYPE) ? vrrp->vip : vrrp->evip; + clear_diff_address(l, n); +} + +/* Clear virtual routes not present in the new data */ +static void +clear_diff_vrrp_vroutes(vrrp_rt * old_vrrp) +{ + vrrp_rt *vrrp = vrrp_exist(old_vrrp); + clear_diff_routes(old_vrrp->vroutes, vrrp->vroutes); +} + +/* Keep the state from before reload */ +static void +reset_vrrp_state(vrrp_rt * old_vrrp) +{ + /* Keep VRRP state, ipsec AH seq_number */ + vrrp_rt *vrrp = vrrp_exist(old_vrrp); + vrrp->state = old_vrrp->state; + vrrp->init_state = old_vrrp->state; + vrrp->wantstate = old_vrrp->state; + if (!old_vrrp->sync) + vrrp->effective_priority = old_vrrp->effective_priority; + memcpy(vrrp->ipsecah_counter, old_vrrp->ipsecah_counter, sizeof(seq_counter)); + +#ifdef _HAVE_IPVS_SYNCD_ + if (old_vrrp->lvs_syncd_if) + ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, + (old_vrrp->state == VRRP_STATE_MAST) ? IPVS_MASTER: + IPVS_BACKUP, + old_vrrp->vrid); + if (vrrp->lvs_syncd_if) + ipvs_syncd_cmd(IPVS_STARTDAEMON, NULL, + (vrrp->state == VRRP_STATE_MAST) ? IPVS_MASTER: + IPVS_BACKUP, + vrrp->vrid); +#endif + + /* Remember if we had vips up and add new ones if needed */ + vrrp->vipset = old_vrrp->vipset; + if (vrrp->vipset) { + if (!LIST_ISEMPTY(vrrp->vip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_VIP_TYPE); + if (!LIST_ISEMPTY(vrrp->evip)) + vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_EVIP_TYPE); + if (!LIST_ISEMPTY(vrrp->vroutes)) + vrrp_handle_iproutes(vrrp, IPROUTE_ADD); + } +} + +/* Diff when reloading configuration */ +void +clear_diff_vrrp(void) +{ + element e; + list l = old_vrrp_data->vrrp; + vrrp_rt *vrrp; + + if (LIST_ISEMPTY(l)) + return; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + vrrp_rt *new_vrrp; + + /* + * Try to find this vrrp into the new conf data + * reloaded. + */ + new_vrrp = vrrp_exist(vrrp); + if (!new_vrrp) { + vrrp_restore_interface(vrrp, 1); + + /* Remove VMAC if one was created */ + if (vrrp->vmac) + netlink_link_del_vmac(vrrp); + } else { + /* + * If this vrrp instance exist in new + * data, then perform a VIP|EVIP diff. + */ + clear_diff_vrrp_vip(vrrp, VRRP_VIP_TYPE); + clear_diff_vrrp_vip(vrrp, VRRP_EVIP_TYPE); + + /* virtual routes diff */ + clear_diff_vrrp_vroutes(vrrp); + + /* + * Remove VMAC if it existed in old vrrp instance, + * but not the new one. + */ + if (vrrp->vmac && !new_vrrp->vmac) { + netlink_link_del_vmac(vrrp); + } + + /* reset the state */ + reset_vrrp_state(vrrp); + } + } +} + +/* Set script status to a sensible value on reload */ +void +clear_diff_script(void) +{ + element e; + list l = old_vrrp_data->vrrp_script; + vrrp_script *vscript, *nvscript; + + if (LIST_ISEMPTY(l)) + return; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vscript = ELEMENT_DATA(e); + if (vscript->result >= vscript->rise) { + nvscript = find_script_by_name(vscript->sname); + if (nvscript) { + log_message(LOG_INFO, "VRRP_Script(%s) considered successful on reload", + nvscript->sname); + nvscript->result = VRRP_SCRIPT_STATUS_INIT_GOOD; + } + } + } +} diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_data.c keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_data.c --- keepalived-1.2.7/keepalived/vrrp/vrrp_data.c 2012-08-29 00:05:12.000000000 +0200 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_data.c 2013-05-24 09:39:17.631832300 +0200 @@ -142,7 +142,7 @@ free_sock(void *sock_data) interface *ifp; if (sock->fd_in > 0) { ifp = if_get_by_ifindex(sock->ifindex); - if_leave_vrrp_group(sock->family, sock->fd_in, ifp); + if_leave_vrrp_group(sock->family, sock->fd_in, ifp, 0); } if (sock->fd_out > 0) close(sock->fd_out); diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_data.c.orig keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_data.c.orig --- keepalived-1.2.7/keepalived/vrrp/vrrp_data.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_data.c.orig 2012-08-29 00:05:12.000000000 +0200 @@ -0,0 +1,462 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: Dynamic data structure definition. + * + * Author: Alexandre Cassen, <acassen@linux-vs.org> + * + * 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. + * + * 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. + * + * Copyright (C) 2001-2012 Alexandre Cassen, <acassen@gmail.com> + */ + +#include "vrrp_data.h" +#include "vrrp_index.h" +#include "vrrp_sync.h" +#include "vrrp_if.h" +#include "vrrp.h" +#include "memory.h" +#include "utils.h" +#include "logger.h" + +/* global vars */ +vrrp_conf_data *vrrp_data = NULL; +vrrp_conf_data *old_vrrp_data = NULL; +char *vrrp_buffer; + +/* Static addresses facility function */ +void +alloc_saddress(vector_t *strvec) +{ + if (LIST_ISEMPTY(vrrp_data->static_addresses)) + vrrp_data->static_addresses = alloc_list(free_ipaddress, dump_ipaddress); + alloc_ipaddress(vrrp_data->static_addresses, strvec, NULL); +} + +/* Static routes facility function */ +void +alloc_sroute(vector_t *strvec) +{ + if (LIST_ISEMPTY(vrrp_data->static_routes)) + vrrp_data->static_routes = alloc_list(free_iproute, dump_iproute); + alloc_route(vrrp_data->static_routes, strvec); +} + +/* VRRP facility functions */ +static void +free_vgroup(void *data) +{ + vrrp_sgroup *vgroup = data; + + FREE(vgroup->gname); + free_strvec(vgroup->iname); + free_list(vgroup->index_list); + FREE_PTR(vgroup->script_backup); + FREE_PTR(vgroup->script_master); + FREE_PTR(vgroup->script_fault); + FREE_PTR(vgroup->script); + FREE(vgroup); +} +static void +dump_vgroup(void *data) +{ + vrrp_sgroup *vgroup = data; + int i; + char *str; + + log_message(LOG_INFO, " VRRP Sync Group = %s, %s", vgroup->gname, + (vgroup->state == VRRP_STATE_MAST) ? "MASTER" : "BACKUP"); + for (i = 0; i < vector_size(vgroup->iname); i++) { + str = vector_slot(vgroup->iname, i); + log_message(LOG_INFO, " monitor = %s", str); + } + if (vgroup->global_tracking) + log_message(LOG_INFO, " Same tracking for all VRRP instances"); + if (vgroup->script_backup) + log_message(LOG_INFO, " Backup state transition script = %s", + vgroup->script_backup); + if (vgroup->script_master) + log_message(LOG_INFO, " Master state transition script = %s", + vgroup->script_master); + if (vgroup->script_fault) + log_message(LOG_INFO, " Fault state transition script = %s", + vgroup->script_fault); + if (vgroup->script) + log_message(LOG_INFO, " Generic state transition script = '%s'", + vgroup->script); + if (vgroup->smtp_alert) + log_message(LOG_INFO, " Using smtp notification"); +} + +static void +free_vscript(void *data) +{ + vrrp_script *vscript = data; + + FREE(vscript->sname); + FREE_PTR(vscript->script); + FREE(vscript); +} +static void +dump_vscript(void *data) +{ + vrrp_script *vscript = data; + char *str; + + log_message(LOG_INFO, " VRRP Script = %s", vscript->sname); + log_message(LOG_INFO, " Command = %s", vscript->script); + log_message(LOG_INFO, " Interval = %d sec", vscript->interval / TIMER_HZ); + log_message(LOG_INFO, " Timeout = %d sec", vscript->timeout / TIMER_HZ); + log_message(LOG_INFO, " Weight = %d", vscript->weight); + log_message(LOG_INFO, " Rise = %d", vscript->rise); + log_message(LOG_INFO, " Fall = %d", vscript->fall); + + switch (vscript->result) { + case VRRP_SCRIPT_STATUS_INIT: + str = "INIT"; break; + case VRRP_SCRIPT_STATUS_INIT_GOOD: + str = "INIT/GOOD"; break; + case VRRP_SCRIPT_STATUS_DISABLED: + str = "DISABLED"; break; + default: + str = (vscript->result >= vscript->rise) ? "GOOD" : "BAD"; + } + log_message(LOG_INFO, " Status = %s", str); +} + +/* Socket pool functions */ +static void +free_sock(void *sock_data) +{ + sock_t *sock = sock_data; + interface *ifp; + if (sock->fd_in > 0) { + ifp = if_get_by_ifindex(sock->ifindex); + if_leave_vrrp_group(sock->family, sock->fd_in, ifp); + } + if (sock->fd_out > 0) + close(sock->fd_out); + FREE(sock_data); +} + +static void +dump_sock(void *sock_data) +{ + sock_t *sock = sock_data; + log_message(LOG_INFO, "VRRP sockpool: [ifindex(%d), proto(%d), fd(%d,%d)]" + , sock->ifindex + , sock->proto + , sock->fd_in + , sock->fd_out); +} + +static void +free_vrrp(void *data) +{ + vrrp_rt *vrrp = data; + element e; + + FREE(vrrp->iname); + FREE_PTR(vrrp->send_buffer); + FREE_PTR(vrrp->lvs_syncd_if); + FREE_PTR(vrrp->script_backup); + FREE_PTR(vrrp->script_master); + FREE_PTR(vrrp->script_fault); + FREE_PTR(vrrp->script_stop); + FREE_PTR(vrrp->script); + FREE(vrrp->ipsecah_counter); + + if (!LIST_ISEMPTY(vrrp->track_ifp)) + for (e = LIST_HEAD(vrrp->track_ifp); e; ELEMENT_NEXT(e)) + FREE(ELEMENT_DATA(e)); + free_list(vrrp->track_ifp); + + if (!LIST_ISEMPTY(vrrp->track_script)) + for (e = LIST_HEAD(vrrp->track_script); e; ELEMENT_NEXT(e)) + FREE(ELEMENT_DATA(e)); + free_list(vrrp->track_script); + + free_list(vrrp->vip); + free_list(vrrp->evip); + free_list(vrrp->vroutes); + FREE(vrrp); +} +static void +dump_vrrp(void *data) +{ + vrrp_rt *vrrp = data; + char auth_data[sizeof(vrrp->auth_data) + 1]; + + log_message(LOG_INFO, " VRRP Instance = %s", vrrp->iname); + if (vrrp->family == AF_INET6) + log_message(LOG_INFO, " Using Native IPv6"); + if (vrrp->init_state == VRRP_STATE_BACK) + log_message(LOG_INFO, " Want State = BACKUP"); + else + log_message(LOG_INFO, " Want State = MASTER"); + log_message(LOG_INFO, " Runing on device = %s", IF_NAME(vrrp->ifp)); + if (vrrp->dont_track_primary) + log_message(LOG_INFO, " VRRP interface tracking disabled"); + if (vrrp->mcast_saddr) + log_message(LOG_INFO, " Using mcast src_ip = %s", + inet_ntop2(vrrp->mcast_saddr)); + if (vrrp->lvs_syncd_if) + log_message(LOG_INFO, " Runing LVS sync daemon on interface = %s", + vrrp->lvs_syncd_if); + if (vrrp->garp_delay) + log_message(LOG_INFO, " Gratuitous ARP delay = %d", + vrrp->garp_delay/TIMER_HZ); + log_message(LOG_INFO, " Virtual Router ID = %d", vrrp->vrid); + log_message(LOG_INFO, " Priority = %d", vrrp->base_priority); + log_message(LOG_INFO, " Advert interval = %dsec", + vrrp->adver_int / TIMER_HZ); + if (vrrp->nopreempt) + log_message(LOG_INFO, " Preempt disabled"); + if (vrrp->preempt_delay) + log_message(LOG_INFO, " Preempt delay = %ld secs", + vrrp->preempt_delay / TIMER_HZ); + if (vrrp->auth_type) { + log_message(LOG_INFO, " Authentication type = %s", + (vrrp->auth_type == + VRRP_AUTH_AH) ? "IPSEC_AH" : "SIMPLE_PASSWORD"); + /* vrrp->auth_data is not \0 terminated */ + memcpy(auth_data, vrrp->auth_data, sizeof(vrrp->auth_data)); + auth_data[sizeof(vrrp->auth_data)] = '\0'; + log_message(LOG_INFO, " Password = %s", auth_data); + } + if (!LIST_ISEMPTY(vrrp->track_ifp)) { + log_message(LOG_INFO, " Tracked interfaces = %d", LIST_SIZE(vrrp->track_ifp)); + dump_list(vrrp->track_ifp); + } + if (!LIST_ISEMPTY(vrrp->track_script)) { + log_message(LOG_INFO, " Tracked scripts = %d", + LIST_SIZE(vrrp->track_script)); + dump_list(vrrp->track_script); + } + if (!LIST_ISEMPTY(vrrp->vip)) { + log_message(LOG_INFO, " Virtual IP = %d", LIST_SIZE(vrrp->vip)); + dump_list(vrrp->vip); + } + if (!LIST_ISEMPTY(vrrp->evip)) { + log_message(LOG_INFO, " Virtual IP Excluded = %d", LIST_SIZE(vrrp->evip)); + dump_list(vrrp->evip); + } + if (!LIST_ISEMPTY(vrrp->vroutes)) { + log_message(LOG_INFO, " Virtual Routes = %d", LIST_SIZE(vrrp->vroutes)); + dump_list(vrrp->vroutes); + } + if (vrrp->script_backup) + log_message(LOG_INFO, " Backup state transition script = %s", + vrrp->script_backup); + if (vrrp->script_master) + log_message(LOG_INFO, " Master state transition script = %s", + vrrp->script_master); + if (vrrp->script_fault) + log_message(LOG_INFO, " Fault state transition script = %s", + vrrp->script_fault); + if (vrrp->script_stop) + log_message(LOG_INFO, " Stop state transition script = %s", + vrrp->script_stop); + if (vrrp->script) + log_message(LOG_INFO, " Generic state transition script = '%s'", + vrrp->script); + if (vrrp->smtp_alert) + log_message(LOG_INFO, " Using smtp notification"); +} + +void +alloc_vrrp_sync_group(char *gname) +{ + int size = strlen(gname); + vrrp_sgroup *new; + + /* Allocate new VRRP group structure */ + new = (vrrp_sgroup *) MALLOC(sizeof (vrrp_sgroup)); + new->gname = (char *) MALLOC(size + 1); + new->state = VRRP_STATE_INIT; + memcpy(new->gname, gname, size); + new->global_tracking = 0; + + list_add(vrrp_data->vrrp_sync_group, new); +} + +void +alloc_vrrp(char *iname) +{ + int size = strlen(iname); + seq_counter *counter; + vrrp_rt *new; + + /* Allocate new VRRP structure */ + new = (vrrp_rt *) MALLOC(sizeof (vrrp_rt)); + counter = (seq_counter *) MALLOC(sizeof (seq_counter)); + + /* Build the structure */ + new->ipsecah_counter = counter; + + /* Set default values */ + new->family = AF_INET; + new->wantstate = VRRP_STATE_BACK; + new->init_state = VRRP_STATE_BACK; + new->adver_int = TIMER_HZ; + new->iname = (char *) MALLOC(size + 1); + memcpy(new->iname, iname, size); + + list_add(vrrp_data->vrrp, new); +} + +void +alloc_vrrp_track(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + + if (LIST_ISEMPTY(vrrp->track_ifp)) + vrrp->track_ifp = alloc_list(NULL, dump_track); + alloc_track(vrrp->track_ifp, strvec); +} + +void +alloc_vrrp_track_script(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + + if (LIST_ISEMPTY(vrrp->track_script)) + vrrp->track_script = alloc_list(NULL, dump_track_script); + alloc_track_script(vrrp->track_script, strvec); +} + +void +alloc_vrrp_vip(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + if (vrrp->ifp == NULL) { + log_message(LOG_ERR, "Configuration error: VRRP definition must belong to an interface"); + } + + if (LIST_ISEMPTY(vrrp->vip)) + vrrp->vip = alloc_list(free_ipaddress, dump_ipaddress); + alloc_ipaddress(vrrp->vip, strvec, vrrp->ifp); +} +void +alloc_vrrp_evip(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + + if (LIST_ISEMPTY(vrrp->evip)) + vrrp->evip = alloc_list(free_ipaddress, dump_ipaddress); + alloc_ipaddress(vrrp->evip, strvec, vrrp->ifp); +} + +void +alloc_vrrp_vroute(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + + if (LIST_ISEMPTY(vrrp->vroutes)) + vrrp->vroutes = alloc_list(free_iproute, dump_iproute); + alloc_route(vrrp->vroutes, strvec); +} + +void +alloc_vrrp_script(char *sname) +{ + int size = strlen(sname); + vrrp_script *new; + + /* Allocate new VRRP group structure */ + new = (vrrp_script *) MALLOC(sizeof (vrrp_script)); + new->sname = (char *) MALLOC(size + 1); + memcpy(new->sname, sname, size + 1); + new->interval = VRRP_SCRIPT_DI * TIMER_HZ; + new->timeout = VRRP_SCRIPT_DT * TIMER_HZ; + new->weight = VRRP_SCRIPT_DW; + new->result = VRRP_SCRIPT_STATUS_INIT; + new->inuse = 0; + new->rise = 1; + new->fall = 1; + list_add(vrrp_data->vrrp_script, new); +} + +/* data facility functions */ +void +alloc_vrrp_buffer(void) +{ + vrrp_buffer = (char *) MALLOC(VRRP_PACKET_TEMP_LEN); +} + +void +free_vrrp_buffer(void) +{ + FREE(vrrp_buffer); +} + +vrrp_conf_data * +alloc_vrrp_data(void) +{ + vrrp_conf_data *new; + + new = (vrrp_conf_data *) MALLOC(sizeof (vrrp_conf_data)); + new->vrrp = alloc_list(free_vrrp, dump_vrrp); + new->vrrp_index = alloc_mlist(NULL, NULL, 255+1); + new->vrrp_index_fd = alloc_mlist(NULL, NULL, 1024+1); + new->vrrp_sync_group = alloc_list(free_vgroup, dump_vgroup); + new->vrrp_script = alloc_list(free_vscript, dump_vscript); + new->vrrp_socket_pool = alloc_list(free_sock, dump_sock); + + return new; +} + +void +free_vrrp_data(vrrp_conf_data * data) +{ + free_list(data->static_addresses); + free_list(data->static_routes); + free_mlist(data->vrrp_index, 255+1); + free_mlist(data->vrrp_index_fd, 1024+1); + free_list(data->vrrp); + free_list(data->vrrp_sync_group); + free_list(data->vrrp_script); +// free_list(data->vrrp_socket_pool); + FREE(data); +} + +void +free_vrrp_sockpool(vrrp_conf_data * data) +{ + free_list(data->vrrp_socket_pool); +} + +void +dump_vrrp_data(vrrp_conf_data * data) +{ + if (!LIST_ISEMPTY(data->static_addresses)) { + log_message(LOG_INFO, "------< Static Addresses >------"); + dump_list(data->static_addresses); + } + if (!LIST_ISEMPTY(data->static_routes)) { + log_message(LOG_INFO, "------< Static Routes >------"); + dump_list(data->static_routes); + } + if (!LIST_ISEMPTY(data->vrrp)) { + log_message(LOG_INFO, "------< VRRP Topology >------"); + dump_list(data->vrrp); + } + if (!LIST_ISEMPTY(data->vrrp_sync_group)) { + log_message(LOG_INFO, "------< VRRP Sync groups >------"); + dump_list(data->vrrp_sync_group); + } + if (!LIST_ISEMPTY(data->vrrp_script)) { + log_message(LOG_INFO, "------< VRRP Scripts >------"); + dump_list(data->vrrp_script); + } +} diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_if.c keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_if.c --- keepalived-1.2.7/keepalived/vrrp/vrrp_if.c 2012-08-17 01:21:28.000000000 +0200 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_if.c 2013-05-24 09:39:17.635832500 +0200 @@ -462,7 +462,7 @@ if_join_vrrp_group(sa_family_t family, i } int -if_leave_vrrp_group(sa_family_t family, int sd, interface *ifp) +if_leave_vrrp_group(sa_family_t family, int sd, interface *ifp, int unicast) { struct ip_mreqn imr; struct ipv6_mreq imr6; @@ -472,6 +472,9 @@ if_leave_vrrp_group(sa_family_t family, if (sd < 0 || !ifp) return -1; + if (unicast) + goto skip_mcast_release; + /* Leaving the VRRP multicast group */ if (family == AF_INET) { memset(&imr, 0, sizeof(imr)); @@ -500,6 +503,7 @@ if_leave_vrrp_group(sa_family_t family, return -1; } +skip_mcast_release: /* Finally close the desc */ close(sd); return 0; diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_if.c.orig keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_if.c.orig --- keepalived-1.2.7/keepalived/vrrp/vrrp_if.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_if.c.orig 2012-08-17 01:21:28.000000000 +0200 @@ -0,0 +1,641 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: Interfaces manipulation. + * + * Author: Alexandre Cassen, <acassen@linux-vs.org> + * + * 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. + * + * 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. + * + * Copyright (C) 2001-2012 Alexandre Cassen, <acassen@gmail.com> + */ + +/* global include */ +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +typedef __uint64_t u64; +typedef __uint32_t u32; +typedef __uint16_t u16; +typedef __uint8_t u8; +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <errno.h> +#include <fcntl.h> +#include <syslog.h> +#include <ctype.h> +#ifdef use_linux_libc5 +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#ifdef _KRNL_2_4_ +#include <linux/ethtool.h> +#endif + +/* local include */ +#include "scheduler.h" +#include "global_data.h" +#include "vrrp_data.h" +#include "vrrp.h" +#include "vrrp_if.h" +#include "vrrp_netlink.h" +#include "memory.h" +#include "utils.h" +#include "logger.h" + +/* Global vars */ +static list if_queue; +static struct ifreq ifr; + +/* Helper functions */ +/* Return interface from interface index */ +interface * +if_get_by_ifindex(const int ifindex) +{ + interface *ifp; + element e; + + if (LIST_ISEMPTY(if_queue)) + return NULL; + + for (e = LIST_HEAD(if_queue); e; ELEMENT_NEXT(e)) { + ifp = ELEMENT_DATA(e); + if (ifp->ifindex == ifindex) + return ifp; + } + return NULL; +} + +interface * +if_get_by_ifname(const char *ifname) +{ + interface *ifp; + element e; + + if (LIST_ISEMPTY(if_queue)) { + log_message(LOG_ERR, "Interface queue is empty"); + return NULL; + } + + for (e = LIST_HEAD(if_queue); e; ELEMENT_NEXT(e)) { + ifp = ELEMENT_DATA(e); + if (!strcmp(ifp->ifname, ifname)) + return ifp; + } + + log_message(LOG_ERR, "No such interface, %s", ifname); + return NULL; +} + +/* MII Transceiver Registers poller functions */ +static int +if_mii_read(const int fd, const int phy_id, int location) +{ + uint16_t *data = (uint16_t *) (&ifr.ifr_data); + + data[0] = phy_id; + data[1] = location; + + if (ioctl(fd, SIOCGMIIREG, &ifr) < 0) { + log_message(LOG_ERR, "SIOCGMIIREG on %s failed: %s", ifr.ifr_name, + strerror(errno)); + return -1; + } + return data[3]; +} + +/* +static void if_mii_dump(const uint16_t mii_regs[32], unsigned phy_id) +{ + int mii_reg; + + printf(" MII PHY #%d transceiver registers:\n", phy_id); + for (mii_reg = 0; mii_reg < 32; mii_reg++) + printf("%s %4.4x", (mii_reg % 8) == 0 ? "\n ":"", mii_regs[mii_reg]); +} +*/ + +static int +if_mii_status(const int fd) +{ + uint16_t *data = (uint16_t *) (&ifr.ifr_data); + unsigned phy_id = data[0]; + uint16_t mii_regs[32]; + int mii_reg; + uint16_t bmsr, new_bmsr; + + /* Reset MII registers */ + memset(mii_regs, 0, sizeof (mii_regs)); + + for (mii_reg = 0; mii_reg < 32; mii_reg++) + mii_regs[mii_reg] = if_mii_read(fd, phy_id, mii_reg); + +// if_mii_dump(mii_regs, phy_id); + + if (mii_regs[0] == 0xffff) { + log_message(LOG_ERR, "No MII transceiver present for %s !!!", + ifr.ifr_name); + return -1; + } + + bmsr = mii_regs[1]; + + /* + * For Basic Mode Status Register (BMSR). + * Sticky field (Link established & Jabber detected), we need to read + * a second time the BMSR to get current status. + */ + new_bmsr = if_mii_read(fd, phy_id, 1); + +// printf(" \nBasic Mode Status Register 0x%4.4x ... 0x%4.4x\n", bmsr, new_bmsr); + + if (bmsr & 0x0004) + return LINK_UP; + else if (new_bmsr & 0x0004) + return LINK_UP; + else + return LINK_DOWN; +} + +int +if_mii_probe(const char *ifname) +{ + uint16_t *data = (uint16_t *) (&ifr.ifr_data); + int phy_id; + int fd = socket(AF_INET, SOCK_DGRAM, 0); + int status = 0; + + if (fd < 0) + return -1; + memset(&ifr, 0, sizeof (struct ifreq)); + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + if (ioctl(fd, SIOCGMIIPHY, &ifr) < 0) { + close(fd); + return -1; + } + + /* check if the driver reports BMSR using the MII interface, as we + * will need this and we already know that some don't support it. + */ + phy_id = data[0]; /* save it in case it is overwritten */ + data[1] = 1; + if (ioctl(fd, SIOCGMIIREG, &ifr) < 0) { + close(fd); + return -1; + } + data[0] = phy_id; + + /* Dump the MII transceiver */ + status = if_mii_status(fd); + close(fd); + return status; +} + +static int +if_ethtool_status(const int fd) +{ +#ifdef ETHTOOL_GLINK + struct ethtool_value edata; + int err = 0; + + edata.cmd = ETHTOOL_GLINK; + ifr.ifr_data = (caddr_t) & edata; + err = ioctl(fd, SIOCETHTOOL, &ifr); + if (err == 0) + return (edata.data) ? 1 : 0; + else +#endif + return -1; +} + +int +if_ethtool_probe(const char *ifname) +{ + int fd = socket(AF_INET, SOCK_DGRAM, 0); + int status = 0; + + if (fd < 0) + return -1; + memset(&ifr, 0, sizeof (struct ifreq)); + strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name)); + + status = if_ethtool_status(fd); + close(fd); + return status; +} + +void +if_ioctl_flags(interface * ifp) +{ + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (fd < 0) + return; + memset(&ifr, 0, sizeof (struct ifreq)); + strncpy(ifr.ifr_name, ifp->ifname, sizeof (ifr.ifr_name)); + if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { + close(fd); + return; + } + ifp->flags = ifr.ifr_flags; + close(fd); +} + +/* Interfaces lookup */ +static void +free_if(void *data) +{ + FREE(data); +} + +void +dump_if(void *data) +{ + interface *ifp = data; + char addr_str[41]; + + log_message(LOG_INFO, "------< NIC >------"); + log_message(LOG_INFO, " Name = %s", ifp->ifname); + log_message(LOG_INFO, " index = %d", ifp->ifindex); + log_message(LOG_INFO, " IPv4 address = %s", inet_ntop2(ifp->sin_addr.s_addr)); + inet_ntop(AF_INET6, &ifp->sin6_addr, addr_str, 41); + log_message(LOG_INFO, " IPv6 address = %s", addr_str); + + /* FIXME: Harcoded for ethernet */ + if (ifp->hw_type == ARPHRD_ETHER) + log_message(LOG_INFO, " MAC = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x", + ifp->hw_addr[0], ifp->hw_addr[1], ifp->hw_addr[2] + , ifp->hw_addr[3], ifp->hw_addr[4], ifp->hw_addr[5]); + + if (ifp->flags & IFF_UP) + log_message(LOG_INFO, " is UP"); + + if (ifp->flags & IFF_RUNNING) + log_message(LOG_INFO, " is RUNNING"); + + if (!(ifp->flags & IFF_UP) && !(ifp->flags & IFF_RUNNING)) + log_message(LOG_INFO, " is DOWN"); + + log_message(LOG_INFO, " MTU = %d", ifp->mtu); + + switch (ifp->hw_type) { + case ARPHRD_LOOPBACK: + log_message(LOG_INFO, " HW Type = LOOPBACK"); + break; + case ARPHRD_ETHER: + log_message(LOG_INFO, " HW Type = ETHERNET"); + break; + default: + log_message(LOG_INFO, " HW Type = UNKNOWN"); + break; + } + + /* MII channel supported ? */ + if (IF_MII_SUPPORTED(ifp)) + log_message(LOG_INFO, " NIC support MII regs"); + else if (IF_ETHTOOL_SUPPORTED(ifp)) + log_message(LOG_INFO, " NIC support EHTTOOL GLINK interface"); + else + log_message(LOG_INFO, " Enabling NIC ioctl refresh polling"); +} + +static void +init_if_queue(void) +{ + if_queue = alloc_list(free_if, dump_if); +} + +void +if_add_queue(interface * ifp) +{ + list_add(if_queue, ifp); +} + +static int +if_linkbeat_refresh_thread(thread_t * thread) +{ + interface *ifp = THREAD_ARG(thread); + + if (IF_MII_SUPPORTED(ifp)) + ifp->linkbeat = (if_mii_probe(ifp->ifname)) ? 1 : 0; + else if (IF_ETHTOOL_SUPPORTED(ifp)) + ifp->linkbeat = (if_ethtool_probe(ifp->ifname)) ? 1 : 0; + else + ifp->linkbeat = 1; + + /* + * update ifp->flags to get the new IFF_RUNNING status. + * Some buggy drivers need this... + */ + if_ioctl_flags(ifp); + + /* Register next polling thread */ + thread_add_timer(master, if_linkbeat_refresh_thread, ifp, POLLING_DELAY); + return 0; +} + +static void +init_if_linkbeat(void) +{ + interface *ifp; + element e; + int status; + + for (e = LIST_HEAD(if_queue); e; ELEMENT_NEXT(e)) { + ifp = ELEMENT_DATA(e); + ifp->lb_type = LB_IOCTL; + status = if_mii_probe(ifp->ifname); + if (status >= 0) { + ifp->lb_type = LB_MII; + ifp->linkbeat = (status) ? 1 : 0; + } else { + status = if_ethtool_probe(ifp->ifname); + if (status >= 0) { + ifp->lb_type = LB_ETHTOOL; + ifp->linkbeat = (status) ? 1 : 0; + } + } + + /* Register new monitor thread */ + thread_add_timer(master, if_linkbeat_refresh_thread, ifp, POLLING_DELAY); + } +} + +int +if_linkbeat(const interface * ifp) +{ + if (!global_data->linkbeat_use_polling) + return 1; + + if (IF_MII_SUPPORTED(ifp) || IF_ETHTOOL_SUPPORTED(ifp)) + return IF_LINKBEAT(ifp); + + return 1; +} + +/* Interface queue helpers*/ +void +free_interface_queue(void) +{ + if (!LIST_ISEMPTY(if_queue)) + free_list(if_queue); + if_queue = NULL; + kernel_netlink_close(); +} + +void +init_interface_queue(void) +{ + init_if_queue(); +// dump_list(if_queue); + netlink_interface_lookup(); +} + +void +init_interface_linkbeat(void) +{ + if (global_data->linkbeat_use_polling) { + log_message(LOG_INFO, "Using MII-BMSR NIC polling thread..."); + init_if_linkbeat(); + } else { + log_message(LOG_INFO, "Using LinkWatch kernel netlink reflector..."); + } +} + +int +if_join_vrrp_group(sa_family_t family, int *sd, interface *ifp, int proto) +{ + struct ip_mreqn imr; + struct ipv6_mreq imr6; + int ret = 0; + + if (*sd < 0) + return -1; + + /* -> outbound processing option + * join the multicast group. + * binding the socket to the interface for outbound multicast + * traffic. + */ + + if (family == AF_INET) { + memset(&imr, 0, sizeof(imr)); + imr.imr_multiaddr.s_addr = htonl(INADDR_VRRP_GROUP); + imr.imr_address.s_addr = IF_ADDR(ifp); + imr.imr_ifindex = IF_INDEX(ifp); + + /* -> Need to handle multicast convergance after takeover. + * We retry until multicast is available on the interface. + */ + ret = setsockopt(*sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (char *) &imr, sizeof(struct ip_mreqn)); + } else { + memset(&imr6, 0, sizeof(imr6)); + imr6.ipv6mr_multiaddr.s6_addr16[0] = htons(0xff02); + imr6.ipv6mr_multiaddr.s6_addr16[7] = htons(0x12); + imr6.ipv6mr_interface = IF_INDEX(ifp); + ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + (char *) &imr6, sizeof(struct ipv6_mreq)); + } + + if (ret < 0) { + log_message(LOG_INFO, "cant do IP%s_ADD_MEMBERSHIP errno=%s (%d)", + (family == AF_INET) ? "" : "V6", strerror(errno), errno); + close(*sd); + *sd = -1; + } + + return *sd; +} + +int +if_leave_vrrp_group(sa_family_t family, int sd, interface *ifp) +{ + struct ip_mreqn imr; + struct ipv6_mreq imr6; + int ret = 0; + + /* If fd is -1 then we add a membership trouble */ + if (sd < 0 || !ifp) + return -1; + + /* Leaving the VRRP multicast group */ + if (family == AF_INET) { + memset(&imr, 0, sizeof(imr)); + /* FIXME: change this to use struct ip_mreq */ + imr.imr_multiaddr.s_addr = htonl(INADDR_VRRP_GROUP); + imr.imr_address.s_addr = IF_ADDR(ifp); + imr.imr_ifindex = IF_INDEX(ifp); + ret = setsockopt(sd, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (char *) &imr, sizeof (struct ip_mreqn)); + } else { + memset(&imr6, 0, sizeof(imr6)); + /* rfc5798.5.1.2.2 : destination IPv6 mcast group is + * ff02:0:0:0:0:0:0:12. + */ + imr6.ipv6mr_multiaddr.s6_addr16[0] = htons(0xff02); + imr6.ipv6mr_multiaddr.s6_addr16[7] = htons(0x12); + imr6.ipv6mr_interface = IF_INDEX(ifp); + ret = setsockopt(sd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, + (char *) &imr6, sizeof(struct ipv6_mreq)); + } + + if (ret < 0) { + log_message(LOG_INFO, "cant do IP%s_DROP_MEMBERSHIP errno=%s (%d)", + (family == AF_INET) ? "" : "V6", strerror(errno), errno); + close(sd); + return -1; + } + + /* Finally close the desc */ + close(sd); + return 0; +} + +int +if_setsockopt_bindtodevice(int *sd, interface *ifp) +{ + int ret; + + if (*sd < 0) + return -1; + + /* -> inbound processing option + * Specify the bound_dev_if. + * why IP_ADD_MEMBERSHIP & IP_MULTICAST_IF doesnt set + * sk->bound_dev_if themself ??? !!! + * Needed for filter multicasted advert per interface. + * + * -- If you read this !!! and know the answer to the question + * please feel free to answer me ! :) + */ + ret = setsockopt(*sd, SOL_SOCKET, SO_BINDTODEVICE, IF_NAME(ifp), strlen(IF_NAME(ifp)) + 1); + if (ret < 0) { + log_message(LOG_INFO, "cant bind to device %s. errno=%d. (try to run it as root)", + IF_NAME(ifp), errno); + close(*sd); + *sd = -1; + } + + return *sd; +} + +int +if_setsockopt_hdrincl(int *sd) +{ + int ret; + int on = 1; + + if (*sd < 0) + return -1; + + /* Include IP header into RAW protocol packet */ + ret = setsockopt(*sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)); + if (ret < 0) { + log_message(LOG_INFO, "cant set HDRINCL IP option. errno=%d (%m)", errno); + close(*sd); + *sd = -1; + } + + return *sd; +} + +int +if_setsockopt_mcast_loop(sa_family_t family, int *sd) +{ + int ret; + unsigned char loop = 0; + int loopv6 = 0; + + if (*sd < 0) + return -1; + + /* Include IP header into RAW protocol packet */ + if (family == AF_INET) + ret = setsockopt(*sd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); + else + ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loopv6, sizeof(loopv6)); + + if (ret < 0) { + log_message(LOG_INFO, "cant set IP%s_MULTICAST_LOOP IP option. errno=%d (%m)", + (family == AF_INET) ? "" : "V6", errno); + close(*sd); + *sd = -1; + } + + return *sd; +} + +int +if_setsockopt_mcast_hops(sa_family_t family, int *sd) +{ + int ret; + int hops = 255; + + /* Not applicable for IPv4 */ + if (*sd < 0 || family == AF_INET) + return -1; + + /* Include IP header into RAW protocol packet */ + ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); + if (ret < 0) { + log_message(LOG_INFO, "cant set IPV6_MULTICAST_HOPS IP option. errno=%d (%m)", errno); + close(*sd); + *sd = -1; + } + + return *sd; +} + +int +if_setsockopt_mcast_if(sa_family_t family, int *sd, interface *ifp) +{ + int ret; + unsigned int ifindex; + + /* Not applicable for IPv4 */ + if (*sd < 0 || family == AF_INET) + return -1; + + /* Include IP header into RAW protocol packet */ + ifindex = IF_INDEX(ifp); + ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)); + if (ret < 0) { + log_message(LOG_INFO, "cant set IPV6_MULTICAST_IF IP option. errno=%d (%m)", errno); + close(*sd); + *sd = -1; + } + + return *sd; +} + +int if_setsockopt_priority(int *sd) { + int ret; + int priority = 6; + + if (*sd < 0) + return -1; + + /* Set SO_PRIORITY for VRRP traffic */ + ret = setsockopt(*sd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)); + if (ret < 0) { + log_message(LOG_INFO, "cant set SO_PRIORITY IP option. errno=%d (%m)", errno); + close(*sd); + *sd = -1; + } + + return *sd; +} diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_parser.c keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_parser.c --- keepalived-1.2.7/keepalived/vrrp/vrrp_parser.c 2012-08-29 00:05:12.000000000 +0200 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_parser.c 2013-05-24 09:42:51.076040600 +0200 @@ -185,6 +185,18 @@ vrrp_mcastip_handler(vector_t *strvec) inet_ston(vector_slot(strvec, 1), &vrrp->mcast_saddr); } static void +vrrp_unicast_bind_handler(vector strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + inet_ston(VECTOR_SLOT(strvec, 1), &vrrp->unicast_bind); +} +static void +vrrp_unicast_peer_handler(vector strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + inet_ston(VECTOR_SLOT(strvec, 1), &vrrp->unicast_peer); +} +static void vrrp_vrid_handler(vector_t *strvec) { vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); @@ -484,6 +496,8 @@ vrrp_init_keywords(void) install_keyword("track_interface", &vrrp_track_int_handler); install_keyword("track_script", &vrrp_track_scr_handler); install_keyword("mcast_src_ip", &vrrp_mcastip_handler); + install_keyword("vrrp_unicast_bind", &vrrp_unicast_bind_handler); + install_keyword("vrrp_unicast_peer", &vrrp_unicast_peer_handler); install_keyword("virtual_router_id", &vrrp_vrid_handler); install_keyword("priority", &vrrp_prio_handler); install_keyword("advert_int", &vrrp_adv_handler); diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_parser.c.orig keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_parser.c.orig --- keepalived-1.2.7/keepalived/vrrp/vrrp_parser.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_parser.c.orig 2012-08-29 00:05:12.000000000 +0200 @@ -0,0 +1,519 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: Configuration file parser/reader. Place into the dynamic + * data structure representation the conf file representing + * the loadbalanced server pool. + * + * Author: Alexandre Cassen, <acassen@linux-vs.org> + * + * 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. + * + * 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. + * + * Copyright (C) 2001-2012 Alexandre Cassen, <acassen@gmail.com> + */ + +#include "vrrp_parser.h" +#include "vrrp_data.h" +#include "vrrp_sync.h" +#include "vrrp_index.h" +#include "vrrp_if.h" +#include "vrrp_vmac.h" +#include "vrrp.h" +#include "global_data.h" +#include "global_parser.h" +#include "logger.h" +#include "parser.h" +#include "memory.h" + +/* Static addresses handler */ +static void +static_addresses_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_saddress); +} + +/* Static routes handler */ +static void +static_routes_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_sroute); +} + +/* VRRP handlers */ +static void +vrrp_sync_group_handler(vector_t *strvec) +{ + alloc_vrrp_sync_group(vector_slot(strvec, 1)); +} +static void +vrrp_group_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->iname = read_value_block(); +} +static void +vrrp_gnotify_backup_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->script_backup = set_value(strvec); + vgroup->notify_exec = 1; +} +static void +vrrp_gnotify_master_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->script_master = set_value(strvec); + vgroup->notify_exec = 1; +} +static void +vrrp_gnotify_fault_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->script_fault = set_value(strvec); + vgroup->notify_exec = 1; +} +static void +vrrp_gnotify_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->script = set_value(strvec); + vgroup->notify_exec = 1; +} +static void +vrrp_gsmtp_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->smtp_alert = 1; +} +static void +vrrp_gglobal_tracking_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->global_tracking = 1; +} +static void +vrrp_handler(vector_t *strvec) +{ + alloc_vrrp(vector_slot(strvec, 1)); +} +static void +vrrp_vmac_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->vmac = 1; + if (!vrrp->mcast_saddr) + vrrp->mcast_saddr = IF_ADDR(vrrp->ifp); + if (vector_size(strvec) == 2) { + strncpy(vrrp->vmac_ifname, vector_slot(strvec, 1), + IFNAMSIZ - 1); + } else if (vrrp->vrid) { + snprintf(vrrp->vmac_ifname, IFNAMSIZ, "vrrp.%d", vrrp->vrid); + } + + if (strlen(vrrp->vmac_ifname)) { + log_message(LOG_INFO, "vmac_ifname=%s for vrrp_instace %s" + , vrrp->vmac_ifname + , vrrp->iname); + } + if (vrrp->ifp && !(vrrp->vmac & 2)) + netlink_link_add_vmac(vrrp); +} +static void +vrrp_native_ipv6_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->family = AF_INET6; + + if (vrrp->auth_type != VRRP_AUTH_NONE) + vrrp->auth_type = VRRP_AUTH_NONE; +} +static void +vrrp_state_handler(vector_t *strvec) +{ + char *str = vector_slot(strvec, 1); + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp_sgroup *vgroup = vrrp->sync; + + if (!strcmp(str, "MASTER")) { + vrrp->wantstate = VRRP_STATE_MAST; + vrrp->init_state = VRRP_STATE_MAST; + } + + /* set eventual sync group */ + if (vgroup) + vgroup->state = vrrp->wantstate; +} +static void +vrrp_int_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + char *name = vector_slot(strvec, 1); + vrrp->ifp = if_get_by_ifname(name); + if (vrrp->vmac && !(vrrp->vmac & 2)) + netlink_link_add_vmac(vrrp); +} +static void +vrrp_track_int_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_vrrp_track); +} +static void +vrrp_track_scr_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_vrrp_track_script); +} +static void +vrrp_dont_track_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->dont_track_primary = 1; +} +static void +vrrp_mcastip_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + inet_ston(vector_slot(strvec, 1), &vrrp->mcast_saddr); +} +static void +vrrp_vrid_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->vrid = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_VID(vrrp->vrid)) { + log_message(LOG_INFO, "VRRP Error : VRID not valid !"); + log_message(LOG_INFO, + " must be between 1 & 255. reconfigure !"); + } else { + alloc_vrrp_bucket(vrrp); + if (vrrp->vmac && strlen(vrrp->vmac_ifname) == 0) { + snprintf(vrrp->vmac_ifname, IFNAMSIZ, "vrrp.%d" + , vrrp->vrid); + log_message(LOG_INFO, "vmac_ifname=%s for vrrp_instace %s" + , vrrp->vmac_ifname + , vrrp->iname); + } + } +} +static void +vrrp_prio_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->effective_priority = vrrp->base_priority = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_PRIORITY(vrrp->base_priority)) { + log_message(LOG_INFO, "VRRP Error : Priority not valid !"); + log_message(LOG_INFO, + " must be between 1 & 255. reconfigure !"); + log_message(LOG_INFO, + " Using default value : %d\n", VRRP_PRIO_DFL); + vrrp->effective_priority = vrrp->base_priority = VRRP_PRIO_DFL; + } +} +static void +vrrp_adv_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->adver_int = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_ADVERT_INT(vrrp->adver_int)) { + log_message(LOG_INFO, "VRRP Error : Advert interval not valid !"); + log_message(LOG_INFO, + " must be between less than 1sec."); + log_message(LOG_INFO, " Using default value : 1sec"); + vrrp->adver_int = 1; + } + vrrp->adver_int *= TIMER_HZ; +} +static void +vrrp_debug_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->debug = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_DEBUG_INT(vrrp->debug)) { + log_message(LOG_INFO, "VRRP Error : Debug interval not valid !"); + log_message(LOG_INFO, " must be between 0-4"); + vrrp->debug = 0; + } +} +static void +vrrp_nopreempt_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->nopreempt = 1; +} +static void /* backwards compatibility */ +vrrp_preempt_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->nopreempt = 0; +} +static void +vrrp_preempt_delay_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->preempt_delay = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_PREEMPT_DELAY(vrrp->preempt_delay)) { + log_message(LOG_INFO, "VRRP Error : Preempt_delay not valid !"); + log_message(LOG_INFO, " must be between 0-%d", + TIMER_MAX_SEC); + vrrp->preempt_delay = 0; + } + vrrp->preempt_delay *= TIMER_HZ; + vrrp->preempt_time = timer_add_long(timer_now(), vrrp->preempt_delay); +} +static void +vrrp_notify_backup_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script_backup = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_notify_master_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script_master = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_notify_fault_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script_fault = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_notify_stop_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script_stop = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_notify_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_smtp_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->smtp_alert = 1; +} +static void +vrrp_lvs_syncd_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->lvs_syncd_if = set_value(strvec); +} +static void +vrrp_garp_delay_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->garp_delay = atoi(vector_slot(strvec, 1)) * TIMER_HZ; + if (vrrp->garp_delay < TIMER_HZ) + vrrp->garp_delay = TIMER_HZ; +} +static void +vrrp_auth_type_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + char *str = vector_slot(strvec, 1); + + if (!strcmp(str, "AH") && vrrp->family == AF_INET) + vrrp->auth_type = VRRP_AUTH_AH; + else if (!strcmp(str, "PASS") && vrrp->family == AF_INET) + vrrp->auth_type = VRRP_AUTH_PASS; +} +static void +vrrp_auth_pass_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + char *str = vector_slot(strvec, 1); + int max_size = sizeof (vrrp->auth_data); + int str_len = strlen(str); + + if (str_len > max_size) { + str_len = max_size; + log_message(LOG_INFO, + "Truncating auth_pass to %d characters", max_size); + } + + memset(vrrp->auth_data, 0, max_size); + memcpy(vrrp->auth_data, str, str_len); +} +static void +vrrp_vip_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + char *buf; + char *str = NULL; + vector_t *vec = NULL; + int nbvip = 0; + + buf = (char *) MALLOC(MAXBUF); + while (read_line(buf, MAXBUF)) { + vec = alloc_strvec(buf); + if (vec) { + str = vector_slot(vec, 0); + if (!strcmp(str, EOB)) { + free_strvec(vec); + break; + } + + if (vector_size(vec)) { + nbvip++; + if (nbvip > VRRP_MAX_VIP) { + log_message(LOG_INFO, + "VRRP_Instance(%s) " + "trunc to the first %d VIPs.", + vrrp->iname, VRRP_MAX_VIP); + log_message(LOG_INFO, + " => Declare others VIPs into" + " the excluded vip block"); + } else + alloc_vrrp_vip(vec); + } + + free_strvec(vec); + } + memset(buf, 0, MAXBUF); + } + FREE(buf); +} +static void +vrrp_evip_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_vrrp_evip); +} +static void +vrrp_vroutes_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_vrrp_vroute); +} +static void +vrrp_script_handler(vector_t *strvec) +{ + alloc_vrrp_script(vector_slot(strvec, 1)); +} +static void +vrrp_vscript_script_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->script = set_value(strvec); +} +static void +vrrp_vscript_interval_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->interval = atoi(vector_slot(strvec, 1)) * TIMER_HZ; + if (vscript->interval < TIMER_HZ) + vscript->interval = TIMER_HZ; +} +static void +vrrp_vscript_timeout_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->timeout = atoi(vector_slot(strvec, 1)) * TIMER_HZ; + if (vscript->timeout < TIMER_HZ) + vscript->timeout = TIMER_HZ; +} +static void +vrrp_vscript_weight_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->weight = atoi(vector_slot(strvec, 1)); +} +static void +vrrp_vscript_rise_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->rise = atoi(vector_slot(strvec, 1)); + if (vscript->rise < 1) + vscript->rise = 1; +} +static void +vrrp_vscript_fall_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->fall = atoi(vector_slot(strvec, 1)); + if (vscript->fall < 1) + vscript->fall = 1; +} + +vector_t * +vrrp_init_keywords(void) +{ + /* global definitions mapping */ + global_init_keywords(); + + /* Static routes mapping */ + install_keyword_root("static_ipaddress", &static_addresses_handler); + install_keyword_root("static_routes", &static_routes_handler); + + /* VRRP Instance mapping */ + install_keyword_root("vrrp_sync_group", &vrrp_sync_group_handler); + install_keyword("group", &vrrp_group_handler); + install_keyword("notify_backup", &vrrp_gnotify_backup_handler); + install_keyword("notify_master", &vrrp_gnotify_master_handler); + install_keyword("notify_fault", &vrrp_gnotify_fault_handler); + install_keyword("notify", &vrrp_gnotify_handler); + install_keyword("smtp_alert", &vrrp_gsmtp_handler); + install_keyword("global_tracking", &vrrp_gglobal_tracking_handler); + install_keyword_root("vrrp_instance", &vrrp_handler); + install_keyword("use_vmac", &vrrp_vmac_handler); + install_keyword("native_ipv6", &vrrp_native_ipv6_handler); + install_keyword("state", &vrrp_state_handler); + install_keyword("interface", &vrrp_int_handler); + install_keyword("dont_track_primary", &vrrp_dont_track_handler); + install_keyword("track_interface", &vrrp_track_int_handler); + install_keyword("track_script", &vrrp_track_scr_handler); + install_keyword("mcast_src_ip", &vrrp_mcastip_handler); + install_keyword("virtual_router_id", &vrrp_vrid_handler); + install_keyword("priority", &vrrp_prio_handler); + install_keyword("advert_int", &vrrp_adv_handler); + install_keyword("virtual_ipaddress", &vrrp_vip_handler); + install_keyword("virtual_ipaddress_excluded", &vrrp_evip_handler); + install_keyword("virtual_routes", &vrrp_vroutes_handler); + install_keyword("preempt", &vrrp_preempt_handler); + install_keyword("nopreempt", &vrrp_nopreempt_handler); + install_keyword("preempt_delay", &vrrp_preempt_delay_handler); + install_keyword("debug", &vrrp_debug_handler); + install_keyword("notify_backup", &vrrp_notify_backup_handler); + install_keyword("notify_master", &vrrp_notify_master_handler); + install_keyword("notify_fault", &vrrp_notify_fault_handler); + install_keyword("notify_stop", &vrrp_notify_stop_handler); + install_keyword("notify", &vrrp_notify_handler); + install_keyword("smtp_alert", &vrrp_smtp_handler); + install_keyword("lvs_sync_daemon_interface", &vrrp_lvs_syncd_handler); + install_keyword("garp_master_delay", &vrrp_garp_delay_handler); + install_keyword("authentication", NULL); + install_sublevel(); + install_keyword("auth_type", &vrrp_auth_type_handler); + install_keyword("auth_pass", &vrrp_auth_pass_handler); + install_sublevel_end(); + install_keyword_root("vrrp_script", &vrrp_script_handler); + install_keyword("script", &vrrp_vscript_script_handler); + install_keyword("interval", &vrrp_vscript_interval_handler); + install_keyword("timeout", &vrrp_vscript_timeout_handler); + install_keyword("weight", &vrrp_vscript_weight_handler); + install_keyword("rise", &vrrp_vscript_rise_handler); + install_keyword("fall", &vrrp_vscript_fall_handler); + + return keywords; +} diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_parser.c~ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_parser.c~ --- keepalived-1.2.7/keepalived/vrrp/vrrp_parser.c~ 1970-01-01 01:00:00.000000000 +0100 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_parser.c~ 2013-05-24 09:39:17.000000000 +0200 @@ -0,0 +1,521 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: Configuration file parser/reader. Place into the dynamic + * data structure representation the conf file representing + * the loadbalanced server pool. + * + * Author: Alexandre Cassen, <acassen@linux-vs.org> + * + * 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. + * + * 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. + * + * Copyright (C) 2001-2012 Alexandre Cassen, <acassen@gmail.com> + */ + +#include "vrrp_parser.h" +#include "vrrp_data.h" +#include "vrrp_sync.h" +#include "vrrp_index.h" +#include "vrrp_if.h" +#include "vrrp_vmac.h" +#include "vrrp.h" +#include "global_data.h" +#include "global_parser.h" +#include "logger.h" +#include "parser.h" +#include "memory.h" + +/* Static addresses handler */ +static void +static_addresses_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_saddress); +} + +/* Static routes handler */ +static void +static_routes_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_sroute); +} + +/* VRRP handlers */ +static void +vrrp_sync_group_handler(vector_t *strvec) +{ + alloc_vrrp_sync_group(vector_slot(strvec, 1)); +} +static void +vrrp_group_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->iname = read_value_block(); +} +static void +vrrp_gnotify_backup_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->script_backup = set_value(strvec); + vgroup->notify_exec = 1; +} +static void +vrrp_gnotify_master_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->script_master = set_value(strvec); + vgroup->notify_exec = 1; +} +static void +vrrp_gnotify_fault_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->script_fault = set_value(strvec); + vgroup->notify_exec = 1; +} +static void +vrrp_gnotify_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->script = set_value(strvec); + vgroup->notify_exec = 1; +} +static void +vrrp_gsmtp_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->smtp_alert = 1; +} +static void +vrrp_gglobal_tracking_handler(vector_t *strvec) +{ + vrrp_sgroup *vgroup = LIST_TAIL_DATA(vrrp_data->vrrp_sync_group); + vgroup->global_tracking = 1; +} +static void +vrrp_handler(vector_t *strvec) +{ + alloc_vrrp(vector_slot(strvec, 1)); +} +static void +vrrp_vmac_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->vmac = 1; + if (!vrrp->mcast_saddr) + vrrp->mcast_saddr = IF_ADDR(vrrp->ifp); + if (vector_size(strvec) == 2) { + strncpy(vrrp->vmac_ifname, vector_slot(strvec, 1), + IFNAMSIZ - 1); + } else if (vrrp->vrid) { + snprintf(vrrp->vmac_ifname, IFNAMSIZ, "vrrp.%d", vrrp->vrid); + } + + if (strlen(vrrp->vmac_ifname)) { + log_message(LOG_INFO, "vmac_ifname=%s for vrrp_instace %s" + , vrrp->vmac_ifname + , vrrp->iname); + } + if (vrrp->ifp && !(vrrp->vmac & 2)) + netlink_link_add_vmac(vrrp); +} +static void +vrrp_native_ipv6_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->family = AF_INET6; + + if (vrrp->auth_type != VRRP_AUTH_NONE) + vrrp->auth_type = VRRP_AUTH_NONE; +} +static void +vrrp_state_handler(vector_t *strvec) +{ + char *str = vector_slot(strvec, 1); + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp_sgroup *vgroup = vrrp->sync; + + if (!strcmp(str, "MASTER")) { + vrrp->wantstate = VRRP_STATE_MAST; + vrrp->init_state = VRRP_STATE_MAST; + } + + /* set eventual sync group */ + if (vgroup) + vgroup->state = vrrp->wantstate; +} +static void +vrrp_int_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + char *name = vector_slot(strvec, 1); + vrrp->ifp = if_get_by_ifname(name); + if (vrrp->vmac && !(vrrp->vmac & 2)) + netlink_link_add_vmac(vrrp); +} +static void +vrrp_track_int_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_vrrp_track); +} +static void +vrrp_track_scr_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_vrrp_track_script); +} +static void +vrrp_dont_track_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->dont_track_primary = 1; +} +static void +vrrp_mcastip_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + inet_ston(vector_slot(strvec, 1), &vrrp->mcast_saddr); +} +static void +vrrp_vrid_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->vrid = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_VID(vrrp->vrid)) { + log_message(LOG_INFO, "VRRP Error : VRID not valid !"); + log_message(LOG_INFO, + " must be between 1 & 255. reconfigure !"); + } else { + alloc_vrrp_bucket(vrrp); + if (vrrp->vmac && strlen(vrrp->vmac_ifname) == 0) { + snprintf(vrrp->vmac_ifname, IFNAMSIZ, "vrrp.%d" + , vrrp->vrid); + log_message(LOG_INFO, "vmac_ifname=%s for vrrp_instace %s" + , vrrp->vmac_ifname + , vrrp->iname); + } + } +} +static void +vrrp_prio_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->effective_priority = vrrp->base_priority = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_PRIORITY(vrrp->base_priority)) { + log_message(LOG_INFO, "VRRP Error : Priority not valid !"); + log_message(LOG_INFO, + " must be between 1 & 255. reconfigure !"); + log_message(LOG_INFO, + " Using default value : %d\n", VRRP_PRIO_DFL); + vrrp->effective_priority = vrrp->base_priority = VRRP_PRIO_DFL; + } +} +static void +vrrp_adv_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->adver_int = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_ADVERT_INT(vrrp->adver_int)) { + log_message(LOG_INFO, "VRRP Error : Advert interval not valid !"); + log_message(LOG_INFO, + " must be between less than 1sec."); + log_message(LOG_INFO, " Using default value : 1sec"); + vrrp->adver_int = 1; + } + vrrp->adver_int *= TIMER_HZ; +} +static void +vrrp_debug_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->debug = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_DEBUG_INT(vrrp->debug)) { + log_message(LOG_INFO, "VRRP Error : Debug interval not valid !"); + log_message(LOG_INFO, " must be between 0-4"); + vrrp->debug = 0; + } +} +static void +vrrp_nopreempt_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->nopreempt = 1; +} +static void /* backwards compatibility */ +vrrp_preempt_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->nopreempt = 0; +} +static void +vrrp_preempt_delay_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->preempt_delay = atoi(vector_slot(strvec, 1)); + + if (VRRP_IS_BAD_PREEMPT_DELAY(vrrp->preempt_delay)) { + log_message(LOG_INFO, "VRRP Error : Preempt_delay not valid !"); + log_message(LOG_INFO, " must be between 0-%d", + TIMER_MAX_SEC); + vrrp->preempt_delay = 0; + } + vrrp->preempt_delay *= TIMER_HZ; + vrrp->preempt_time = timer_add_long(timer_now(), vrrp->preempt_delay); +} +static void +vrrp_notify_backup_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script_backup = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_notify_master_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script_master = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_notify_fault_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script_fault = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_notify_stop_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script_stop = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_notify_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->script = set_value(strvec); + vrrp->notify_exec = 1; +} +static void +vrrp_smtp_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->smtp_alert = 1; +} +static void +vrrp_lvs_syncd_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->lvs_syncd_if = set_value(strvec); +} +static void +vrrp_garp_delay_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + vrrp->garp_delay = atoi(vector_slot(strvec, 1)) * TIMER_HZ; + if (vrrp->garp_delay < TIMER_HZ) + vrrp->garp_delay = TIMER_HZ; +} +static void +vrrp_auth_type_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + char *str = vector_slot(strvec, 1); + + if (!strcmp(str, "AH") && vrrp->family == AF_INET) + vrrp->auth_type = VRRP_AUTH_AH; + else if (!strcmp(str, "PASS") && vrrp->family == AF_INET) + vrrp->auth_type = VRRP_AUTH_PASS; +} +static void +vrrp_auth_pass_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + char *str = vector_slot(strvec, 1); + int max_size = sizeof (vrrp->auth_data); + int str_len = strlen(str); + + if (str_len > max_size) { + str_len = max_size; + log_message(LOG_INFO, + "Truncating auth_pass to %d characters", max_size); + } + + memset(vrrp->auth_data, 0, max_size); + memcpy(vrrp->auth_data, str, str_len); +} +static void +vrrp_vip_handler(vector_t *strvec) +{ + vrrp_rt *vrrp = LIST_TAIL_DATA(vrrp_data->vrrp); + char *buf; + char *str = NULL; + vector_t *vec = NULL; + int nbvip = 0; + + buf = (char *) MALLOC(MAXBUF); + while (read_line(buf, MAXBUF)) { + vec = alloc_strvec(buf); + if (vec) { + str = vector_slot(vec, 0); + if (!strcmp(str, EOB)) { + free_strvec(vec); + break; + } + + if (vector_size(vec)) { + nbvip++; + if (nbvip > VRRP_MAX_VIP) { + log_message(LOG_INFO, + "VRRP_Instance(%s) " + "trunc to the first %d VIPs.", + vrrp->iname, VRRP_MAX_VIP); + log_message(LOG_INFO, + " => Declare others VIPs into" + " the excluded vip block"); + } else + alloc_vrrp_vip(vec); + } + + free_strvec(vec); + } + memset(buf, 0, MAXBUF); + } + FREE(buf); +} +static void +vrrp_evip_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_vrrp_evip); +} +static void +vrrp_vroutes_handler(vector_t *strvec) +{ + alloc_value_block(strvec, alloc_vrrp_vroute); +} +static void +vrrp_script_handler(vector_t *strvec) +{ + alloc_vrrp_script(vector_slot(strvec, 1)); +} +static void +vrrp_vscript_script_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->script = set_value(strvec); +} +static void +vrrp_vscript_interval_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->interval = atoi(vector_slot(strvec, 1)) * TIMER_HZ; + if (vscript->interval < TIMER_HZ) + vscript->interval = TIMER_HZ; +} +static void +vrrp_vscript_timeout_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->timeout = atoi(vector_slot(strvec, 1)) * TIMER_HZ; + if (vscript->timeout < TIMER_HZ) + vscript->timeout = TIMER_HZ; +} +static void +vrrp_vscript_weight_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->weight = atoi(vector_slot(strvec, 1)); +} +static void +vrrp_vscript_rise_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->rise = atoi(vector_slot(strvec, 1)); + if (vscript->rise < 1) + vscript->rise = 1; +} +static void +vrrp_vscript_fall_handler(vector_t *strvec) +{ + vrrp_script *vscript = LIST_TAIL_DATA(vrrp_data->vrrp_script); + vscript->fall = atoi(vector_slot(strvec, 1)); + if (vscript->fall < 1) + vscript->fall = 1; +} + +vector_t * +vrrp_init_keywords(void) +{ + /* global definitions mapping */ + global_init_keywords(); + + /* Static routes mapping */ + install_keyword_root("static_ipaddress", &static_addresses_handler); + install_keyword_root("static_routes", &static_routes_handler); + + /* VRRP Instance mapping */ + install_keyword_root("vrrp_sync_group", &vrrp_sync_group_handler); + install_keyword("group", &vrrp_group_handler); + install_keyword("notify_backup", &vrrp_gnotify_backup_handler); + install_keyword("notify_master", &vrrp_gnotify_master_handler); + install_keyword("notify_fault", &vrrp_gnotify_fault_handler); + install_keyword("notify", &vrrp_gnotify_handler); + install_keyword("smtp_alert", &vrrp_gsmtp_handler); + install_keyword("global_tracking", &vrrp_gglobal_tracking_handler); + install_keyword_root("vrrp_instance", &vrrp_handler); + install_keyword("use_vmac", &vrrp_vmac_handler); + install_keyword("native_ipv6", &vrrp_native_ipv6_handler); + install_keyword("state", &vrrp_state_handler); + install_keyword("interface", &vrrp_int_handler); + install_keyword("dont_track_primary", &vrrp_dont_track_handler); + install_keyword("track_interface", &vrrp_track_int_handler); + install_keyword("track_script", &vrrp_track_scr_handler); + install_keyword("mcast_src_ip", &vrrp_mcastip_handler); + install_keyword("vrrp_unicast_bind", &vrrp_unicast_bind_handler); + install_keyword("vrrp_unicast_peer", &vrrp_unicast_peer_handler); + install_keyword("virtual_router_id", &vrrp_vrid_handler); + install_keyword("priority", &vrrp_prio_handler); + install_keyword("advert_int", &vrrp_adv_handler); + install_keyword("virtual_ipaddress", &vrrp_vip_handler); + install_keyword("virtual_ipaddress_excluded", &vrrp_evip_handler); + install_keyword("virtual_routes", &vrrp_vroutes_handler); + install_keyword("preempt", &vrrp_preempt_handler); + install_keyword("nopreempt", &vrrp_nopreempt_handler); + install_keyword("preempt_delay", &vrrp_preempt_delay_handler); + install_keyword("debug", &vrrp_debug_handler); + install_keyword("notify_backup", &vrrp_notify_backup_handler); + install_keyword("notify_master", &vrrp_notify_master_handler); + install_keyword("notify_fault", &vrrp_notify_fault_handler); + install_keyword("notify_stop", &vrrp_notify_stop_handler); + install_keyword("notify", &vrrp_notify_handler); + install_keyword("smtp_alert", &vrrp_smtp_handler); + install_keyword("lvs_sync_daemon_interface", &vrrp_lvs_syncd_handler); + install_keyword("garp_master_delay", &vrrp_garp_delay_handler); + install_keyword("authentication", NULL); + install_sublevel(); + install_keyword("auth_type", &vrrp_auth_type_handler); + install_keyword("auth_pass", &vrrp_auth_pass_handler); + install_sublevel_end(); + install_keyword_root("vrrp_script", &vrrp_script_handler); + install_keyword("script", &vrrp_vscript_script_handler); + install_keyword("interval", &vrrp_vscript_interval_handler); + install_keyword("timeout", &vrrp_vscript_timeout_handler); + install_keyword("weight", &vrrp_vscript_weight_handler); + install_keyword("rise", &vrrp_vscript_rise_handler); + install_keyword("fall", &vrrp_vscript_fall_handler); + + return keywords; +} diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_scheduler.c keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_scheduler.c --- keepalived-1.2.7/keepalived/vrrp/vrrp_scheduler.c 2012-08-29 01:18:29.000000000 +0200 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_scheduler.c 2013-05-24 09:39:17.645833100 +0200 @@ -468,12 +468,12 @@ vrrp_open_sockpool(list l) for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { sock = ELEMENT_DATA(e); sock->fd_in = open_vrrp_socket(sock->family, sock->proto, - sock->ifindex); + sock->ifindex, 0); if (sock->fd_in == -1) sock->fd_out = -1; else sock->fd_out = open_vrrp_send_socket(sock->family, sock->proto, - sock->ifindex); + sock->ifindex, 0); } } diff -rupN keepalived-1.2.7/keepalived/vrrp/vrrp_scheduler.c.orig keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_scheduler.c.orig --- keepalived-1.2.7/keepalived/vrrp/vrrp_scheduler.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ keepalived-1.2.7-unicast/keepalived/vrrp/vrrp_scheduler.c.orig 2012-08-29 01:18:29.000000000 +0200 @@ -0,0 +1,1064 @@ +/* + * Soft: Keepalived is a failover program for the LVS project + * <www.linuxvirtualserver.org>. It monitor & manipulate + * a loadbalanced server pool using multi-layer checks. + * + * Part: Sheduling framework for vrrp code. + * + * Author: Alexandre Cassen, <acassen@linux-vs.org> + * + * 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. + * + * 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. + * + * Copyright (C) 2001-2012 Alexandre Cassen, <acassen@gmail.com> + */ + +#include "vrrp_scheduler.h" +#include "vrrp_ipsecah.h" +#include "vrrp_if.h" +#include "vrrp.h" +#include "vrrp_sync.h" +#include "vrrp_notify.h" +#include "vrrp_netlink.h" +#include "vrrp_data.h" +#include "vrrp_index.h" +#include "ipvswrapper.h" +#include "memory.h" +#include "notify.h" +#include "list.h" +#include "logger.h" +#include "main.h" +#include "smtp.h" +#include "signals.h" +#ifdef _WITH_SNMP_ +#include "vrrp_snmp.h" +#endif + +/* VRRP FSM (Finite State Machine) design. + * + * The state transition diagram implemented is : + * + * +---------------+ + * +----------------| |----------------+ + * | | Fault | | + * | +------------>| |<------------+ | + * | | +---------------+ | | + * | | | | | + * | | V | | + * | | +---------------+ | | + * | | +--------->| |<---------+ | | + * | | | | Initialize | | | | + * | | | +-------| |-------+ | | | + * | | | | +---------------+ | | | | + * | | | | | | | | + * V | | V V | | V + * +---------------+ +---------------+ + * | |---------------------->| | + * | Master | | Backup | + * | |<----------------------| | + * +---------------+ +---------------+ + */ +static void vrrp_backup(vrrp_rt *, char *, int); +static void vrrp_leave_master(vrrp_rt *, char *, int); +static void vrrp_leave_fault(vrrp_rt *, char *, int); +static void vrrp_become_master(vrrp_rt *, char *, int); + +static void vrrp_goto_master(vrrp_rt *); +static void vrrp_master(vrrp_rt *); +static void vrrp_fault(vrrp_rt *); + +static int vrrp_update_priority(thread_t * thread); +static int vrrp_script_child_timeout_thread(thread_t * thread); +static int vrrp_script_child_thread(thread_t * thread); +static int vrrp_script_thread(thread_t * thread); + +struct { + void (*read) (vrrp_rt *, char *, int); + void (*read_to) (vrrp_rt *); +} VRRP_FSM[VRRP_MAX_FSM_STATE + 1] = +{ +/* Stream Read Handlers | Stream Read_to handlers * + *------------------------------+------------------------------*/ + {NULL, NULL}, + {vrrp_backup, vrrp_goto_master}, /* BACKUP */ + {vrrp_leave_master, vrrp_master}, /* MASTER */ + {vrrp_leave_fault, vrrp_fault}, /* FAULT */ + {vrrp_become_master, vrrp_goto_master} /* GOTO_MASTER */ +}; + +/* VRRP TSM (Transition State Matrix) design. + * + * Introducing the Synchronization extension to VRRP + * protocol, introduce the need for a transition machinery. + * This mecanism can be designed using a diagonal matrix. + * We call this matrix the VRRP TSM: + * + * \ E | B | M | F | + * S \ | | | | + * ------+-----+-----+-----+ Legend: + * B | x 1 2 | B: VRRP BACKUP state + * ------+ | M: VRRP MASTER state + * M | 3 x 4 | F: VRRP FAULT state + * ------+ | S: VRRP start state (before transition) + * F | 5 6 x | E: VRRP end state (after transition) + * ------+-----------------+ [1..6]: Handler functions. + * + * So we have have to implement n(n-1) handlers in order to deal with + * all transitions possible. This matrix defines the maximum handlers + * to implement for having the most time optimized transition machine. + * For example: + * . The handler (1) will sync all the BACKUP VRRP instances of a + * group to MASTER state => we will call it vrrp_sync_master. + * .... and so on for all other state .... + * + * This matrix is the strict implementation way. For readability and + * performance we have implemented some handlers directly into the VRRP + * FSM. For instance the handlers (5) & (6) are directly into the VRRP + * FSM since it will speed up convergence to init state. + * Additionnaly, we have implemented some other handlers into the matrix + * in order to speed up group synchronization takeover. For instance + * transitions : + * o B->B: To catch wantstate MASTER transition to force sync group + * to this transition state too. + * o F->F: To speed up FAULT state transition if group is not already + * synced to FAULT state. + */ +struct { + void (*handler) (vrrp_rt *); +} VRRP_TSM[VRRP_MAX_TSM_STATE + 1][VRRP_MAX_TSM_STATE + 1] = +{ + { {NULL}, {NULL}, {NULL}, {NULL} }, + { {NULL}, {vrrp_sync_master_election}, {vrrp_sync_master}, {vrrp_sync_fault} }, + { {NULL}, {vrrp_sync_backup}, {vrrp_sync_master}, {vrrp_sync_fault} }, + { {NULL}, {vrrp_sync_backup}, {vrrp_sync_master}, {vrrp_sync_fault} } +}; + +/* SMTP alert notifier */ +static void +vrrp_smtp_notifier(vrrp_rt * vrrp) +{ + if (vrrp->smtp_alert) { + if (vrrp->state == VRRP_STATE_MAST) + smtp_alert(NULL, vrrp, NULL, + "Entering MASTER state", + "=> VRRP Instance is now owning VRRP VIPs <="); + if (vrrp->state == VRRP_STATE_BACK) + smtp_alert(NULL, vrrp, NULL, + "Entering BACKUP state", + "=> VRRP Instance is nolonger owning VRRP VIPs <="); + } +} + +/* Log interface message */ +static void vrrp_log_int_down(vrrp_rt *vrrp) +{ + if (!IF_ISUP(vrrp->ifp)) + log_message(LOG_INFO, "Kernel is reporting: interface %s DOWN", + IF_NAME(vrrp->ifp)); + if (!LIST_ISEMPTY(vrrp->track_ifp)) + vrrp_log_tracked_down(vrrp->track_ifp); +} + +static void vrrp_log_int_up(vrrp_rt *vrrp) +{ + if (IF_ISUP(vrrp->ifp)) + log_message(LOG_INFO, "Kernel is reporting: interface %s UP", + IF_NAME(vrrp->ifp)); + if (!LIST_ISEMPTY(vrrp->track_ifp)) + log_message(LOG_INFO, "Kernel is reporting: tracked interface are UP"); +} + +/* + * Initialize state handling + * --rfc2338.6.4.1 + */ +static void +vrrp_init_state(list l) +{ + vrrp_rt *vrrp; + vrrp_sgroup *vgroup; + element e; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + + /* In case of VRRP SYNC, we have to carefully check that we are + * not running floating priorities on any VRRP instance. + */ + if (vrrp->sync && !vrrp->sync->global_tracking) { + element e2; + tracked_sc *sc; + tracked_if *tip; + int warning = 0; + + if (!LIST_ISEMPTY(vrrp->track_ifp)) { + for (e2 = LIST_HEAD(vrrp->track_ifp); e2; ELEMENT_NEXT(e2)) { + tip = ELEMENT_DATA(e2); + if (tip->weight) { + tip->weight = 0; + warning++; + } + } + } + + if (!LIST_ISEMPTY(vrrp->track_script)) { + for (e2 = LIST_HEAD(vrrp->track_script); e2; ELEMENT_NEXT(e2)) { + sc = ELEMENT_DATA(e2); + if (sc->weight) { + sc->scr->inuse--; + warning++; + } + } + } + + if (warning > 0) { + log_message(LOG_INFO, "VRRP_Instance(%s) : ignoring " + "tracked script with weights due to SYNC group", + vrrp->iname); + } + } else { + /* Register new priority update thread */ + thread_add_timer(master, vrrp_update_priority, + vrrp, vrrp->adver_int); + } + + if (vrrp->base_priority == VRRP_PRIO_OWNER || + vrrp->wantstate == VRRP_STATE_MAST) { +#ifdef _HAVE_IPVS_SYNCD_ + /* Check if sync daemon handling is needed */ + if (vrrp->lvs_syncd_if) + ipvs_syncd_cmd(IPVS_STARTDAEMON, + vrrp->lvs_syncd_if, IPVS_MASTER, + vrrp->vrid); +#endif + vrrp->state = VRRP_STATE_GOTO_MASTER; + } else { + vrrp->ms_down_timer = 3 * vrrp->adver_int + + VRRP_TIMER_SKEW(vrrp); +#ifdef _HAVE_IPVS_SYNCD_ + /* Check if sync daemon handling is needed */ + if (vrrp->lvs_syncd_if) + ipvs_syncd_cmd(IPVS_STARTDAEMON, + vrrp->lvs_syncd_if, IPVS_BACKUP, + vrrp->vrid); +#endif + log_message(LOG_INFO, "VRRP_Instance(%s) Entering BACKUP STATE", + vrrp->iname); + + /* Set BACKUP state */ + vrrp_restore_interface(vrrp, 0); + vrrp->state = VRRP_STATE_BACK; + vrrp_smtp_notifier(vrrp); + notify_instance_exec(vrrp, VRRP_STATE_BACK); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + + /* Init group if needed */ + if ((vgroup = vrrp->sync)) { + if (GROUP_STATE(vgroup) != VRRP_STATE_BACK) { + vgroup->state = VRRP_STATE_BACK; + vrrp_sync_smtp_notifier(vgroup); + notify_group_exec(vgroup, VRRP_STATE_BACK); +#ifdef _WITH_SNMP_ + vrrp_snmp_group_trap(vgroup); +#endif + } + } + } + } +} + +static void +vrrp_init_sands(list l) +{ + vrrp_rt *vrrp; + element e; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + vrrp_init_instance_sands(vrrp); + } +} + +/* if run after vrrp_init_state(), it will be able to detect scripts that + * have been disabled because of a sync group and will avoid to start them. + */ +static void +vrrp_init_script(list l) +{ + vrrp_script *vscript; + element e; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vscript = ELEMENT_DATA(e); + if (vscript->inuse == 0) + vscript->result = VRRP_SCRIPT_STATUS_DISABLED; + + if (vscript->result == VRRP_SCRIPT_STATUS_INIT) { + vscript->result = vscript->rise - 1; /* one success is enough */ + thread_add_event(master, vrrp_script_thread, vscript, vscript->interval); + } else if (vscript->result == VRRP_SCRIPT_STATUS_INIT_GOOD) { + vscript->result = vscript->rise; /* one failure is enough */ + thread_add_event(master, vrrp_script_thread, vscript, vscript->interval); + } + } +} + +/* Timer functions */ +static timeval_t +vrrp_compute_timer(const int fd) +{ + vrrp_rt *vrrp; + element e; + list l = &vrrp_data->vrrp_index_fd[fd%1024 + 1]; + timeval_t timer; + + /* Multiple instances on the same interface */ + timer_reset(timer); + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + if (timer_cmp(vrrp->sands, timer) < 0 || + timer_isnull(timer)) + timer = timer_dup(vrrp->sands); + } + + return timer; +} + +static long +vrrp_timer_fd(const int fd) +{ + timeval_t timer, vrrp_timer; + long vrrp_long; + + timer = vrrp_compute_timer(fd); + vrrp_timer = timer_sub(timer, time_now); + vrrp_long = timer_long(vrrp_timer); + + return (vrrp_long < 0) ? TIMER_MAX_SEC : vrrp_long; +} + +static int +vrrp_timer_vrid_timeout(const int fd) +{ + vrrp_rt *vrrp; + element e; + list l = &vrrp_data->vrrp_index_fd[fd%1024 + 1]; + timeval_t timer; + int vrid = 0; + + /* Multiple instances on the same interface */ + timer_reset(timer); + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + if (timer_cmp(vrrp->sands, timer) < 0 || + timer_isnull(timer)) { + timer = timer_dup(vrrp->sands); + vrid = vrrp->vrid; + } + } + return vrid; +} + +/* Thread functions */ +static void +vrrp_register_workers(list l) +{ + sock_t *sock; + timeval_t timer; + long vrrp_timer = 0; + element e; + + /* Init compute timer */ + memset(&timer, 0, sizeof (struct timeval)); + + /* Init the VRRP instances state */ + vrrp_init_state(vrrp_data->vrrp); + + /* Init VRRP instances sands */ + vrrp_init_sands(vrrp_data->vrrp); + + /* Init VRRP tracking scripts */ + if (!LIST_ISEMPTY(vrrp_data->vrrp_script)) + vrrp_init_script(vrrp_data->vrrp_script); + + /* Register VRRP workers threads */ + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + sock = ELEMENT_DATA(e); + /* jump to asynchronous handling */ + vrrp_timer = vrrp_timer_fd(sock->fd_in); + + /* Register a timer thread if interface is shut */ + if (sock->fd_in == -1) + thread_add_timer(master, vrrp_read_dispatcher_thread, + sock, vrrp_timer); + else + thread_add_read(master, vrrp_read_dispatcher_thread, + sock, sock->fd_in, vrrp_timer); + } +} + +/* VRRP dispatcher functions */ +static int +already_exist_sock(list l, sa_family_t family, int proto, int ifindex) +{ + sock_t *sock; + element e; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + sock = ELEMENT_DATA(e); + if ((sock->family == family) && + (sock->proto == proto) && + (sock->ifindex == ifindex)) + return 1; + } + return 0; +} + +void +alloc_sock(sa_family_t family, list l, int proto, int ifindex) +{ + sock_t *new; + + new = (sock_t *) MALLOC(sizeof (sock_t)); + new->family = family; + new->proto = proto; + new->ifindex = ifindex; + + list_add(l, new); +} + +static void +vrrp_create_sockpool(list l) +{ + vrrp_rt *vrrp; + list p = vrrp_data->vrrp; + element e; + int ifindex; + int proto; + + for (e = LIST_HEAD(p); e; ELEMENT_NEXT(e)) { + vrrp = ELEMENT_DATA(e); + ifindex = IF_INDEX(vrrp->ifp); + if (vrrp->auth_type == VRRP_AUTH_AH) + proto = IPPROTO_IPSEC_AH; + else + proto = IPPROTO_VRRP; + + /* add the vrrp element if not exist */ + if (!already_exist_sock(l, vrrp->family, proto, ifindex)) + alloc_sock(vrrp->family, l, proto, ifindex); + } +} + +static void +vrrp_open_sockpool(list l) +{ + sock_t *sock; + element e; + + for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { + sock = ELEMENT_DATA(e); + sock->fd_in = open_vrrp_socket(sock->family, sock->proto, + sock->ifindex); + if (sock->fd_in == -1) + sock->fd_out = -1; + else + sock->fd_out = open_vrrp_send_socket(sock->family, sock->proto, + sock->ifindex); + } +} + +static void +vrrp_set_fds(list l) +{ + sock_t *sock; + vrrp_rt *vrrp; + list p = vrrp_data->vrrp; + element e_sock; + element e_vrrp; + int proto; + + for (e_sock = LIST_HEAD(l); e_sock; ELEMENT_NEXT(e_sock)) { + sock = ELEMENT_DATA(e_sock); + for (e_vrrp = LIST_HEAD(p); e_vrrp; ELEMENT_NEXT(e_vrrp)) { + vrrp = ELEMENT_DATA(e_vrrp); + if (vrrp->auth_type == VRRP_AUTH_AH) + proto = IPPROTO_IPSEC_AH; + else + proto = IPPROTO_VRRP; + + if ((sock->ifindex == IF_INDEX(vrrp->ifp)) && + (sock->proto == proto)) { + vrrp->fd_in = sock->fd_in; + vrrp->fd_out = sock->fd_out; + + /* append to hash index */ + alloc_vrrp_fd_bucket(vrrp); + } + } + } +} + +/* + * We create & allocate a socket pool here. The soft design + * can be sum up by the following sketch : + * + * fd1 fd2 fd3 fd4 fdi fdi+1 + * -----\__/--------\__/---........---\__/--- + * | ETH0 | | ETH1 | | ETHn | + * +------+ +------+ +------+ + * + * Here we have n physical NIC. Each NIC own a maximum of 2 fds. + * (one for VRRP the other for IPSEC_AH). All our VRRP instances + * are multiplexed through this fds. So our design can handle 2*n + * multiplexing points. + */ +int +vrrp_dispatcher_init(thread_t * thread) +{ + /* create the VRRP socket pool list */ + vrrp_create_sockpool(vrrp_data->vrrp_socket_pool); + + /* open the VRRP socket pool */ + vrrp_open_sockpool(vrrp_data->vrrp_socket_pool); + + /* set VRRP instance fds to sockpool */ + vrrp_set_fds(vrrp_data->vrrp_socket_pool); + + /* register read dispatcher worker thread */ + vrrp_register_workers(vrrp_data->vrrp_socket_pool); + + /* Dump socket pool */ + if (debug & 32) + dump_list(vrrp_data->vrrp_socket_pool); + return 1; +} + +void +vrrp_dispatcher_release(vrrp_conf_data *conf_data) +{ + free_list(conf_data->vrrp_socket_pool); +} + +static void +vrrp_backup(vrrp_rt * vrrp, char *buffer, int len) +{ + struct iphdr *iph; + ipsec_ah *ah; + + if (vrrp->family == AF_INET) { + iph = (struct iphdr *) buffer; + + if (iph->protocol == IPPROTO_IPSEC_AH) { + ah = (ipsec_ah *) (buffer + sizeof (struct iphdr)); + if (ntohl(ah->seq_number) >= vrrp->ipsecah_counter->seq_number) + vrrp->ipsecah_counter->cycle = 0; + } + } + + vrrp_state_backup(vrrp, buffer, len); +} + +static void +vrrp_become_master(vrrp_rt * vrrp, char *buffer, int len) +{ + struct iphdr *iph; + ipsec_ah *ah; + + if (vrrp->family == AF_INET) { + iph = (struct iphdr *) buffer; + + /* + * If we are in IPSEC AH mode, we must be sync + * with the remote IPSEC AH VRRP instance counter. + */ + if (iph->protocol == IPPROTO_IPSEC_AH) { + log_message(LOG_INFO, "VRRP_Instance(%s) IPSEC-AH : seq_num sync", + vrrp->iname); + ah = (ipsec_ah *) (buffer + sizeof (struct iphdr)); + vrrp->ipsecah_counter->seq_number = ntohl(ah->seq_number) + 1; + vrrp->ipsecah_counter->cycle = 0; + } + } + + /* Then jump to master state */ + vrrp->wantstate = VRRP_STATE_MAST; + vrrp_state_goto_master(vrrp); +} + +static void +vrrp_leave_master(vrrp_rt * vrrp, char *buffer, int len) +{ + if (!VRRP_ISUP(vrrp)) { + vrrp_log_int_down(vrrp); + vrrp->wantstate = VRRP_STATE_GOTO_FAULT; + vrrp_state_leave_master(vrrp); + } else if (vrrp_state_master_rx(vrrp, buffer, len)) { + vrrp_state_leave_master(vrrp); + vrrp_smtp_notifier(vrrp); + } +} + +static void +vrrp_ah_sync(vrrp_rt *vrrp) +{ + /* + * Transition to BACKUP state for AH + * seq number synchronization. + */ + log_message(LOG_INFO, "VRRP_Instance(%s) in FAULT state jump to AH sync", + vrrp->iname); + vrrp->wantstate = VRRP_STATE_BACK; + vrrp_state_leave_master(vrrp); +} + +static void +vrrp_leave_fault(vrrp_rt * vrrp, char *buffer, int len) +{ + if (!VRRP_ISUP(vrrp)) + return; + + if (vrrp_state_fault_rx(vrrp, buffer, len)) { + if (vrrp->sync) { + if (vrrp_sync_leave_fault(vrrp)) { + log_message(LOG_INFO, + "VRRP_Instance(%s) prio is higher than received advert", + vrrp->iname); + vrrp_become_master(vrrp, buffer, len); + } + } else { + log_message(LOG_INFO, + "VRRP_Instance(%s) prio is higher than received advert", + vrrp->iname); + vrrp_become_master(vrrp, buffer, len); + } + } else { + if (vrrp->sync) { + if (vrrp_sync_leave_fault(vrrp)) { + log_message(LOG_INFO, "VRRP_Instance(%s) Entering BACKUP STATE", + vrrp->iname); + vrrp->state = VRRP_STATE_BACK; + vrrp_smtp_notifier(vrrp); + notify_instance_exec(vrrp, VRRP_STATE_BACK); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + } + } else { + log_message(LOG_INFO, "VRRP_Instance(%s) Entering BACKUP STATE", + vrrp->iname); + vrrp->state = VRRP_STATE_BACK; + vrrp_smtp_notifier(vrrp); + notify_instance_exec(vrrp, VRRP_STATE_BACK); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + } + } +} + +static void +vrrp_goto_master(vrrp_rt * vrrp) +{ + if (!VRRP_ISUP(vrrp)) { + vrrp_log_int_down(vrrp); + log_message(LOG_INFO, "VRRP_Instance(%s) Now in FAULT state", + vrrp->iname); + if (vrrp->state != VRRP_STATE_FAULT) + notify_instance_exec(vrrp, VRRP_STATE_FAULT); + vrrp->state = VRRP_STATE_FAULT; + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + notify_instance_exec(vrrp, VRRP_STATE_FAULT); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + } else { + /* If becoming MASTER in IPSEC AH AUTH, we reset the anti-replay */ + if (vrrp->ipsecah_counter->cycle) { + vrrp->ipsecah_counter->cycle = 0; + vrrp->ipsecah_counter->seq_number = 0; + } + + /* handle master state transition */ + vrrp->wantstate = VRRP_STATE_MAST; + vrrp_state_goto_master(vrrp); + } +} + +/* Delayed gratuitous ARP thread */ +int +vrrp_gratuitous_arp_thread(thread_t * thread) +{ + vrrp_rt *vrrp = THREAD_ARG(thread); + + /* Simply broadcast the gratuitous ARP */ + vrrp_send_link_update(vrrp); + + return 0; +} + +/* Update VRRP effective priority based on multiple checkers. + * This is a thread which is executed every adver_int. + */ +static int +vrrp_update_priority(thread_t * thread) +{ + vrrp_rt *vrrp = THREAD_ARG(thread); + int prio_offset, new_prio; + + /* compute prio_offset right here */ + prio_offset = 0; + + /* Now we will sum the weights of all interfaces which are tracked. */ + if ((!vrrp->sync || vrrp->sync->global_tracking) && !LIST_ISEMPTY(vrrp->track_ifp)) + prio_offset += vrrp_tracked_weight(vrrp->track_ifp); + + /* Now we will sum the weights of all scripts which are tracked. */ + if ((!vrrp->sync || vrrp->sync->global_tracking) && !LIST_ISEMPTY(vrrp->track_script)) + prio_offset += vrrp_script_weight(vrrp->track_script); + + if (vrrp->base_priority == VRRP_PRIO_OWNER) { + /* we will not run a PRIO_OWNER into a non-PRIO_OWNER */ + vrrp->effective_priority = VRRP_PRIO_OWNER; + } else { + /* WARNING! we must compute new_prio on a signed int in order + to detect overflows and avoid wrapping. */ + new_prio = vrrp->base_priority + prio_offset; + if (new_prio < 1) + new_prio = 1; + else if (new_prio > 254) + new_prio = 254; + vrrp->effective_priority = new_prio; + } + + /* Register next priority update thread */ + thread_add_timer(master, vrrp_update_priority, vrrp, vrrp->adver_int); + return 0; +} + +static void +vrrp_master(vrrp_rt * vrrp) +{ + /* Check if interface we are running on is UP */ + if (vrrp->wantstate != VRRP_STATE_GOTO_FAULT) { + if (!VRRP_ISUP(vrrp)) { + vrrp_log_int_down(vrrp); + vrrp->wantstate = VRRP_STATE_GOTO_FAULT; + } + } + + /* Then perform the state transition */ + if (vrrp->wantstate == VRRP_STATE_GOTO_FAULT || + vrrp->wantstate == VRRP_STATE_BACK || + vrrp->ipsecah_counter->cycle) { + vrrp->ms_down_timer = 3 * vrrp->adver_int + VRRP_TIMER_SKEW(vrrp); + + /* handle backup state transition */ + vrrp_state_leave_master(vrrp); + + if (vrrp->state == VRRP_STATE_BACK) + log_message(LOG_INFO, "VRRP_Instance(%s) Now in BACKUP state", + vrrp->iname); + if (vrrp->state == VRRP_STATE_FAULT) + log_message(LOG_INFO, "VRRP_Instance(%s) Now in FAULT state", + vrrp->iname); + } else if (vrrp->state == VRRP_STATE_MAST) { + /* + * Send the VRRP advert. + * If we catch the master transition + * <=> vrrp_state_master_tx(...) = 1 + * register a gratuitous arp thread delayed to 5 secs. + */ + if (vrrp_state_master_tx(vrrp, 0)) { + thread_add_timer(master, vrrp_gratuitous_arp_thread, + vrrp, + (vrrp->garp_delay) ? + vrrp->garp_delay : VRRP_GARP_DELAY); + vrrp_smtp_notifier(vrrp); + } + } +} + +static void +vrrp_fault(vrrp_rt * vrrp) +{ + vrrp_sgroup *vgroup = vrrp->sync; + + if (vgroup) { + if (!vrrp_sync_leave_fault(vrrp)) + return; + } else if (VRRP_ISUP(vrrp)) + vrrp_log_int_up(vrrp); + else + return; + + /* refresh the multicast fd */ + if (new_vrrp_socket(vrrp) < 0) + return; + + /* + * We force the IPSEC AH seq_number sync + * to be done in read advert handler. + * So we ignore this timeouted state until remote + * VRRP MASTER send its advert for the concerned + * instance. + */ + if (vrrp->auth_type == VRRP_AUTH_AH) { + vrrp_ah_sync(vrrp); + } else { + /* Otherwise, we transit to init state */ + if (vrrp->init_state == VRRP_STATE_BACK) { + vrrp->state = VRRP_STATE_BACK; + notify_instance_exec(vrrp, VRRP_STATE_BACK); +#ifdef _WITH_SNMP_ + vrrp_snmp_instance_trap(vrrp); +#endif + } else { + vrrp_goto_master(vrrp); + } + } +} + +/* Handle dispatcher read timeout */ +static int +vrrp_dispatcher_read_to(int fd) +{ + vrrp_rt *vrrp; + int vrid = 0; + int prev_state = 0; + + /* Searching for matching instance */ + vrid = vrrp_timer_vrid_timeout(fd); + vrrp = vrrp_index_lookup(vrid, fd); + + /* Run the FSM handler */ + prev_state = vrrp->state; + VRRP_FSM_READ_TO(vrrp); + + /* handle instance synchronization */ +// printf("Send [%s] TSM transtition : [%d,%d] Wantstate = [%d]\n" +// , vrrp->iname +// , prev_state +// , vrrp->state +// , vrrp->wantstate); + VRRP_TSM_HANDLE(prev_state, vrrp); + + /* + * We are sure the instance exist. So we can + * compute new sands timer safely. + */ + vrrp_init_instance_sands(vrrp); + return vrrp->fd_in; +} + +/* Handle dispatcher read packet */ +static int +vrrp_dispatcher_read(sock_t * sock) +{ + vrrp_rt *vrrp; + vrrp_pkt *hd; + int len = 0, prev_state = 0, proto = 0; + uint32_t saddr; + + /* Clean the read buffer */ + memset(vrrp_buffer, 0, VRRP_PACKET_TEMP_LEN); + + /* read & affect received buffer */ + len = read(sock->fd_in, vrrp_buffer, VRRP_PACKET_TEMP_LEN); + hd = vrrp_get_header(sock->family, vrrp_buffer, &proto, &saddr); + + /* Searching for matching instance */ + vrrp = vrrp_index_lookup(hd->vrid, sock->fd_in); + + /* If no instance found => ignore the advert */ + if (!vrrp) + return sock->fd_in; + + /* Run the FSM handler */ + prev_state = vrrp->state; + VRRP_FSM_READ(vrrp, vrrp_buffer, len); + + /* handle instance synchronization */ +// printf("Read [%s] TSM transtition : [%d,%d] Wantstate = [%d]\n" +// , vrrp->iname +// , prev_state +// , vrrp->state +// , vrrp->wantstate); + VRRP_TSM_HANDLE(prev_state, vrrp); + + /* + * Refresh sands only if found matching instance. + * Otherwize the packet is simply ignored... + */ + vrrp_init_instance_sands(vrrp); + + return sock->fd_in; +} + +/* Our read packet dispatcher */ +int +vrrp_read_dispatcher_thread(thread_t * thread) +{ + long vrrp_timer = 0; + sock_t *sock; + int fd; + + /* Fetch thread arg */ + sock = THREAD_ARG(thread); + + /* Dispatcher state handler */ + if (thread->type == THREAD_READ_TIMEOUT || sock->fd_in == -1) + fd = vrrp_dispatcher_read_to(sock->fd_in); + else + fd = vrrp_dispatcher_read(sock); + + /* register next dispatcher thread */ + vrrp_timer = vrrp_timer_fd(fd); + if (fd == -1) + thread_add_timer(thread->master, vrrp_read_dispatcher_thread, + sock, vrrp_timer); + else + thread_add_read(thread->master, vrrp_read_dispatcher_thread, + sock, fd, vrrp_timer); + + return 0; +} + +/* Script tracking threads */ +static int +vrrp_script_thread(thread_t * thread) +{ + vrrp_script *vscript = THREAD_ARG(thread); + int status, ret; + pid_t pid; + + /* Register next timer tracker */ + thread_add_timer(thread->master, vrrp_script_thread, vscript, + vscript->interval); + + /* Daemonization to not degrade our scheduling timer */ + pid = fork(); + + /* In case of fork is error. */ + if (pid < 0) { + log_message(LOG_INFO, "Failed fork process"); + return -1; + } + + /* In case of this is parent process */ + if (pid) { + thread_add_child(thread->master, vrrp_script_child_thread, + vscript, pid, + (vscript->timeout) ? vscript->timeout : vscript->interval); + return 0; + } + + /* Child part */ + signal_handler_destroy(); + closeall(0); + open("/dev/null", O_RDWR); + ret = dup(0); + if (ret < 0) { + log_message(LOG_INFO, "dup(0) error"); + } + + ret = dup(0); + if (ret < 0) { + log_message(LOG_INFO, "dup(0) error"); + } + + status = system_call(vscript->script); + + if (status < 0 || !WIFEXITED(status)) + status = 0; /* Script errors aren't server errors */ + else + status = WEXITSTATUS(status); + + exit(status); +} + +static int +vrrp_script_child_thread(thread_t * thread) +{ + int wait_status; + vrrp_script *vscript = THREAD_ARG(thread); + + if (thread->type == THREAD_CHILD_TIMEOUT) { + pid_t pid; + + pid = THREAD_CHILD_PID(thread); + + /* The child hasn't responded. Kill it off. */ + if (vscript->result > vscript->rise) { + vscript->result--; + } else { + if (vscript->result == vscript->rise) + log_message(LOG_INFO, "VRRP_Script(%s) timed out", vscript->sname); + vscript->result = 0; + } + kill(pid, SIGTERM); + thread_add_child(thread->master, vrrp_script_child_timeout_thread, + vscript, pid, 2); + return 0; + } + + wait_status = THREAD_CHILD_STATUS(thread); + + if (WIFEXITED(wait_status)) { + int status; + status = WEXITSTATUS(wait_status); + if (status == 0) { + /* success */ + if (vscript->result < vscript->rise - 1) { + vscript->result++; + } else { + if (vscript->result < vscript->rise) + log_message(LOG_INFO, "VRRP_Script(%s) succeeded", vscript->sname); + vscript->result = vscript->rise + vscript->fall - 1; + } + } else { + /* failure */ + if (vscript->result > vscript->rise) { + vscript->result--; + } else { + if (vscript->result >= vscript->rise) + log_message(LOG_INFO, "VRRP_Script(%s) failed", vscript->sname); + vscript->result = 0; + } + } + } + + return 0; +} + +static int +vrrp_script_child_timeout_thread(thread_t * thread) +{ + pid_t pid; + + if (thread->type != THREAD_CHILD_TIMEOUT) + return 0; + + /* OK, it still hasn't exited. Now really kill it off. */ + pid = THREAD_CHILD_PID(thread); + if (kill(pid, SIGKILL) < 0) { + /* Its possible it finished while we're handing this */ + if (errno != ESRCH) + DBG("kill error: %s", strerror(errno)); + return 0; + } + + log_message(LOG_WARNING, "Process [%d] didn't respond to SIGTERM", pid); + waitpid(pid, NULL, 0); + + return 0; +}