|
@@ -46,6 +46,7 @@
|
|
#define BSD_COMP /* needed on older solaris for FIONREAD */
|
|
#define BSD_COMP /* needed on older solaris for FIONREAD */
|
|
#endif /* HAVE_FILIO_H / __OS_solaris */
|
|
#endif /* HAVE_FILIO_H / __OS_solaris */
|
|
#include <sys/ioctl.h> /* ioctl() used on write error */
|
|
#include <sys/ioctl.h> /* ioctl() used on write error */
|
|
|
|
+#include <arpa/inet.h> /* for inet_pton() */
|
|
#include <netinet/in.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip.h>
|
|
@@ -957,7 +958,227 @@ end:
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+/* Attempt to extract real connection information from an upstream load
|
|
|
|
+ * balancer or reverse proxy. This should be called right after accept()ing the
|
|
|
|
+ * connection, and before TLS negotiation.
|
|
|
|
+ *
|
|
|
|
+ * Returns:
|
|
|
|
+ * -1 on parsing error (connection should be closed)
|
|
|
|
+ * 0 on parser success, and connection information was extracted
|
|
|
|
+ * 1 on parser success, but no connection information was provided by the
|
|
|
|
+ * upstream load balancer or reverse proxy.
|
|
|
|
+ */
|
|
|
|
+int tcpconn_read_haproxy(struct tcp_connection *c) {
|
|
|
|
+ int bytes, retval = 0;
|
|
|
|
+ uint32_t size, port;
|
|
|
|
+ char *p, *end;
|
|
|
|
+ struct ip_addr *src_ip, *dst_ip;
|
|
|
|
+
|
|
|
|
+ const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
|
|
|
|
+
|
|
|
|
+ // proxy header union
|
|
|
|
+ union {
|
|
|
|
+ // v1 struct
|
|
|
|
+ struct {
|
|
|
|
+ char line[108];
|
|
|
|
+ } v1;
|
|
|
|
+
|
|
|
|
+ // v2 struct
|
|
|
|
+ struct {
|
|
|
|
+ uint8_t sig[12];
|
|
|
|
+ uint8_t ver_cmd;
|
|
|
|
+ uint8_t fam;
|
|
|
|
+ uint16_t len;
|
|
|
|
+
|
|
|
|
+ union {
|
|
|
|
+ struct { /* for TCP/UDP over IPv4, len = 12 */
|
|
|
|
+ uint32_t src_addr;
|
|
|
|
+ uint32_t dst_addr;
|
|
|
|
+ uint16_t src_port;
|
|
|
|
+ uint16_t dst_port;
|
|
|
|
+ } ip4;
|
|
|
|
+
|
|
|
|
+ struct { /* for TCP/UDP over IPv6, len = 36 */
|
|
|
|
+ uint8_t src_addr[16];
|
|
|
|
+ uint8_t dst_addr[16];
|
|
|
|
+ uint16_t src_port;
|
|
|
|
+ uint16_t dst_port;
|
|
|
|
+ } ip6;
|
|
|
|
+
|
|
|
|
+ struct { /* for AF_UNIX sockets, len = 216 */
|
|
|
|
+ uint8_t src_addr[108];
|
|
|
|
+ uint8_t dst_addr[108];
|
|
|
|
+ } unx;
|
|
|
|
+ } addr;
|
|
|
|
+ } v2;
|
|
|
|
+
|
|
|
|
+ } hdr;
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ bytes = recv(c->s, &hdr, sizeof(hdr), MSG_PEEK);
|
|
|
|
+ } while (bytes == -1 && (errno == EINTR || errno == EAGAIN));
|
|
|
|
+
|
|
|
|
+ src_ip = &c->rcv.src_ip;
|
|
|
|
+ dst_ip = &c->rcv.dst_ip;
|
|
|
|
+
|
|
|
|
+ if (bytes >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0 &&
|
|
|
|
+ (hdr.v2.ver_cmd & 0xF0) == 0x20) {
|
|
|
|
+ LM_DBG("received PROXY protocol v2 header\n");
|
|
|
|
+ size = 16 + ntohs(hdr.v2.len);
|
|
|
|
+
|
|
|
|
+ if (bytes < size) {
|
|
|
|
+ return -1; /* truncated or too large header */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (hdr.v2.ver_cmd & 0xF) {
|
|
|
|
+ case 0x01: /* PROXY command */
|
|
|
|
+ switch (hdr.v2.fam) {
|
|
|
|
+ case 0x11: /* TCPv4 */
|
|
|
|
+ src_ip->af = AF_INET;
|
|
|
|
+ src_ip->len = 4;
|
|
|
|
+ src_ip->u.addr32[0] =
|
|
|
|
+ hdr.v2.addr.ip4.src_addr;
|
|
|
|
+ c->rcv.src_port =
|
|
|
|
+ hdr.v2.addr.ip4.src_port;
|
|
|
|
+
|
|
|
|
+ dst_ip->af = AF_INET;
|
|
|
|
+ dst_ip->len = 4;
|
|
|
|
+ dst_ip->u.addr32[0] =
|
|
|
|
+ hdr.v2.addr.ip4.dst_addr;
|
|
|
|
+ c->rcv.dst_port =
|
|
|
|
+ hdr.v2.addr.ip4.dst_port;
|
|
|
|
+
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ case 0x21: /* TCPv6 */
|
|
|
|
+ src_ip->af = AF_INET6;
|
|
|
|
+ src_ip->len = 16;
|
|
|
|
+ memcpy(src_ip->u.addr,
|
|
|
|
+ hdr.v2.addr.ip6.src_addr, 16);
|
|
|
|
+ c->rcv.src_port =
|
|
|
|
+ hdr.v2.addr.ip6.src_port;
|
|
|
|
+
|
|
|
|
+ dst_ip->af = AF_INET6;
|
|
|
|
+ dst_ip->len = 16;
|
|
|
|
+ memcpy(dst_ip->u.addr,
|
|
|
|
+ hdr.v2.addr.ip6.src_addr, 16);
|
|
|
|
+ c->rcv.dst_port =
|
|
|
|
+ hdr.v2.addr.ip6.dst_port;
|
|
|
|
+
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ default: /* unsupported protocol */
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ case 0x00: /* LOCAL command */
|
|
|
|
+ retval = 1; /* keep local connection address for LOCAL */
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ return -1; /* not a supported command */
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if (bytes >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0) {
|
|
|
|
+ LM_DBG("received PROXY protocol v1 header\n");
|
|
|
|
+ end = memchr(hdr.v1.line, '\r', bytes - 1);
|
|
|
|
+ if (!end || end[1] != '\n') {
|
|
|
|
+ return -1; /* partial or invalid header */
|
|
|
|
+ }
|
|
|
|
+ *end = '\0'; /* terminate the string to ease parsing */
|
|
|
|
+ size = end + 2 - hdr.v1.line;
|
|
|
|
+ p = hdr.v1.line + 5;
|
|
|
|
+
|
|
|
|
+ if (strncmp(p, " TCP", 4) == 0) {
|
|
|
|
+ switch (p[4]) {
|
|
|
|
+ case '4':
|
|
|
|
+ src_ip->af = dst_ip->af = AF_INET;
|
|
|
|
+ src_ip->len = dst_ip->len = 4;
|
|
|
|
+ break;
|
|
|
|
+ case '6':
|
|
|
|
+ src_ip->af = dst_ip->af = AF_INET6;
|
|
|
|
+ src_ip->len = dst_ip->len = 16;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -1; /* unknown TCP version */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (p[5] != ' ') {
|
|
|
|
+ return -1; /* misformatted header */
|
|
|
|
+ }
|
|
|
|
+ p += 6; /* skip over the already-parsed bytes */
|
|
|
|
+
|
|
|
|
+ /* Parse the source IP address */
|
|
|
|
+ end = strchr(p, ' ');
|
|
|
|
+ if (!end) {
|
|
|
|
+ return -1; /* truncated header */
|
|
|
|
+ }
|
|
|
|
+ *end = '\0'; /* mark the end of the IP address */
|
|
|
|
+ if (inet_pton(src_ip->af, p, src_ip->u.addr) != 1) {
|
|
|
|
+ return -1; /* missing IP address */
|
|
|
|
+ }
|
|
|
|
+ p = end + 1;
|
|
|
|
+
|
|
|
|
+ /* Parse the destination IP address */
|
|
|
|
+ end = strchr(p, ' ');
|
|
|
|
+ if (!end) {
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ *end = '\0'; /* mark the end of the IP address */
|
|
|
|
+ if (inet_pton(dst_ip->af, p, dst_ip->u.addr) != 1) {
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ p = end + 1;
|
|
|
|
+
|
|
|
|
+ /* Parse the source port */
|
|
|
|
+ port = strtoul(p, &end, 10);
|
|
|
|
+ if (port == ULONG_MAX || port == 0 || port >= (1 << 16)) {
|
|
|
|
+ return -1; /* invalid port number */
|
|
|
|
+ }
|
|
|
|
+ c->rcv.src_port = port;
|
|
|
|
+
|
|
|
|
+ if (*end != ' ') {
|
|
|
|
+ return -1; /* invalid header */
|
|
|
|
+ }
|
|
|
|
+ p = end + 1;
|
|
|
|
+
|
|
|
|
+ /* Parse the destination port */
|
|
|
|
+ port = strtoul(p, NULL, 10);
|
|
|
|
+ if (port == ULONG_MAX || port == 0 || port >= (1 << 16)) {
|
|
|
|
+ return -1; /* invalid port number */
|
|
|
|
+ }
|
|
|
|
+ c->rcv.dst_port = port;
|
|
|
|
+
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+ else if (strncmp(p, " UNKNOWN", 8) == 0) {
|
|
|
|
+ /* We know that the sender speaks the correct PROXY protocol with the
|
|
|
|
+ * appropriate version, and we SHOULD accept the connection and use the
|
|
|
|
+ * real connection's parameters as if there were no PROXY protocol header
|
|
|
|
+ * on the wire.
|
|
|
|
+ */
|
|
|
|
+ retval = 1; /* PROXY protocol parsed, but no IP override */
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ return -1; /* invalid header */
|
|
|
|
+ }
|
|
|
|
+ } else if (bytes == 0) {
|
|
|
|
+ return 1; /* EOF? Return "no IP change" in any case */
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ /* Wrong protocol */
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+done:
|
|
|
|
+ /* we need to consume the appropriate amount of data from the socket */
|
|
|
|
+ do {
|
|
|
|
+ bytes = recv(c->s, &hdr, size, 0);
|
|
|
|
+ } while (bytes == -1 && errno == EINTR);
|
|
|
|
+
|
|
|
|
+ return (bytes >= 0) ? retval : -1;
|
|
|
|
+}
|
|
|
|
|
|
struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
|
|
struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
|
|
union sockaddr_union* local_addr,
|
|
union sockaddr_union* local_addr,
|
|
@@ -965,7 +1186,7 @@ struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
|
|
int state)
|
|
int state)
|
|
{
|
|
{
|
|
struct tcp_connection *c;
|
|
struct tcp_connection *c;
|
|
- int rd_b_size;
|
|
|
|
|
|
+ int rd_b_size, ret;
|
|
|
|
|
|
rd_b_size=cfg_get(tcp, tcp_cfg, rd_buf_size);
|
|
rd_b_size=cfg_get(tcp, tcp_cfg, rd_buf_size);
|
|
c=shm_malloc(sizeof(struct tcp_connection) + rd_b_size);
|
|
c=shm_malloc(sizeof(struct tcp_connection) + rd_b_size);
|
|
@@ -985,16 +1206,29 @@ struct tcp_connection* tcpconn_new(int sock, union sockaddr_union* su,
|
|
|
|
|
|
atomic_set(&c->refcnt, 0);
|
|
atomic_set(&c->refcnt, 0);
|
|
local_timer_init(&c->timer, tcpconn_main_timeout, c, 0);
|
|
local_timer_init(&c->timer, tcpconn_main_timeout, c, 0);
|
|
- su2ip_addr(&c->rcv.src_ip, su);
|
|
|
|
- c->rcv.src_port=su_getport(su);
|
|
|
|
- c->rcv.bind_address=ba;
|
|
|
|
- if (likely(local_addr)){
|
|
|
|
- su2ip_addr(&c->rcv.dst_ip, local_addr);
|
|
|
|
- c->rcv.dst_port=su_getport(local_addr);
|
|
|
|
- }else if (ba){
|
|
|
|
- c->rcv.dst_ip=ba->address;
|
|
|
|
- c->rcv.dst_port=ba->port_no;
|
|
|
|
|
|
+ if (unlikely(ksr_tcp_accept_haproxy && state == S_CONN_ACCEPT)) {
|
|
|
|
+ ret = tcpconn_read_haproxy(c);
|
|
|
|
+
|
|
|
|
+ if (ret == -1) {
|
|
|
|
+ LM_ERR("invalid PROXY protocol header\n");
|
|
|
|
+ goto error;
|
|
|
|
+ } else if (ret == 1) {
|
|
|
|
+ LM_DBG("PROXY protocol did not override IP addresses\n");
|
|
|
|
+ goto read_ip_info;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+read_ip_info:
|
|
|
|
+ su2ip_addr(&c->rcv.src_ip, su);
|
|
|
|
+ c->rcv.src_port=su_getport(su);
|
|
|
|
+ if (likely(local_addr)){
|
|
|
|
+ su2ip_addr(&c->rcv.dst_ip, local_addr);
|
|
|
|
+ c->rcv.dst_port=su_getport(local_addr);
|
|
|
|
+ }else if (ba){
|
|
|
|
+ c->rcv.dst_ip=ba->address;
|
|
|
|
+ c->rcv.dst_port=ba->port_no;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ c->rcv.bind_address=ba;
|
|
print_ip("tcpconn_new: new tcp connection: ", &c->rcv.src_ip, "\n");
|
|
print_ip("tcpconn_new: new tcp connection: ", &c->rcv.src_ip, "\n");
|
|
LM_DBG("on port %d, type %d\n", c->rcv.src_port, type);
|
|
LM_DBG("on port %d, type %d\n", c->rcv.src_port, type);
|
|
init_tcp_req(&c->req, (char*)c+sizeof(struct tcp_connection), rd_b_size);
|
|
init_tcp_req(&c->req, (char*)c+sizeof(struct tcp_connection), rd_b_size);
|