we try to bind to it, and if that fails then we don't have that IP
on an interface
*/
-bool ctdb_sys_have_ip(const char *ip)
+bool ctdb_sys_have_ip(const char *ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname)
{
struct sockaddr_in sin;
int s;
int ret;
+ if (is_loopback) {
+ DEBUG(0,(__location__ " NOT IMPLEMENTED YET: ctdb_sys_have_ip() does not yet support is_loopback on AIX. This needs to be implemented similar to system_linux.c\n"));
+ }
+
sin.sin_port = 0;
inet_aton(ip, &sin.sin_addr);
sin.sin_family = AF_INET;
we try to bind to it, and if that fails then we don't have that IP
on an interface
+ if is_loopback is specified it will also return whether the ip address
+ is attached to the loopback interface or not
+
+ ifname, if non-NULL, will return the name of the interface this ip is tied to
*/
-bool ctdb_sys_have_ip(const char *ip)
+bool ctdb_sys_have_ip(const char *ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname)
{
struct sockaddr_in sin;
- int s;
+ struct ifreq *ifr = NULL;
+ struct ifconf ifc;
+ int s, i, num_ifs;
int ret;
+ if (is_loopback) {
+ *is_loopback = false;
+ }
+ if (*ifname) {
+ *ifname = NULL;
+ }
+
sin.sin_port = 0;
inet_aton(ip, &sin.sin_addr);
sin.sin_family = AF_INET;
return false;
}
ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
+ if (ret) {
+ goto finished;
+ }
+
+
+ /* find out how much space we need to store the interface details */
+ ifc.ifc_len = 0;
+ ifc.ifc_req = NULL;
+ ret = ioctl(s, SIOCGIFCONF, &ifc);
+ if (ret) {
+ DEBUG(0,(__location__ " ioctl to read interface list failed\n"));
+ goto finished;
+ }
+
+ ifr = talloc_size(mem_ctx, ifc.ifc_len);
+
+ /* get a list of all interface names and addresses */
+ ifc.ifc_req = ifr;
+ ret = ioctl(s, SIOCGIFCONF, &ifc);
+ if (ret) {
+ DEBUG(0,(__location__ " ioctl to read interface list failed\n"));
+ goto finished;
+ }
+
+ /* loop over all interfaces and search for the one matching ip */
+ num_ifs = ifc.ifc_len/sizeof(struct ifreq);
+ for (i=0; i<num_ifs;i++) {
+ struct sockaddr_in *sa;
+
+ /* we only care bout ipv4 addresses */
+ sa = (struct sockaddr_in *)&ifr[i].ifr_addr;
+ if (sa->sin_family != AF_INET) {
+ continue;
+ }
+
+ /* this is not the interface you are looking for */
+ if(strcmp(inet_ntoa(sa->sin_addr), ip)){
+ continue;
+ }
+
+ /* this is the ifr entry for this interface/address
+ read the interface flags so we can tell if it is
+ loopback or not
+ */
+ ret = ioctl(s, SIOCGIFFLAGS, &ifr[i]);
+ if (ret) {
+ DEBUG(0,(__location__ " failed to read interface flags for interface %s\n", ifr[i].ifr_name));
+ goto finished;
+ }
+
+ /* was this ip tied to a loopback interface ? */
+ if(ifr[i].ifr_flags & IFF_LOOPBACK) {
+ if (is_loopback != NULL) {
+ *is_loopback = true;
+ }
+ }
+
+ if (ifname) {
+ *ifname = talloc_asprintf(mem_ctx, "%s", ifr[i].ifr_name);
+ }
+
+ /* if we got this far, we have found our interface so we can
+ exit the loop.
+ */
+ break;
+ }
+
+finished:
+ talloc_free(ifr);
close(s);
return ret == 0;
}
echo "`/bin/date` Failed to bringup interface $iface"
exit 1
}
+ /sbin/ip addr del $ip/32 dev lo >/dev/null 2>/dev/null
/sbin/ip addr add $ip/$maskbits dev $iface || {
echo "`/bin/date` Failed to add $ip/$maskbits on dev $iface"
exit 1
echo "`/bin/date` Failed to del $ip on dev $iface"
exit 1
}
+ /sbin/ip addr add $ip/32 dev lo >/dev/null 2>/dev/null
# flush our route cache
echo 1 > /proc/sys/net/ipv4/route/flush
takeip)
ip=$2
- echo $ip >> /etc/ctdb/state/nfs/restart
echo $ip >> /etc/ctdb/state/statd/restart
# having a list of what IPs we have allows statd to do the right
ip=$2
maskbits=$3
- echo $ip >> /etc/ctdb/state/nfs/restart
echo $ip >> /etc/ctdb/state/statd/restart
/bin/rm -f /etc/ctdb/state/statd/ip/$ip
- exit 0
- ;;
- recovered)
- [ -f /etc/ctdb/state/nfs/restart ] && [ ! -z "$LOCKD_TCPPORT" ] && {
+ #XXX we should only RST our local ports here
+ # RST all tcp connections to the lockmanager
+ [ ! -z "$LOCKD_TCPPORT" ] && {
# RST all tcp connections used for NLM to ensure that they do
# not survive in ESTABLISHED state across a failover/failback
# and create an ack storm
- netstat -tn |egrep "^tcp.*\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:${LOCKD_TCPPORT}\s+.*ESTABLISHED" | awk '{print $4" "$5}' | while read dest src; do
+ netstat -tn |egrep "^tcp.*\s+$ip:${LOCKD_TCPPORT}\s+.*ESTABLISHED" | awk '{print $4" "$5}' | while read dest src; do
srcip=`echo $src | cut -d: -f1`
srcport=`echo $src | cut -d: -f2`
destip=`echo $dest | cut -d: -f1`
destport=`echo $dest | cut -d: -f2`
ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1
- ctdb killtcp $destip:$destport $srcip:$srcport >/dev/null 2>&1
+# ctdb killtcp $destip:$destport $srcip:$srcport >/dev/null 2>&1
done
} > /dev/null 2>&1
- [ -f /etc/ctdb/state/nfs/restart ] && {
- # RST all tcp connections used for NFS to ensure that they do
- # not survive in ESTABLISHED state across a failover/failback
- # and create an ack storm
- netstat -tn |egrep '^tcp.*\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:2049\s+.*ESTABLISHED' | awk '{print $4" "$5}' | while read dest src; do
- srcip=`echo $src | cut -d: -f1`
- srcport=`echo $src | cut -d: -f2`
- destip=`echo $dest | cut -d: -f1`
- destport=`echo $dest | cut -d: -f2`
- ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1
- ctdb killtcp $destip:$destport $srcip:$srcport >/dev/null 2>&1
- done
- } > /dev/null 2>&1
- /bin/rm -f /etc/ctdb/state/nfs/restart
+ # RST all tcp connections used for NFS to ensure that they do
+ # not survive in ESTABLISHED state across a failover/failback
+ # and create an ack storm
+ netstat -tn |egrep "^tcp.*\s+$ip:2049\s+.*ESTABLISHED" | awk '{print $4" "$5}' | while read dest src; do
+ srcip=`echo $src | cut -d: -f1`
+ srcport=`echo $src | cut -d: -f2`
+ destip=`echo $dest | cut -d: -f1`
+ destport=`echo $dest | cut -d: -f2`
+ ctdb killtcp $srcip:$srcport $destip:$destport >/dev/null 2>&1
+# ctdb killtcp $destip:$destport $srcip:$srcport >/dev/null 2>&1
+ done
+
+ exit 0
+ ;;
+
+ recovered)
# always restart the lockmanager so that we start with a clusterwide
# graceperiod when ip addresses has changed
[ -x /etc/ctdb/statd-callout ] && {
;;
releaseip)
- iface=$1
- ip=$2
- if netstat -tn | egrep "^tcp.*$ip:2049.*ESTABLISHED" > /dev/null 2>&1; then
- echo "`date` Restarting NFS due to established connections for IP $ip"
- ( service nfs status > /dev/null 2>&1 &&
- service nfs restart > /dev/null 2>&1 ) &
- fi
exit 0
;;
- # we need to restart lockmanager to make sure we get a clusterwide
- # lock grace period. but to stop lockmanager completely
- # we must also stop nfs (on linux)
-# service nfs stop > /dev/null 2>&1
-# service nfs start > /dev/null 2>&1
-
# we must also let some time pass between stopping and restarting the
# lockmanager since othervise there is a window where the lockmanager
# will respond "strangely" immediately after restarting it, which
/* from takeover/system.c */
int ctdb_sys_send_arp(const struct sockaddr_in *saddr, const char *iface);
-bool ctdb_sys_have_ip(const char *ip);
+bool ctdb_sys_have_ip(const char *ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname);
int ctdb_sys_send_tcp(int fd,
const struct sockaddr_in *dest,
const struct sockaddr_in *src,
TDB_DATA indata,
bool *async_reply)
{
+ TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
int ret;
struct takeover_callback_state *state;
struct ctdb_public_ip *pip = (struct ctdb_public_ip *)indata.dptr;
- char *ip = inet_ntoa(pip->sin.sin_addr);
struct ctdb_vnn *vnn;
+ char *ip = talloc_strdup(tmp_ctx, inet_ntoa(pip->sin.sin_addr));
+ bool have_ip, is_loopback;
+ char *ifname = NULL;
/* update out vnn list */
vnn = find_public_ip_vnn(ctdb, ip);
if (vnn == NULL) {
DEBUG(0,("takeoverip called for an ip '%s' that is not a public address\n", ip));
+ talloc_free(tmp_ctx);
return 0;
}
vnn->pnn = pip->pnn;
/* if our kernel already has this IP, do nothing */
- if (ctdb_sys_have_ip(ip)) {
+ have_ip = ctdb_sys_have_ip(ip, &is_loopback, tmp_ctx, &ifname);
+ /* if we have the ip and it is not set to a loopback address */
+ if (have_ip && !is_loopback) {
+ talloc_free(tmp_ctx);
return 0;
}
if (ret != 0) {
DEBUG(0,(__location__ " Failed to takeover IP %s on interface %s\n",
ip, vnn->iface));
+ talloc_free(tmp_ctx);
talloc_free(state);
return -1;
}
/* tell ctdb_control.c that we will be replying asynchronously */
*async_reply = true;
+ talloc_free(tmp_ctx);
return 0;
}
TDB_DATA indata,
bool *async_reply)
{
+ TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
int ret;
struct takeover_callback_state *state;
struct ctdb_public_ip *pip = (struct ctdb_public_ip *)indata.dptr;
- char *ip = inet_ntoa(pip->sin.sin_addr);
+ char *ip = talloc_strdup(tmp_ctx, inet_ntoa(pip->sin.sin_addr));
struct ctdb_vnn *vnn;
+ bool have_ip, is_loopback;
+ char *ifname = NULL;
/* update our vnn list */
vnn = find_public_ip_vnn(ctdb, ip);
if (vnn == NULL) {
DEBUG(0,("releaseip called for an ip '%s' that is not a public address\n", ip));
+ talloc_free(tmp_ctx);
return 0;
}
vnn->pnn = pip->pnn;
- if (!ctdb_sys_have_ip(ip)) {
+ have_ip = ctdb_sys_have_ip(ip, &is_loopback, tmp_ctx, &ifname);
+ if ( (!have_ip) || is_loopback) {
DEBUG(0,("Redundant release of IP %s/%u on interface %s (ip not held)\n",
ip, vnn->public_netmask_bits,
vnn->iface));
+ talloc_free(tmp_ctx);
return 0;
}
if (ret != 0) {
DEBUG(0,(__location__ " Failed to release IP %s on interface %s\n",
ip, vnn->iface));
+ talloc_free(tmp_ctx);
talloc_free(state);
return -1;
}
/* tell the control that we will be reply asynchronously */
*async_reply = true;
+ talloc_free(tmp_ctx);
return 0;
}
void ctdb_release_all_ips(struct ctdb_context *ctdb)
{
struct ctdb_vnn *vnn;
+ TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
+ bool have_ip, is_loopback;
+ char *ifname = NULL;
for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
- if (ctdb_sys_have_ip(vnn->public_address)) {
+ have_ip = ctdb_sys_have_ip(vnn->public_address, &is_loopback, tmp_ctx, &ifname);
+ if (have_ip && !is_loopback) {
struct in_addr in;
+
ctdb_event_script(ctdb, "releaseip %s %s %u",
vnn->iface,
vnn->public_address,
}
}
}
+ talloc_free(tmp_ctx);
}
}
/*
- add a tcp socket to the list of connections we will kill on failover
+ add a tcp socket to the list of connections we want to RST
*/
static int ctdb_killtcp_add_connection(struct ctdb_context *ctdb,
struct sockaddr_in *src, struct sockaddr_in *dst)
{
struct ctdb_kill_tcp *killtcp;
struct ctdb_killtcp_con *con;
- char *addr;
struct ctdb_vnn *vnn;
- addr = inet_ntoa(dst->sin_addr);
-
- vnn = find_public_ip_vnn(ctdb, addr);
+ vnn = find_public_ip_vnn(ctdb, inet_ntoa(dst->sin_addr));
if (vnn == NULL) {
- DEBUG(0,(__location__ " Could not killtcp, '%s' is not a public address\n", addr));
+ vnn = find_public_ip_vnn(ctdb, inet_ntoa(src->sin_addr));
+ }
+ if (vnn == NULL) {
+ DEBUG(0,(__location__ " Could not killtcp, not a public address\n"));
return -1;
}
}
/*
- called by a daemon to inform us of a TCP connection that one of its
- clients managing that should reset when IP takeover is done
+ kill a TCP connection.
*/
int32_t ctdb_control_kill_tcp(struct ctdb_context *ctdb, TDB_DATA indata)
{