Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

patch #10345: Implement IPv6 socket options #22

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
12 changes: 11 additions & 1 deletion src/api/api_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,14 +258,24 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
buf->ptr = p;
ip_addr_set(&buf->addr, addr);
buf->port = port;
buf->flags = 0;
#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();
buf->flags = NETBUF_FLAG_DESTADDR;
buf->flags |= NETBUF_FLAG_DESTADDR;
ip_addr_set(&buf->toaddr, ip_current_dest_addr());
buf->toport_chksum = udphdr->dest;
}
#if LWIP_IPV6
if (netconn_is_flag_set(conn, NETCONN_FLAG_HOPLIMIT)) {
if (ip_current_is_v6()) {
struct ip6_hdr *ip6hdr = (struct ip6_hdr *)ip6_current_header();
buf->flags |= NETBUF_FLAG_HOPLIMIT;
buf->hoplim = IP6H_HOPLIM(ip6hdr);
}
}
#endif /* LWIP_IPV6 */
#endif /* LWIP_NETBUF_RECVINFO */
}

Expand Down
149 changes: 120 additions & 29 deletions src/api/sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -1193,33 +1193,62 @@ lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16
msg->msg_flags = 0;

if (msg->msg_control) {
u8_t wrote_msg = 0;
socklen_t msg_controllen_in = msg->msg_controllen;
msg->msg_controllen = 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!! */
if (msg_controllen_in >= msg->msg_controllen + CMSG_SPACE(sizeof(struct in_pktinfo))) {
struct cmsghdr *chdr = ((u8_t *)msg->msg_control + msg->msg_controllen);
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)));
msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
wrote_msg = 1;
msg->msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo));
} else {
msg->msg_flags |= MSG_CTRUNC;
}
#endif /* LWIP_IPV4 */
}
#if LWIP_IPV6
else if (IP_IS_V6(&buf->toaddr)) {
if (msg_controllen_in >= msg->msg_controllen + CMSG_SPACE(sizeof(struct in6_pktinfo))) {
struct cmsghdr *chdr = ((u8_t *)msg->msg_control + msg->msg_controllen);
struct in6_pktinfo *pkti = (struct in6_pktinfo *)CMSG_DATA(chdr);
chdr->cmsg_level = IPPROTO_IPV6;
chdr->cmsg_type = IPV6_PKTINFO;
chdr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
pkti->ipi6_ifindex = buf->p->if_idx;
inet6_addr_from_ip6addr(&pkti->ipi6_addr, ip_2_ip6(netbuf_destaddr(buf)));
msg->msg_controllen += CMSG_SPACE(sizeof(struct in6_pktinfo));
} else {
msg->msg_flags |= MSG_CTRUNC;
}
}
#endif /* LWIP_IPV6 */
}
#endif /* LWIP_NETBUF_RECVINFO */

if (!wrote_msg) {
msg->msg_controllen = 0;
#if LWIP_IPV6
if (buf->flags & NETBUF_FLAG_HOPLIMIT) {
if (IP_IS_V6(&buf->toaddr)) {
if (msg_controllen_in >= msg->msg_controllen + CMSG_SPACE(sizeof(int))) {
struct cmsghdr *chdr = ((u8_t *)msg->msg_control + msg->msg_controllen);
int *hoplim = (int *)CMSG_DATA(chdr);
chdr->cmsg_level = IPPROTO_IPV6;
chdr->cmsg_type = IPV6_HOPLIMIT;
chdr->cmsg_len = CMSG_LEN(sizeof(int));
*hoplim = buf->hoplim;
msg->msg_controllen += CMSG_SPACE(sizeof(int));
} else {
msg->msg_flags |= MSG_CTRUNC;
}
}
}
#endif /* LWIP_IPV6 */
#endif /* LWIP_NETBUF_RECVINFO */
}

/* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */
Expand Down Expand Up @@ -3080,34 +3109,22 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
break;
#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
case IP_MULTICAST_TTL:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
done_socket(sock);
return ENOPROTOOPT;
}
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
*(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
s, *(int *)optval));
s, *(u8_t *)optval));
break;
case IP_MULTICAST_IF:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr);
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
done_socket(sock);
return ENOPROTOOPT;
}
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, struct in_addr, NETCONN_UDP);
inet_addr_from_ip4addr((struct in_addr *)optval, udp_get_multicast_netif_addr(sock->conn->pcb.udp));
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
s, *(u32_t *)optval));
break;
case IP_MULTICAST_LOOP:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
*(u8_t *)optval = 1;
} else {
*(u8_t *)optval = 0;
}
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
*(u8_t *)optval = udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP) ? 1 : 0;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
s, *(int *)optval));
s, *(u8_t *)optval));
break;
#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
default:
Expand Down Expand Up @@ -3169,12 +3186,38 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt
/* Level: IPPROTO_IPV6 */
case IPPROTO_IPV6:
switch (optname) {
case IPV6_UNICAST_HOPS:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
*(int *)optval = sock->conn->pcb.ip->ttl;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_UNICAST_HOPS) = %d\n",
s, *(int *)optval));
break;
case IPV6_V6ONLY:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int);
*(int *)optval = (netconn_get_ipv6only(sock->conn) ? 1 : 0);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
s, *(int *)optval));
break;
#if LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
case IPV6_MULTICAST_IF:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP);
*(int *)optval = udp_get_multicast_netif_index(sock->conn->pcb.udp);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF) = %d\n",
s, *(int *)optval));
break;
case IPV6_MULTICAST_HOPS:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
*(u8_t *)optval = udp_get_multicast_ttl(sock->conn->pcb.udp);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_HOPS) = %d\n",
s, *(u8_t *)optval));
break;
case IPV6_MULTICAST_LOOP:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, u8_t, NETCONN_UDP);
*(u8_t *)optval = udp_is_flag_set(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP) ? 1 : 0;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_LOOP) = %d\n",
s, *(u8_t *)optval));
break;
#endif /* LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
default:
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
s, optname));
Expand Down Expand Up @@ -3527,9 +3570,9 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
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;
netconn_set_flags(sock->conn, NETCONN_FLAG_PKTINFO);
} else {
sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
netconn_clear_flags(sock->conn, NETCONN_FLAG_PKTINFO);
}
break;
#endif /* LWIP_NETBUF_RECVINFO */
Expand Down Expand Up @@ -3646,6 +3689,12 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
/* Level: IPPROTO_IPV6 */
case IPPROTO_IPV6:
switch (optname) {
case IPV6_UNICAST_HOPS:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
sock->conn->pcb.ip->ttl = *(int *)optval;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_UNICAST_HOPS, ...) -> %d\n",
s, *(int *)optval));
break;
case IPV6_V6ONLY:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
if (*(const int *)optval) {
Expand All @@ -3656,6 +3705,48 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
s, (netconn_get_ipv6only(sock->conn) ? 1 : 0)));
break;
#if LWIP_NETBUF_RECVINFO
case IPV6_RECVPKTINFO:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
if (*(const int *)optval) {
netconn_set_flags(sock->conn, NETCONN_FLAG_PKTINFO);
} else {
netconn_clear_flags(sock->conn, NETCONN_FLAG_PKTINFO);
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_RECVPKTINFO, ..) -> %d\n",
s, *(const int *)optval));
break;
case IPV6_RECVHOPLIMIT:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
if (*(const int *)optval) {
netconn_set_flags(sock->conn, NETCONN_FLAG_HOPLIMIT);
} else {
netconn_clear_flags(sock->conn, NETCONN_FLAG_HOPLIMIT);
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, ..) -> %d\n",
s, *(const int *)optval));
break;
#endif /* LWIP_NETBUF_RECVINFO */
#if LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
case IPV6_MULTICAST_IF:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
udp_set_multicast_netif_index(sock->conn->pcb.udp, (u8_t)(*(int *)optval));
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_MULTICAST_IF, ..) -> %d\n",
s, *(int *)optval));
break;
case IPV6_MULTICAST_HOPS:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t *)optval));
break;
case IPV6_MULTICAST_LOOP:
LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
if (*(const u8_t *)optval) {
udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
} else {
udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
}
break;
#endif /* LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
#if LWIP_IPV6_MLD
case IPV6_JOIN_GROUP:
case IPV6_LEAVE_GROUP: {
Expand Down
2 changes: 1 addition & 1 deletion src/core/ipv6/ip6_addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
/* used by IP6_ADDR_ANY(6) in ip6_addr.h */
const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);

#define lwip_xchar(i) ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10))
#define lwip_xchar(i) ((char)((i) < 10 ? '0' + (i) : 'a' + (i) - 10))

/**
* Check whether "cp" is a valid ascii representation
Expand Down
1 change: 1 addition & 0 deletions src/core/pbuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,7 @@ pbuf_clone(pbuf_layer layer, pbuf_type type, struct pbuf *p)
return NULL;
}
err = pbuf_copy(q, p);
q->if_idx = p->if_idx;
LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
return q;
Expand Down
10 changes: 7 additions & 3 deletions src/include/lwip/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ extern "C" {
#endif /* LWIP_NETBUF_RECVINFO */
/** A FIN has been received but not passed to the application yet */
#define NETCONN_FIN_RX_PENDING 0x80
#if LWIP_NETBUF_RECVINFO
/** The hop limit will be stored for incoming packets */
#define NETCONN_FLAG_HOPLIMIT 0x100
#endif /* LWIP_NETBUF_RECVINFO */

/* Helpers to process several netconn_types by the same code */
#define NETCONNTYPE_GROUP(t) ((t)&0xF0)
Expand Down Expand Up @@ -274,7 +278,7 @@ struct netconn {
s16_t linger;
#endif /* LWIP_SO_LINGER */
/** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
u8_t flags;
u16_t flags;
#if LWIP_TCP
/** TCP: when data passed to netconn_write doesn't fit into the send buffer,
this temporarily stores the message.
Expand Down Expand Up @@ -369,8 +373,8 @@ err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
err_t netconn_err(struct netconn *conn);
#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize)

#define netconn_set_flags(conn, set_flags) do { (conn)->flags = (u8_t)((conn)->flags | (set_flags)); } while(0)
#define netconn_clear_flags(conn, clr_flags) do { (conn)->flags = (u8_t)((conn)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0)
#define netconn_set_flags(conn, set_flags) do { (conn)->flags = (u16_t)((conn)->flags | (set_flags)); } while(0)
#define netconn_clear_flags(conn, clr_flags) do { (conn)->flags = (u16_t)((conn)->flags & (u16_t)(~(clr_flags) & 0xffff)); } while(0)
#define netconn_is_flag_set(conn, flag) (((conn)->flags & (flag)) != 0)

#define netconn_set_callback_arg(conn, arg) do { (conn)->callback_arg.ptr = (arg); } while(0)
Expand Down
5 changes: 5 additions & 0 deletions src/include/lwip/netbuf.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ extern "C" {
#define NETBUF_FLAG_DESTADDR 0x01
/** This netbuf includes a checksum */
#define NETBUF_FLAG_CHKSUM 0x02
/** This netbuf has hop limit set */
#define NETBUF_FLAG_HOPLIMIT 0x04

/** "Network buffer" - contains data and addressing info */
struct netbuf {
Expand All @@ -66,6 +68,9 @@ struct netbuf {
u16_t toport_chksum;
#if LWIP_NETBUF_RECVINFO
ip_addr_t toaddr;
#if LWIP_IPV6
u8_t hoplim;
#endif /* LWIP_IPV6 */
#endif /* LWIP_NETBUF_RECVINFO */
#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
};
Expand Down
16 changes: 16 additions & 0 deletions src/include/lwip/sockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,12 @@ struct linger {
* Options for level IPPROTO_IPV6
*/
#define IPV6_CHECKSUM 7 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
#define IPV6_UNICAST_HOPS 16 /* RFC3493: hop limit in outgoing unicast IPv6 packets */
#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
#define IPV6_RECVPKTINFO 49 /* RFC3542: receive ancillary data for packet */
#define IPV6_PKTINFO 50 /* RFC3542: ancillary data for a packet */
#define IPV6_RECVHOPLIMIT 51 /* RFC3542: receive hop limit for packet */
#define IPV6_HOPLIMIT 52 /* RFC3542: ancillary data containing hop limit for packet */
#endif /* LWIP_IPV6 */

#if LWIP_UDP && LWIP_UDPLITE
Expand All @@ -316,6 +321,10 @@ struct linger {
#define IP_MULTICAST_TTL 5
#define IP_MULTICAST_IF 6
#define IP_MULTICAST_LOOP 7

#define IPV6_MULTICAST_IF 17 /* RFC3493: interface for outgoing multicast packets */
#define IPV6_MULTICAST_HOPS 18 /* RFC3493: hop limit for outgoing multicast packets */
#define IPV6_MULTICAST_LOOP 19 /* RFC3493: a copy of the packet is looped back for local delivery */
#endif /* LWIP_MULTICAST_TX_OPTIONS */

#if LWIP_IGMP
Expand All @@ -338,6 +347,13 @@ struct in_pktinfo {
};
#endif /* LWIP_IPV4 */

#if LWIP_IPV6
struct in6_pktinfo {
struct in6_addr ipi6_addr;
int ipi6_ifindex;
};
#endif /* LWIP_IPV6 */

#if LWIP_IPV6_MLD
/*
* Options and types related to IPv6 multicast membership
Expand Down