From: Baptiste Assmann Date: Tue, 2 Aug 2016 06:18:55 +0000 (+0200) Subject: MINOR: new update_server_addr_port() function to change both server's ADDR and servic... X-Git-Tag: v1.7-dev5~51 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d458adcc52b74608e2fe6a2a95f09ce5e94932b7;p=thirdparty%2Fhaproxy.git MINOR: new update_server_addr_port() function to change both server's ADDR and service PORT This function can replace update_server_addr() where the need to change the server's port as well as the IP address is required. It performs some validation before performing each type of change. --- diff --git a/include/proto/server.h b/include/proto/server.h index ee45f63c0f..47630fe24c 100644 --- a/include/proto/server.h +++ b/include/proto/server.h @@ -40,6 +40,7 @@ int srv_lastsession(const struct server *s); int srv_getinter(const struct check *check); int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy); int update_server_addr(struct server *s, void *ip, int ip_sin_family, const char *updater); +const char *update_server_addr_port(struct server *s, const char *addr, const char *port, char *updater); struct server *server_find_by_id(struct proxy *bk, int id); struct server *server_find_by_name(struct proxy *bk, const char *name); struct server *server_find_best_match(struct proxy *bk, char *name, int id, int *diff); diff --git a/src/server.c b/src/server.c index a90ae9b21d..b105e28eb3 100644 --- a/src/server.c +++ b/src/server.c @@ -2610,6 +2610,180 @@ int update_server_addr(struct server *s, void *ip, int ip_sin_family, const char return 0; } +/* + * This function update a server's addr and port only for AF_INET and AF_INET6 families. + * + * Caller can pass its name through to get it integrated in the response message + * returned by the function. + * + * The function first does the following, in that order: + * - validates the new addr and/or port + * - checks if an update is required (new IP or port is different than current ones) + * - checks the update is allowed: + * - don't switch from/to a family other than AF_INET4 and AF_INET6 + * - allow all changes if no CHECKS are configured + * - if CHECK is configured: + * - if switch to port map (SRV_F_MAPPORTS), ensure health check have their own ports + * - applies required changes to both ADDR and PORT if both 'required' and 'allowed' + * conditions are met + */ +const char *update_server_addr_port(struct server *s, const char *addr, const char *port, char *updater) +{ + struct sockaddr_storage sa; + int ret, port_change_required; + char current_addr[INET6_ADDRSTRLEN]; + u_int16_t current_port, new_port; + struct chunk *msg; + + msg = get_trash_chunk(); + chunk_reset(msg); + + if (addr) { + memset(&sa, 0, sizeof(struct sockaddr_storage)); + if (str2ip2(addr, &sa, 0) == NULL) { + chunk_printf(msg, "Invalid addr '%s'", addr); + goto out; + } + + /* changes are allowed on AF_INET* families only */ + if ((sa.ss_family != AF_INET) && (sa.ss_family != AF_INET6)) { + chunk_printf(msg, "Update to families other than AF_INET and AF_INET6 supported only through configuration file"); + goto out; + } + + /* collecting data currently setup */ + memset(current_addr, '\0', sizeof(current_addr)); + ret = addr_to_str(&s->addr, current_addr, sizeof(current_addr)); + /* changes are allowed on AF_INET* families only */ + if ((ret != AF_INET) && (ret != AF_INET6)) { + chunk_printf(msg, "Update for the current server address family is only supported through configuration file"); + goto out; + } + + /* applying ADDR changes if required and allowed + * ipcmp returns 0 when both ADDR are the same + */ + if (ipcmp(&s->addr, &sa) == 0) { + chunk_appendf(msg, "no need to change the addr"); + goto port; + } + current_port = get_host_port(&s->addr); + memset(&s->addr, '\0', sizeof(s->addr)); + ipcpy(&sa, &s->addr); + set_host_port(&s->addr, current_port); + + /* we also need to update check's ADDR only if it uses the server's one */ + if ((s->check.state & CHK_ST_CONFIGURED) && (s->flags & SRV_F_CHECKADDR)) { + current_port = get_host_port(&s->check.addr); + memset(&s->check.addr, '\0', sizeof(s->check.addr)); + ipcpy(&sa, &s->check.addr); + set_host_port(&s->check.addr, current_port); + } + + /* we also need to update agent ADDR only if it use the server's one */ + if ((s->agent.state & CHK_ST_CONFIGURED) && (s->flags & SRV_F_AGENTADDR)) { + current_port = get_host_port(&s->agent.addr); + memset(&s->agent.addr, '\0', sizeof(s->agent.addr)); + ipcpy(&sa, &s->agent.addr); + set_host_port(&s->agent.addr, current_port); + } + + /* update report for caller */ + chunk_printf(msg, "IP changed from '%s' to '%s'", current_addr, addr); + } + + port: + if (port) { + char sign = '\0'; + char *endptr; + + if (addr) + chunk_appendf(msg, ", "); + + /* collecting data currently setup */ + current_port = get_host_port(&s->addr); + + /* check if PORT change is required */ + port_change_required = 0; + + sign = *port; + new_port = strtol(port, &endptr, 10); + if ((errno != 0) || (port == endptr)) { + chunk_appendf(msg, "problem converting port '%s' to an int", port); + goto out; + } + + /* check if caller triggers a port mapped or offset */ + if (sign == '-' || (sign == '+')) { + /* check if server currently uses port map */ + if (!(s->flags & SRV_F_MAPPORTS)) { + /* switch from fixed port to port map mandatorily triggers + * a port change */ + port_change_required = 1; + /* check is configured + * we're switching from a fixed port to a SRV_F_MAPPORTS (mapped) port + * prevent PORT change if check doesn't have it's dedicated port while switching + * to port mapping */ + if ((s->check.state & CHK_ST_CONFIGURED) && !(s->flags & SRV_F_CHECKPORT)) { + chunk_appendf(msg, "can't change to port map because it is incompatible with current health check port configuration (use 'port' statement from the 'server' directive."); + goto out; + } + } + /* we're already using port maps */ + else { + port_change_required = current_port != new_port; + } + } + /* fixed port */ + else { + port_change_required = current_port != new_port; + } + + /* applying PORT changes if required and update response message */ + if (port_change_required) { + /* apply new port */ + set_host_port(&s->addr, new_port); + + /* prepare message */ + chunk_appendf(msg, "port changed from '"); + if (s->flags & SRV_F_MAPPORTS) + chunk_appendf(msg, "+"); + chunk_appendf(msg, "%d' to '", current_port); + + if (sign == '-') { + s->flags |= SRV_F_MAPPORTS; + chunk_appendf(msg, "%c", sign); + /* just use for result output */ + new_port = -new_port; + } + else if (sign == '+') { + s->flags |= SRV_F_MAPPORTS; + chunk_appendf(msg, "%c", sign); + } + else { + s->flags &= ~SRV_F_MAPPORTS; + } + + chunk_appendf(msg, "%d'", new_port); + + /* we also need to update health checks port only if it uses server's realport */ + if ((s->check.state & CHK_ST_CONFIGURED) && !(s->flags & SRV_F_CHECKPORT)) { + s->check.port = new_port; + } + } + else { + chunk_appendf(msg, "no need to change the port"); + } + } + +out: + if (updater) + chunk_appendf(msg, " by '%s'", updater); + chunk_appendf(msg, "\n"); + return msg->str; +} + + /* * update server status based on result of name resolution * returns: