1 /* $OpenBSD: carp.c,v 1.12 2010/06/29 21:25:37 kjell 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 /* Map CARP interface link state into RUNSTATE enum */ 50 static enum RUNSTATE 51 carp_map_state(u_char link_state) 52 { 53 enum RUNSTATE state = FAIL; 54 55 switch(link_state) { 56 case LINK_STATE_UP: 57 case LINK_STATE_HALF_DUPLEX: 58 case LINK_STATE_FULL_DUPLEX: 59 state = MASTER; 60 break; 61 case LINK_STATE_DOWN: 62 state = SLAVE; 63 break; 64 case LINK_STATE_UNKNOWN: 65 state = INIT; 66 break; 67 } 68 69 return state; 70 } 71 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 control_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 unsigned int rtfilter; 228 229 cfgstate.route_socket = -1; 230 if (cfgstate.lockedstate != INIT) { 231 cfgstate.runstate = cfgstate.lockedstate; 232 log_msg(1, "carp_init: locking runstate to %s", 233 carp_state_name(cfgstate.runstate)); 234 return 0; 235 } 236 237 if (!cfgstate.carp_ifname || !*cfgstate.carp_ifname) { 238 fprintf(stderr, "No carp interface\n"); 239 return -1; 240 } 241 242 cfgstate.carp_ifindex = if_nametoindex(cfgstate.carp_ifname); 243 if (!cfgstate.carp_ifindex) { 244 fprintf(stderr, "No carp interface index\n"); 245 return -1; 246 } 247 248 cfgstate.route_socket = socket(PF_ROUTE, SOCK_RAW, 0); 249 if (cfgstate.route_socket < 0) { 250 fprintf(stderr, "No routing socket\n"); 251 return -1; 252 } 253 254 rtfilter = ROUTE_FILTER(RTM_IFINFO); 255 if (setsockopt(cfgstate.route_socket, PF_ROUTE, ROUTE_MSGFILTER, 256 &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */ 257 log_msg(2, "carp_init: setsockopt"); 258 259 cfgstate.runstate = carp_get_state(cfgstate.carp_ifname); 260 if (cfgstate.runstate == FAIL) { 261 fprintf(stderr, "Failed to check interface \"%s\".\n", 262 cfgstate.carp_ifname); 263 fprintf(stderr, "Correct or manually select runstate.\n"); 264 return -1; 265 } 266 log_msg(1, "carp_init: initializing runstate to %s", 267 carp_state_name(cfgstate.runstate)); 268 269 return 0; 270 } 271 272 /* Enable or disable isakmpd/iked connection checker. */ 273 void 274 control_setrun(void) 275 { 276 if (cfgstate.runstate == MASTER) { 277 if (monitor_control_active(1)) 278 log_msg(0, "failed to activate controlled daemon"); 279 } else { 280 if (monitor_control_active(0)) 281 log_msg(0, "failed to passivate controlled daemon"); 282 } 283 } 284