sockets: task #14247, add CMSG and IP_PKTINFO

This commit adds CMSG infrastructure (currently used with recvmsg) and
the IP_PKTINFO socket option.

In order to use IP_PKTINFO, set LWIP_NETBUF_RECVINFO to 1

Unit test is added to verify this feature
This commit is contained in:
Joel Cunningham
2017-04-25 09:40:04 -05:00
parent ca961b9bc2
commit 2f117add7a
7 changed files with 217 additions and 20 deletions

View File

@@ -245,12 +245,10 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
ip_addr_set(&buf->addr, addr);
buf->port = port;
#if LWIP_NETBUF_RECVINFO
{
if (conn->flags & NETCONN_FLAG_PKTINFO) {
/* get the UDP header - always in the first pbuf, ensured by udp_input */
const struct udp_hdr* udphdr = (const struct udp_hdr*)ip_next_header_ptr();
#if LWIP_CHECKSUM_ON_COPY
buf->flags = NETBUF_FLAG_DESTADDR;
#endif /* LWIP_CHECKSUM_ON_COPY */
ip_addr_set(&buf->toaddr, ip_current_dest_addr());
buf->toport_chksum = udphdr->dest;
}
@@ -694,6 +692,7 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
{
struct netconn *conn;
int size;
u8_t init_flags = 0;
conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
if (conn == NULL) {
@@ -714,6 +713,9 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
#if LWIP_UDP
case NETCONN_UDP:
size = DEFAULT_UDP_RECVMBOX_SIZE;
#if LWIP_NETBUF_RECVINFO
init_flags |= NETCONN_FLAG_PKTINFO;
#endif /* LWIP_NETBUF_RECVINFO */
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
@@ -761,7 +763,7 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
#if LWIP_SO_LINGER
conn->linger = -1;
#endif /* LWIP_SO_LINGER */
conn->flags = 0;
conn->flags = init_flags;
return conn;
free_and_return:
memp_free(MEMP_NETCONN, conn);

View File

@@ -1030,8 +1030,7 @@ lwip_recv_tcp_from(struct lwip_sock *sock, struct sockaddr *from, socklen_t *fro
* Keeps sock->lastdata for peeking.
*/
static err_t
lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, const struct iovec *iov, int iovcnt,
struct sockaddr *from, socklen_t *fromlen, u16_t *datagram_len, int dbg_s)
lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16_t *datagram_len, int dbg_s)
{
struct netbuf *buf;
u8_t apiflags;
@@ -1040,7 +1039,7 @@ lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, const struct iovec *iov
int i;
LWIP_UNUSED_ARG(dbg_s);
LWIP_ERROR("lwip_recvfrom_udp_raw: invalid arguments", (iov != NULL) || (iovcnt <= 0), return ERR_ARG;);
LWIP_ERROR("lwip_recvfrom_udp_raw: invalid arguments", (msg->msg_iov != NULL) || (msg->msg_iovlen <= 0), return ERR_ARG;);
if (flags & MSG_DONTWAIT) {
apiflags = NETCONN_DONTBLOCK;
@@ -1069,30 +1068,63 @@ lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, const struct iovec *iov
copied = 0;
/* copy the pbuf payload into the iovs */
for (i = 0; (i < iovcnt) && (copied < buflen); i++) {
for (i = 0; (i < msg->msg_iovlen) && (copied < buflen); i++) {
u16_t len_left = buflen - copied;
if (iov[i].iov_len > len_left) {
if (msg->msg_iov[i].iov_len > len_left) {
copylen = len_left;
} else {
copylen = (u16_t)iov[i].iov_len;
copylen = (u16_t)msg->msg_iov[i].iov_len;
}
/* copy the contents of the received buffer into
the supplied memory buffer */
pbuf_copy_partial(buf->p, (u8_t*)iov[i].iov_base, copylen, copied);
pbuf_copy_partial(buf->p, (u8_t*)msg->msg_iov[i].iov_base, copylen, copied);
copied += copylen;
}
/* Check to see from where the data was.*/
#if !SOCKETS_DEBUG
if (from && fromlen)
if (msg->msg_name && msg->msg_namelen)
#endif /* !SOCKETS_DEBUG */
{
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw(%d): addr=", dbg_s));
ip_addr_debug_print(SOCKETS_DEBUG, netbuf_fromaddr(buf));
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", netbuf_fromport(buf), copied));
if (from && fromlen) {
lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf), from, fromlen);
if (msg->msg_name && msg->msg_namelen) {
lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf),
(struct sockaddr *)msg->msg_name, &msg->msg_namelen);
}
}
/* Initialize flag output */
msg->msg_flags = 0;
if (msg->msg_control){
u8_t wrote_msg = 0;
#if LWIP_NETBUF_RECVINFO
/* Check if packet info was recorded */
if (buf->flags & NETBUF_FLAG_DESTADDR) {
if (IP_IS_V4(&buf->toaddr)) {
#if LWIP_IPV4
if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) {
struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */
struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr);
chdr->cmsg_level = IPPROTO_IP;
chdr->cmsg_type = IP_PKTINFO;
chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pkti->ipi_ifindex = buf->p->if_idx;
inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf)));
wrote_msg = 1;
} else {
msg->msg_flags |= MSG_CTRUNC;
}
#endif /* LWIP_IPV4 */
}
}
#endif /* LWIP_NETBUF_RECVINFO */
if (!wrote_msg) {
msg->msg_controllen = 0;
}
}
@@ -1130,10 +1162,18 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
{
u16_t datagram_len = 0;
struct iovec vec;
struct msghdr msg;
err_t err;
vec.iov_base = mem;
vec.iov_len = len;
err = lwip_recvfrom_udp_raw(sock, flags, &vec, 1, from, fromlen, &datagram_len, s);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_name = from;
msg.msg_namelen = (fromlen ? *fromlen : 0);
err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
s, lwip_strerr(err)));
@@ -1142,6 +1182,9 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
return -1;
}
ret = (ssize_t)LWIP_MIN(LWIP_MIN(len, datagram_len), SSIZE_MAX);
if (fromlen) {
*fromlen = msg.msg_namelen;
}
}
sock_set_errno(sock, 0);
@@ -1240,8 +1283,7 @@ lwip_recvmsg(int s, struct msghdr *message, int flags)
{
u16_t datagram_len = 0;
err_t err;
err = lwip_recvfrom_udp_raw(sock, flags, message->msg_iov, message->msg_iovlen,
(struct sockaddr *)message->msg_name, &message->msg_namelen, &datagram_len, s);
err = lwip_recvfrom_udp_raw(sock, flags, message, &datagram_len, s);
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
s, lwip_strerr(err)));
@@ -1249,7 +1291,6 @@ lwip_recvmsg(int s, struct msghdr *message, int flags)
done_socket(sock);
return -1;
}
message->msg_flags = 0;
if (datagram_len > buflen) {
message->msg_flags |= MSG_TRUNC;
}
@@ -1590,6 +1631,12 @@ lwip_socket(int domain, int type, int protocol)
DEFAULT_SOCKET_EVENTCB);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
#if LWIP_NETBUF_RECVINFO
if (conn) {
/* netconn layer enables pktinfo by default, sockets default to off */
conn->flags &= ~NETCONN_FLAG_PKTINFO;
}
#endif /* LWIP_NETBUF_RECVINFO */
break;
case SOCK_STREAM:
conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB);
@@ -2896,6 +2943,16 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
s, sock->conn->pcb.ip->tos));
break;
#if LWIP_NETBUF_RECVINFO
case IP_PKTINFO:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
if (*(const int*)optval) {
sock->conn->flags |= NETCONN_FLAG_PKTINFO;
} else {
sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
}
break;
#endif /* LWIP_NETBUF_RECVINFO */
#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS
case IP_MULTICAST_TTL:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);