Hi, Attached patch allows setting a server's port in addition to the address via the admin socket, e.g.:
set server mybackend/server-1 addr 127.0.0.1:8080 I find this already useful by itself, and furthermore this can be used (one day) to update a server's address/port via DNS SRV lookups. However, the implementation has a slight problem: I tried to support the same port specifications as the server definition, i.e. absolute (:X), relative mapping (:+X, :-X) and port forwarding (omitting the port). On the other hand, I wanted to keep the command backwards compatible, which means omitting the port must not change it. Thus, I ended up with the crude workaround of requiring ':+65336' (== (ushort)0) to enable port forwarding, which is a little ugly. Nevertheless, I used this succesfully to change ports on a sever and switch between all modes, which is quite fancy. So I guess the questions would be: 1. interesting at all? 2. would maybe be an option to ditch backwards compatibility for the sake of a clean implementation (probably less confusing in the long run)? 3. or are there maybe other ideas on how to gracefully handle backwards compatibility? Thanks a lot, Conrad -- Conrad Hoffmann Traffic Engineer SoundCloud Ltd. | Rheinsberger Str. 76/77, 10115 Berlin, Germany Managing Director: Alexander Ljung | Incorporated in England & Wales with Company No. 6343600 | Local Branch Office | AG Charlottenburg | HRB 110657B
From 094680a4f55870993e25636c3cef34e681a353dd Mon Sep 17 00:00:00 2001 From: Conrad Hoffmann <[email protected]> Date: Wed, 29 Jun 2016 00:44:21 +0200 Subject: [PATCH] Allow setting server port via admin socket. It is already possible to change a server's IP address, effectively switching to a new server. However, it is not currently possible to also set the port. This commit extends the 'set server ... addr' command to understand and change ports as well. Like the original server definition, it supports absolute port, relative port mapping and port forwarding. Unfortunately, to keep the command backwards compatible (no port means keep current port), the workaround to enable port forwarding is to specify the port as '+65336'. As mentioned in a comment, this functionality can be used in the future to update the address and port of a server by means of DNS SRV lookups. --- doc/management.txt | 8 ++++-- include/proto/server.h | 2 +- src/server.c | 70 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/doc/management.txt b/doc/management.txt index caf7eb0..c7a0076 100644 --- a/doc/management.txt +++ b/doc/management.txt @@ -1579,8 +1579,12 @@ set rate-limit ssl-sessions global <value> is passed in number of sessions per second sent to the SSL stack. It applies before the handshake in order to protect the stack against handshake abuses. -set server <backend>/<server> addr <ip4 or ip6 address> - Replace the current IP address of a server by the one provided. +set server <backend>/<server> addr <ip4 or ip6 address>[:[<port>] + Replace the current IP address and port of a server by the one provided. + If port is omitted, the server's current settings are used. The port syntax + supports relative port mapping via +<port> and -<port>. To enable port + forwarding, it is currently neccessary to use the workaround of setting the + port to '+65536'. set server <backend>/<server> agent [ up | down ] Force a server's agent to a new state. This can be useful to immediately diff --git a/include/proto/server.h b/include/proto/server.h index ee45f63..7e5c8c1 100644 --- a/include/proto/server.h +++ b/include/proto/server.h @@ -39,7 +39,7 @@ int srv_downtime(const struct server *s); 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); +int update_server_addr(struct server *s, void *ip, int ip_sin_family, unsigned short port, int map, const 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 1095754..7390ecb 100644 --- a/src/server.c +++ b/src/server.c @@ -817,17 +817,46 @@ const char *server_parse_weight_change_request(struct server *sv, const char *server_parse_addr_change_request(struct server *sv, const char *addr_str, const char *updater) { - unsigned char ip[INET6_ADDRSTRLEN]; + void *ip; + unsigned short port; + int high, low, map; + char *fqdn, *err; + struct sockaddr_storage *sa; + + err = fqdn = NULL; + sa = str2sa_range(addr_str, &low, &high, &err, NULL, &fqdn, 0); + // Make sure parsing succeded and the address is numeric. + if (!sa || fqdn) { + goto error; + } + + if (low != high) { + if (low && high) + goto error; // port range not supported + map = 1; + } + else { + if (!low) + map = sv->flags & SRV_F_MAPPORTS; // no port specified, keep current value + else + map = 0; + } - if (inet_pton(AF_INET6, addr_str, ip)) { - update_server_addr(sv, ip, AF_INET6, updater); + // Use current port if no new one was specified + if (sa->ss_family == AF_INET6) { + port = high > 0 ? htons((short)high) : ((struct sockaddr_in6 *)&sv->addr)->sin6_port; + ip = &((struct sockaddr_in6 *)sa)->sin6_addr; + update_server_addr(sv, ip, AF_INET6, port, map, updater); return NULL; } - if (inet_pton(AF_INET, addr_str, ip)) { - update_server_addr(sv, ip, AF_INET, updater); + else if (sa->ss_family == AF_INET) { + port = high > 0 ? htons((short)high) : ((struct sockaddr_in *)&sv->addr)->sin_port; + ip = &((struct sockaddr_in *)sa)->sin_addr.s_addr; + update_server_addr(sv, ip, AF_INET, port, map, updater); return NULL; } + error: return "Could not understand IP address format.\n"; } @@ -2576,32 +2605,44 @@ fileclose: } /* - * update a server's current IP address. + * update a server's current IP address and port. * ip is a pointer to the new IP address, whose address family is ip_sin_family. * ip is in network format. + * port is the new port in network format. + * if map is != 0, port mapping will be enabled, else disabled * updater is a string which contains an information about the requester of the update. * updater is used if not NULL. * * A log line and a stderr warning message is generated based on server's backend options. */ -int update_server_addr(struct server *s, void *ip, int ip_sin_family, const char *updater) +int update_server_addr(struct server *s, void *ip, int ip_sin_family, unsigned short port, int map, const char *updater) { /* generates a log line and a warning on stderr */ if (1) { /* book enough space for both IPv4 and IPv6 */ char oldip[INET6_ADDRSTRLEN]; char newip[INET6_ADDRSTRLEN]; + unsigned short oldport; memset(oldip, '\0', INET6_ADDRSTRLEN); memset(newip, '\0', INET6_ADDRSTRLEN); + if (map) { + s->flags |= SRV_F_MAPPORTS; + } + else { + s->flags &= ~SRV_F_MAPPORTS; + } + /* copy old IP address in a string */ switch (s->addr.ss_family) { case AF_INET: inet_ntop(s->addr.ss_family, &((struct sockaddr_in *)&s->addr)->sin_addr, oldip, INET_ADDRSTRLEN); + oldport = ((struct sockaddr_in *)&s->addr)->sin_port; break; case AF_INET6: inet_ntop(s->addr.ss_family, &((struct sockaddr_in6 *)&s->addr)->sin6_addr, oldip, INET6_ADDRSTRLEN); + oldport = ((struct sockaddr_in6 *)&s->addr)->sin6_port; break; }; @@ -2616,8 +2657,8 @@ int update_server_addr(struct server *s, void *ip, int ip_sin_family, const char }; /* save log line into a buffer */ - chunk_printf(&trash, "%s/%s changed its IP from %s to %s by %s", - s->proxy->id, s->id, oldip, newip, updater); + chunk_printf(&trash, "%s/%s changed its IP from %s:%hu to %s:%hu by %s", + s->proxy->id, s->id, oldip, ntohs(oldport), newip, ntohs(port), updater); /* write the buffer on stderr */ Warning("%s.\n", trash.str); @@ -2632,9 +2673,11 @@ int update_server_addr(struct server *s, void *ip, int ip_sin_family, const char switch (ip_sin_family) { case AF_INET: ((struct sockaddr_in *)&s->addr)->sin_addr.s_addr = *(uint32_t *)ip; + ((struct sockaddr_in *)&s->addr)->sin_port = port; break; case AF_INET6: memcpy(((struct sockaddr_in6 *)&s->addr)->sin6_addr.s6_addr, ip, 16); + ((struct sockaddr_in6 *)&s->addr)->sin6_port = port; break; }; @@ -2682,8 +2725,9 @@ int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver * struct server *s; void *serverip, *firstip; short server_sin_family, firstip_sin_family; + unsigned short port; unsigned char *response_end; - int ret; + int ret, map; struct chunk *chk = get_trash_chunk(); /* initializing variables */ @@ -2711,6 +2755,10 @@ int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver * goto invalid; } + /* at some point DNS can return a port from SRV lookup */ + port = ((struct sockaddr_in *)&s->addr)->sin_port; + map = s->flags & SRV_F_MAPPORTS; + ret = dns_get_ip_from_response(response, response_end, resolution, serverip, server_sin_family, &firstip, &firstip_sin_family); @@ -2765,7 +2813,7 @@ int snr_resolution_cb(struct dns_resolution *resolution, struct dns_nameserver * /* save the first ip we found */ chunk_printf(chk, "%s/%s", nameserver->resolvers->id, nameserver->id); - update_server_addr(s, firstip, firstip_sin_family, (char *)chk->str); + update_server_addr(s, firstip, firstip_sin_family, port, map, (char *)chk->str); stop_resolution: /* update last resolution date and time */ -- 2.9.0

