1 /* $OpenBSD: carp.c,v 1.8 2006/11/28 19:21:15 reyk Exp $ */ 2 3 /* 4 * Copyright (c) 2005 H�kan Olsson. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * This code was written under funding by Multicom Security AB. 30 */ 31 32 33 #include <sys/types.h> 34 #include <sys/ioctl.h> 35 #include <sys/socket.h> 36 #include <net/if.h> 37 #include <net/route.h> 38 39 #include <errno.h> 40 #include <stdio.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 #include "monitor.h" 45 #include "sasyncd.h" 46 47 int carp_demoted = 0; 48 49 static enum RUNSTATE 50 carp_map_state(u_char link_state) 51 { 52 enum RUNSTATE state = FAIL; 53 54 switch(link_state) { 55 case LINK_STATE_UP: 56 case LINK_STATE_HALF_DUPLEX: 57 case LINK_STATE_FULL_DUPLEX: 58 state = MASTER; 59 break; 60 case LINK_STATE_DOWN: 61 state = SLAVE; 62 break; 63 case LINK_STATE_UNKNOWN: 64 state = INIT; 65 break; 66 } 67 68 return state; 69 } 70 71 /* Returns 1 for the CARP MASTER, 0 for BACKUP/INIT, -1 on error. */ 72 static enum RUNSTATE 73 carp_get_state(char *ifname) 74 { 75 struct ifreq ifr; 76 struct if_data ifrdat; 77 int s, saved_errno; 78 79 if (!ifname || !*ifname) { 80 errno = ENOENT; 81 return FAIL; 82 } 83 84 memset(&ifr, 0, sizeof ifr); 85 strlcpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name); 86 87 s = socket(AF_INET, SOCK_DGRAM, 0); 88 if (s < 0) 89 return FAIL; 90 91 ifr.ifr_data = (caddr_t)&ifrdat; 92 if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) { 93 saved_errno = errno; 94 close(s); 95 errno = saved_errno; 96 return FAIL; 97 } 98 close(s); 99 return carp_map_state(ifrdat.ifi_link_state); 100 } 101 102 void 103 carp_demote(int demote, int force) 104 { 105 struct ifgroupreq ifgr; 106 int s; 107 108 if (carp_demoted + demote < 0) { 109 log_msg(1, "carp_demote: mismatched promotion"); 110 return; 111 } 112 113 s = socket(AF_INET, SOCK_DGRAM, 0); 114 if (s < 0) { 115 log_msg(1, "carp_demote: couldn't open socket"); 116 return; 117 } 118 119 bzero(&ifgr, sizeof(ifgr)); 120 strlcpy(ifgr.ifgr_name, cfgstate.carp_ifgroup, sizeof(ifgr.ifgr_name)); 121 122 /* Unless we force it, don't demote if we're not demoting already. */ 123 if (!force) { 124 if (ioctl(s, SIOCGIFGATTR, (caddr_t)&ifgr) == -1) { 125 log_msg(1, "carp_demote: unable to get " 126 "the demote state of group '%s'", 127 cfgstate.carp_ifgroup); 128 goto done; 129 } 130 131 if (ifgr.ifgr_attrib.ifg_carp_demoted == 0) 132 goto done; 133 } 134 135 ifgr.ifgr_attrib.ifg_carp_demoted = demote; 136 if (ioctl(s, SIOCSIFGATTR, (caddr_t)&ifgr) == -1) 137 log_msg(1, "carp_demote: unable to %s the demote state " 138 "of group '%s'", (demote > 0) ? 139 "increment" : "decrement", cfgstate.carp_ifgroup); 140 else { 141 carp_demoted += demote; 142 log_msg(1, "carp_demote: %sed the demote state " 143 "of group '%s'", (demote > 0) ? 144 "increment" : "decrement", cfgstate.carp_ifgroup); 145 } 146 done: 147 close(s); 148 } 149 150 const char* 151 carp_state_name(enum RUNSTATE state) 152 { 153 static const char *carpstate[] = CARPSTATES; 154 155 if (state < 0 || state > FAIL) 156 state = FAIL; 157 return carpstate[state]; 158 } 159 160 void 161 carp_update_state(enum RUNSTATE current_state) 162 { 163 164 if (current_state < 0 || current_state > FAIL) { 165 log_err("carp_update_state: invalid carp state, abort"); 166 cfgstate.runstate = FAIL; 167 return; 168 } 169 170 if (current_state != cfgstate.runstate) { 171 log_msg(1, "carp_update_state: switching state to %s", 172 carp_state_name(current_state)); 173 cfgstate.runstate = current_state; 174 if (current_state == MASTER) 175 pfkey_set_promisc(); 176 isakmpd_setrun(); 177 net_ctl_update_state(); 178 } 179 } 180 181 void 182 carp_check_state() 183 { 184 carp_update_state(carp_get_state(cfgstate.carp_ifname)); 185 } 186 187 void 188 carp_set_rfd(fd_set *fds) 189 { 190 if (cfgstate.route_socket != -1) 191 FD_SET(cfgstate.route_socket, fds); 192 } 193 194 static void 195 carp_read(void) 196 { 197 char msg[2048]; 198 struct rt_msghdr *rtm = (struct rt_msghdr *)&msg; 199 struct if_msghdr ifm; 200 int len; 201 202 len = read(cfgstate.route_socket, msg, sizeof(msg)); 203 204 if (len < sizeof(struct rt_msghdr) || 205 rtm->rtm_version != RTM_VERSION || 206 rtm->rtm_type != RTM_IFINFO) 207 return; 208 209 memcpy(&ifm, rtm, sizeof(ifm)); 210 211 if (ifm.ifm_index == cfgstate.carp_ifindex) 212 carp_update_state(carp_map_state(ifm.ifm_data.ifi_link_state)); 213 } 214 215 void 216 carp_read_message(fd_set *fds) 217 { 218 if (cfgstate.route_socket != -1) 219 if (FD_ISSET(cfgstate.route_socket, fds)) 220 (void)carp_read(); 221 } 222 223 /* Initialize the CARP state. */ 224 int 225 carp_init(void) 226 { 227 cfgstate.route_socket = -1; 228 229 if (cfgstate.lockedstate != INIT) { 230 cfgstate.runstate = cfgstate.lockedstate; 231 log_msg(1, "carp_init: locking runstate to %s", 232 carp_state_name(cfgstate.runstate)); 233 return 0; 234 } 235 236 if (!cfgstate.carp_ifname || !*cfgstate.carp_ifname) { 237 fprintf(stderr, "No carp interface\n"); 238 return -1; 239 } 240 241 cfgstate.carp_ifindex = if_nametoindex(cfgstate.carp_ifname); 242 if (!cfgstate.carp_ifindex) { 243 fprintf(stderr, "No carp interface index\n"); 244 return -1; 245 } 246 247 cfgstate.route_socket = socket(PF_ROUTE, SOCK_RAW, 0); 248 if (cfgstate.route_socket < 0) { 249 fprintf(stderr, "No routing socket\n"); 250 return -1; 251 } 252 253 cfgstate.runstate = carp_get_state(cfgstate.carp_ifname); 254 if (cfgstate.runstate == FAIL) { 255 fprintf(stderr, "Failed to check interface \"%s\".\n", 256 cfgstate.carp_ifname); 257 fprintf(stderr, "Correct or manually select runstate.\n"); 258 return -1; 259 } 260 log_msg(1, "carp_init: initializing runstate to %s", 261 carp_state_name(cfgstate.runstate)); 262 263 return 0; 264 } 265 266 /* Enable or disable isakmpd connection checker. */ 267 void 268 isakmpd_setrun(void) 269 { 270 if (cfgstate.runstate == MASTER) { 271 if (monitor_isakmpd_active(1)) 272 log_msg(0, "failed to activate isakmpd"); 273 } else { 274 if (monitor_isakmpd_active(0)) 275 log_msg(0, "failed to passivate isakmpd"); 276 } 277 } 278