mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-20 04:30:46 +00:00
MEDIUM: quic: retrieve frontend destination address
Retrieve the frontend destination address for a QUIC connection. This address is retrieve from the first received datagram and then stored in the associated quic-conn. This feature relies on IP_PKTINFO or affiliated flags support on the socket. This flag is set for each QUIC listeners in sock_inet_bind_receiver(). To retrieve the destination address, recvfrom() has been replaced by recvmsg() syscall. This operation and parsing of msghdr structure has been extracted in a wrapper quic_recv(). This change is useful to finalize the implementation of 'dst' sample fetch. As such, quic_sock_get_dst() has been edited to return local address from the quic-conn. As a best effort, if local address is not available due to kernel non-support of IP_PKTINFO, address of the listener is returned instead. This should be backported up to 2.6.
This commit is contained in:
parent
90121b3321
commit
97ecc7a8ea
@ -374,6 +374,7 @@ struct quic_dgram {
|
||||
unsigned char *dcid;
|
||||
size_t dcid_len;
|
||||
struct sockaddr_storage saddr;
|
||||
struct sockaddr_storage daddr;
|
||||
struct quic_conn *qc;
|
||||
struct list list;
|
||||
struct mt_list mt_list;
|
||||
@ -636,6 +637,7 @@ struct quic_conn {
|
||||
|
||||
struct ssl_sock_ctx *xprt_ctx;
|
||||
|
||||
struct sockaddr_storage local_addr;
|
||||
struct sockaddr_storage peer_addr;
|
||||
|
||||
/* Used only to reach the tasklet for the I/O handler from this quic_conn object. */
|
||||
|
@ -4590,13 +4590,14 @@ static int parse_retry_token(struct quic_conn *qc,
|
||||
* for QUIC servers (or haproxy listeners).
|
||||
* <dcid> is the destination connection ID, <scid> is the source connection ID,
|
||||
* <token> the token found to be used for this connection with <token_len> as
|
||||
* length. <saddr> is the source address.
|
||||
* length. Endpoints addresses are specified via <local_addr> and <peer_addr>.
|
||||
* Returns the connection if succeeded, NULL if not.
|
||||
*/
|
||||
static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
struct quic_cid *dcid, struct quic_cid *scid,
|
||||
const struct quic_cid *token_odcid,
|
||||
struct sockaddr_storage *saddr,
|
||||
struct sockaddr_storage *local_addr,
|
||||
struct sockaddr_storage *peer_addr,
|
||||
int server, int token, void *owner)
|
||||
{
|
||||
int i;
|
||||
@ -4715,7 +4716,8 @@ static struct quic_conn *qc_new_conn(const struct quic_version *qv, int ipv4,
|
||||
|
||||
qc->streams_by_id = EB_ROOT_UNIQUE;
|
||||
qc->stream_buf_count = 0;
|
||||
memcpy(&qc->peer_addr, saddr, sizeof qc->peer_addr);
|
||||
memcpy(&qc->local_addr, local_addr, sizeof(qc->local_addr));
|
||||
memcpy(&qc->peer_addr, peer_addr, sizeof qc->peer_addr);
|
||||
|
||||
if (server && !qc_lstnr_params_init(qc, &l->bind_conf->quic_params,
|
||||
icid->stateless_reset_token,
|
||||
@ -6028,7 +6030,8 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end,
|
||||
ipv4 = dgram->saddr.ss_family == AF_INET;
|
||||
|
||||
qc = qc_new_conn(qv, ipv4, &pkt->dcid, &pkt->scid, token_odcid,
|
||||
&pkt->saddr, 1, !!pkt->token_len, l);
|
||||
&dgram->daddr, &pkt->saddr, 1,
|
||||
!!pkt->token_len, l);
|
||||
if (qc == NULL)
|
||||
goto drop;
|
||||
|
||||
|
144
src/quic_sock.c
144
src/quic_sock.c
@ -10,10 +10,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* required for struct in6_pktinfo */
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@ -142,14 +144,17 @@ int quic_sock_get_dst(struct connection *conn, struct sockaddr *addr, socklen_t
|
||||
len = sizeof(qc->peer_addr);
|
||||
memcpy(addr, &qc->peer_addr, len);
|
||||
} else {
|
||||
/* FIXME: front connection, no local address for now, we'll
|
||||
* return the listener's address instead.
|
||||
struct sockaddr_storage *from;
|
||||
|
||||
/* Return listener address if IP_PKTINFO or friends are not
|
||||
* supported by the socket.
|
||||
*/
|
||||
BUG_ON(!qc->li);
|
||||
|
||||
if (len > sizeof(qc->li->rx.addr))
|
||||
len = sizeof(qc->li->rx.addr);
|
||||
memcpy(addr, &qc->li->rx.addr, len);
|
||||
from = is_addr(&qc->local_addr) ? &qc->local_addr :
|
||||
&qc->li->rx.addr;
|
||||
if (len > sizeof(*from))
|
||||
len = sizeof(*from);
|
||||
memcpy(addr, from, len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -237,6 +242,7 @@ struct connection *quic_sock_accept_conn(struct listener *l, int *status)
|
||||
*/
|
||||
static int quic_lstnr_dgram_dispatch(unsigned char *buf, size_t len, void *owner,
|
||||
struct sockaddr_storage *saddr,
|
||||
struct sockaddr_storage *daddr,
|
||||
struct quic_dgram *new_dgram, struct list *dgrams)
|
||||
{
|
||||
struct quic_dgram *dgram;
|
||||
@ -260,6 +266,7 @@ static int quic_lstnr_dgram_dispatch(unsigned char *buf, size_t len, void *owner
|
||||
dgram->dcid = dcid;
|
||||
dgram->dcid_len = dcid_len;
|
||||
dgram->saddr = *saddr;
|
||||
dgram->daddr = *daddr;
|
||||
dgram->qc = NULL;
|
||||
LIST_APPEND(dgrams, &dgram->list);
|
||||
MT_LIST_APPEND(&quic_dghdlrs[cid_tid].dgrams, &dgram->mt_list);
|
||||
@ -274,6 +281,111 @@ static int quic_lstnr_dgram_dispatch(unsigned char *buf, size_t len, void *owner
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Receive data from datagram socket <fd>. Data are placed in <out> buffer of
|
||||
* length <len>.
|
||||
*
|
||||
* Datagram addresses will be returned via the next arguments. <from> will be
|
||||
* the peer address and <to> the reception one. Note that <to> can only be
|
||||
* retrieved if the socket supports IP_PKTINFO or affiliated options. If not,
|
||||
* <to> will be set as AF_UNSPEC. The caller must specify <to_port> to ensure
|
||||
* that <to> address is completely filled.
|
||||
*
|
||||
* Returns value from recvmsg syscall.
|
||||
*/
|
||||
static ssize_t quic_recv(int fd, void *out, size_t len,
|
||||
struct sockaddr *from, socklen_t from_len,
|
||||
struct sockaddr *to, socklen_t to_len,
|
||||
uint16_t dst_port)
|
||||
{
|
||||
union pktinfo {
|
||||
#ifdef IP_PKTINFO
|
||||
struct in_pktinfo in;
|
||||
#else /* !IP_PKTINFO */
|
||||
struct in_addr addr;
|
||||
#endif
|
||||
#ifdef IPV6_RECVPKTINFO
|
||||
struct in6_pktinfo in6;
|
||||
#endif
|
||||
};
|
||||
char cdata[CMSG_SPACE(sizeof(union pktinfo))];
|
||||
struct msghdr msg;
|
||||
struct iovec vec;
|
||||
struct cmsghdr *cmsg;
|
||||
ssize_t ret;
|
||||
|
||||
vec.iov_base = out;
|
||||
vec.iov_len = len;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.msg_name = from;
|
||||
msg.msg_namelen = from_len;
|
||||
msg.msg_iov = &vec;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = &cdata;
|
||||
msg.msg_controllen = sizeof(cdata);
|
||||
|
||||
clear_addr((struct sockaddr_storage *)to);
|
||||
|
||||
do {
|
||||
ret = recvmsg(fd, &msg, 0);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
/* TODO handle errno. On EAGAIN/EWOULDBLOCK use fd_cant_recv() if
|
||||
* using dedicated connection socket.
|
||||
*/
|
||||
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
switch (cmsg->cmsg_level) {
|
||||
case IPPROTO_IP:
|
||||
#if defined(IP_PKTINFO)
|
||||
if (cmsg->cmsg_type == IP_PKTINFO) {
|
||||
struct sockaddr_in *in = (struct sockaddr_in *)to;
|
||||
struct in_pktinfo *info = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
||||
|
||||
if (to_len >= sizeof(struct sockaddr_in)) {
|
||||
in->sin_family = AF_INET;
|
||||
in->sin_addr = info->ipi_addr;
|
||||
in->sin_port = dst_port;
|
||||
}
|
||||
}
|
||||
#elif defined(IP_RECVDSTADDR)
|
||||
if (cmsg->cmsg_type == IP_RECVDSTADDR) {
|
||||
struct sockaddr_in *in = (struct sockaddr_in *)to;
|
||||
struct in_addr *info = (struct in_addr *)CMSG_DATA(cmsg);
|
||||
|
||||
if (to_len >= sizeof(struct sockaddr_in)) {
|
||||
in->sin_family = AF_INET;
|
||||
in->sin_addr.s_addr = info->s_addr;
|
||||
in->sin_port = dst_port;
|
||||
}
|
||||
}
|
||||
#endif /* IP_PKTINFO || IP_RECVDSTADDR */
|
||||
break;
|
||||
|
||||
case IPPROTO_IPV6:
|
||||
#ifdef IPV6_RECVPKTINFO
|
||||
if (cmsg->cmsg_type == IPV6_PKTINFO) {
|
||||
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)to;
|
||||
struct in6_pktinfo *info6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
|
||||
|
||||
if (to_len >= sizeof(struct sockaddr_in6)) {
|
||||
in6->sin6_family = AF_INET6;
|
||||
memcpy(&in6->sin6_addr, &info6->ipi6_addr, sizeof(in6->sin6_addr));
|
||||
in6->sin6_port = dst_port;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function called on a read event from a listening socket. It tries
|
||||
* to handle as many connections as possible.
|
||||
*/
|
||||
@ -285,9 +397,8 @@ void quic_sock_fd_iocb(int fd)
|
||||
struct listener *l = objt_listener(fdtab[fd].owner);
|
||||
struct quic_transport_params *params;
|
||||
/* Source address */
|
||||
struct sockaddr_storage saddr = {0};
|
||||
struct sockaddr_storage saddr = {0}, daddr = {0};
|
||||
size_t max_sz, cspace;
|
||||
socklen_t saddrlen;
|
||||
struct quic_dgram *new_dgram;
|
||||
unsigned char *dgram_buf;
|
||||
int max_dgrams;
|
||||
@ -358,18 +469,15 @@ void quic_sock_fd_iocb(int fd)
|
||||
}
|
||||
|
||||
dgram_buf = (unsigned char *)b_tail(buf);
|
||||
saddrlen = sizeof saddr;
|
||||
do {
|
||||
ret = recvfrom(fd, dgram_buf, max_sz, 0,
|
||||
(struct sockaddr *)&saddr, &saddrlen);
|
||||
if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
fd_cant_recv(fd);
|
||||
goto out;
|
||||
}
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
ret = quic_recv(fd, dgram_buf, max_sz,
|
||||
(struct sockaddr *)&saddr, sizeof(saddr),
|
||||
(struct sockaddr *)&daddr, sizeof(daddr),
|
||||
get_net_port(&l->rx.addr));
|
||||
if (ret <= 0)
|
||||
goto out;
|
||||
|
||||
b_add(buf, ret);
|
||||
if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &saddr,
|
||||
if (!quic_lstnr_dgram_dispatch(dgram_buf, ret, l, &saddr, &daddr,
|
||||
new_dgram, &rxbuf->dgrams)) {
|
||||
/* If wrong, consume this datagram */
|
||||
b_del(buf, ret);
|
||||
|
@ -381,6 +381,25 @@ int sock_inet_bind_receiver(struct receiver *rx, char **errmsg)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_QUIC
|
||||
if (rx->proto->proto_type == PROTO_TYPE_DGRAM) {
|
||||
switch (addr_inet.ss_family) {
|
||||
case AF_INET:
|
||||
#if defined(IP_PKTINFO)
|
||||
setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
|
||||
#elif defined(IP_RECVDSTADDR)
|
||||
setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one));
|
||||
#endif /* IP_PKTINFO || IP_RECVDSTADDR */
|
||||
break;
|
||||
case AF_INET6:
|
||||
#ifdef IPV6_RECVPKTINFO
|
||||
setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* USE_QUIC */
|
||||
|
||||
if (!ext && bind(fd, (struct sockaddr *)&addr_inet, rx->proto->fam->sock_addrlen) == -1) {
|
||||
err |= ERR_RETRYABLE | ERR_ALERT;
|
||||
memprintf(errmsg, "cannot bind socket (%s)", strerror(errno));
|
||||
|
Loading…
Reference in New Issue
Block a user