IP_HDRINCL byte ordering
Roy Marples <[email protected]> Mon, 23 Jun 2025 16:33:25 +0100
| Newsgroups | gmane.os.netbsd.devel.network |
|---|---|
| Message-ID | <[email protected]> |
Since inception on NetBSD, IP_HDRINCL for RAW sockets requires ip_len and ip_off to by sent in host byte order.
https://nxr.netbsd.org/xref/src/sys/netinet/raw_ip.c#369
This makes literally no sense because reading from the same socket you get ip_len and ip_off in network byte order.
Writing to a bpf socket, ip_len and ip_off have to be done in network byte order.
No other part of the system behaves like this (that I know of) and it's not documented in ip(4).
Every other OS does not do this. Or if they did, they don't do it now.
Our Linux compat support for this socket option does nothing special here, so it won't work for Linux binaries.
There is even a comment by a NetBSD developer in the dhclient code complaining that the raw socket code didn't
work on NetBSD and this is likely why as the ancient code there assumes network byte ordering for these fields.
Attached is a patch to fix this by adding IP_HDRINCL_RAW and mapping the linux compat socket option to this.
This allows old binaries and old software to compile and work as before.
Portable software should then be written thusly, assuming all target platforms are modern.
#ifdef IP_HDRINCL_RAW
setsockopt(s, IPPROTO_IP, IP_HDRINCL_RAW, &hincl, sizeof(hincl));
#else
#ifdef __NetBSD__
// Either error here or handle byte swapping yourself as before
#endif
setsockopt(s, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl));
#endif
Comments, as always, welcome.
Roy
IP_HDRINCL_RAW.patch
(application/octet-stream, 11.9 KB)
diff -r dd9486957114 sbin/ping/ping.c
--- a/sbin/ping/ping.c Sat Jun 21 02:45:57 2025 +0000
+++ b/sbin/ping/ping.c Mon Jun 23 16:21:49 2025 +0100
@@ -562,14 +562,14 @@
- optlen);
(void) memcpy(opack_ip + 1, optspace, optlen);
- if (prog_setsockopt(s, IPPROTO_IP, IP_HDRINCL,
+ if (prog_setsockopt(s, IPPROTO_IP, IP_HDRINCL_RAW,
(char *) &on, sizeof(on)) < 0)
err(EXIT_FAILURE, "Can't set special IP header");
opack_ip->ip_v = IPVERSION;
opack_ip->ip_hl = (sizeof(struct ip)+optlen) >> 2;
opack_ip->ip_tos = tos;
- opack_ip->ip_off = (pingflags & F_DF) ? IP_DF : 0;
+ opack_ip->ip_off = ntohs((pingflags & F_DF) ? IP_DF : 0);
opack_ip->ip_ttl = ttl ? ttl : MAXTTL;
opack_ip->ip_p = IPPROTO_ICMP;
opack_ip->ip_src = src_addr.sin_addr;
@@ -914,7 +914,7 @@
opack_icmp.icmp_cksum = in_cksum((u_int16_t *)&opack_icmp,
ICMP_MINLEN);
sw = 0;
- if (prog_setsockopt(sloop, IPPROTO_IP, IP_HDRINCL,
+ if (prog_setsockopt(sloop, IPPROTO_IP, IP_HDRINCL_RAW,
(char *)&sw, sizeof(sw)) < 0)
err(EXIT_FAILURE, "Can't turn off special IP header");
if (prog_sendto(sloop, (char *) &opack_icmp,
@@ -953,7 +953,7 @@
opack_icmp.icmp_cksum = in_cksum((u_int16_t *)&opack_icmp, cc);
cc += opack_ip->ip_hl<<2;
- opack_ip->ip_len = cc;
+ opack_ip->ip_len = htons(cc);
i = prog_sendto(s, (char *) opack_ip, cc, 0,
(struct sockaddr *)&send_addr, sizeof(struct sockaddr_in));
if (i != cc) {
@@ -1131,9 +1131,9 @@
SET(ntohs((u_int16_t)icp->icmp_seq));
}
- if (tot_len != opack_ip->ip_len) {
+ if (tot_len != ntohs(opack_ip->ip_len)) {
PR_PACK_SUB();
- switch (opack_ip->ip_len - tot_len) {
+ switch (ntohs(opack_ip->ip_len) - tot_len) {
case MAX_IPOPTLEN:
if ((pongflags & F_RECORD_ROUTE) != 0)
break;
@@ -1155,7 +1155,7 @@
default:
out:
(void)printf("\nwrong total length %d "
- "instead of %d", tot_len, opack_ip->ip_len);
+ "instead of %d", tot_len, ntohs(opack_ip->ip_len));
break;
}
}
@@ -1774,9 +1774,9 @@
(void)printf("\n Vr HL TOS Len ID Flg off TTL Pro cks Src Dst\n");
(void)printf(" %1x %1x %02x %04x %04x",
- ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id);
+ ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len), ip->ip_id);
(void)printf(" %1x %04x",
- ((ip->ip_off)&0xe000)>>13, (ip->ip_off)&0x1fff);
+ (ntohs(ip->ip_off)&0xe000)>>13, (ntohs(ip->ip_off)&0x1fff));
(void)printf(" %02x %02x %04x",
ip->ip_ttl, ip->ip_p, ip->ip_sum);
(void)printf(" %15s ",
diff -r dd9486957114 share/man/man4/ip.4
--- a/share/man/man4/ip.4 Sat Jun 21 02:45:57 2025 +0000
+++ b/share/man/man4/ip.4 Mon Jun 23 16:21:49 2025 +0100
@@ -436,19 +436,34 @@
header prepended to them (based on the destination address and the
protocol number the socket is created with), unless the
.Dv IP_HDRINCL
+or
+.Dv IP_HDRINCL_RAW
option has been set.
Incoming packets are received with IP header and options intact.
.Pp
-.Dv IP_HDRINCL
-indicates the complete IP header is included with the data and may
+.Dv IP_HDRINCL_RAW
+indicates the complete IP header is included with the outgoing packet and may
be used only with the
.Dv SOCK_RAW
type.
+.Pp
+.Dv IP_HDRINCL
+is the same as
+.Dv IP_HDRINCL_RAW
+except that the
+.Fa ip_off
+and
+.Fa ip_len
+fields are in host byte order, rather than network byte order.
.Bd -literal
#include <netinet/ip.h>
int hincl = 1; /* 1 = on, 0 = off */
-setsockopt(s, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl));
+#ifdef IP_HDRINCL_RAW
+ setsockopt(s, IPPROTO_IP, IP_HDRINCL_RAW, &hincl, sizeof(hincl));
+#else
+ setsockopt(s, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl));
+#endif
.Ed
.Pp
Unlike previous
@@ -459,7 +474,8 @@
ip->ip_v = IPVERSION;
ip->ip_hl = hlen >> 2;
ip->ip_id = 0; /* 0 means kernel set appropriate value */
-ip->ip_off = offset;
+ip->ip_off = htons(offset);
+ip->ip_len = htons(len);
.Ed
.Pp
If the header source address is set to
@@ -511,6 +527,15 @@
.Dv IP_RECVPKTINFO
option instead.
Source code compatibility with both environments is thus maintained.
+.Pp
+Portable code that uses
+.Dv IP_HDRINCL
+should #ifdef for
+.Dv IP_HDRINCL_RAW
+and use that first, falling back to
+.Dv IP_HDRINCL
+which should also be network byte order outside of
+.Nx .
.Sh SEE ALSO
.Xr getsockopt 2 ,
.Xr recv 2 ,
diff -r dd9486957114 sys/compat/linux/common/linux_socket.c
--- a/sys/compat/linux/common/linux_socket.c Sat Jun 21 02:45:57 2025 +0000
+++ b/sys/compat/linux/common/linux_socket.c Mon Jun 23 16:21:49 2025 +0100
@@ -946,7 +946,7 @@
case LINUX_IP_TTL:
return IP_TTL;
case LINUX_IP_HDRINCL:
- return IP_HDRINCL;
+ return IP_HDRINCL_RAW;
case LINUX_IP_MULTICAST_TTL:
return IP_MULTICAST_TTL;
case LINUX_IP_MULTICAST_LOOP:
diff -r dd9486957114 sys/netinet/in.h
--- a/sys/netinet/in.h Sat Jun 21 02:45:57 2025 +0000
+++ b/sys/netinet/in.h Mon Jun 23 16:21:49 2025 +0100
@@ -297,6 +297,7 @@
#define IP_PKTINFO 25 /* struct; set default src if/addr */
#define IP_RECVPKTINFO 26 /* int; receive dst if/addr w/dgram */
#define IP_BINDANY 27 /* bool: allow bind to any address */
+#define IP_HDRINCL_RAW 28 /* int; header is included with data */
#define IP_SENDSRCADDR IP_RECVDSTADDR /* FreeBSD compatibility */
/*
diff -r dd9486957114 sys/netinet/in_pcb.h
--- a/sys/netinet/in_pcb.h Sat Jun 21 02:45:57 2025 +0000
+++ b/sys/netinet/in_pcb.h Mon Jun 23 16:21:49 2025 +0100
@@ -178,6 +178,7 @@
#define INP_RECVTTL 0x0800 /* receive incoming IP TTL */
#define INP_RECVPKTINFO 0x1000 /* receive IP dst if/addr */
#define INP_BINDANY 0x2000 /* allow bind to any address */
+#define INP_HDRINCL_RAW 0x4000 /* user supplies entire IP header */
#define INP_CONTROLOPTS (INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\
INP_RECVIF|INP_RECVTTL|INP_RECVPKTINFO)
diff -r dd9486957114 sys/netinet/raw_ip.c
--- a/sys/netinet/raw_ip.c Sat Jun 21 02:45:57 2025 +0000
+++ b/sys/netinet/raw_ip.c Mon Jun 23 16:21:49 2025 +0100
@@ -320,7 +320,7 @@
* If the user handed us a complete IP packet, use it.
* Otherwise, allocate an mbuf for a header and fill it in.
*/
- if ((inp->inp_flags & INP_HDRINCL) == 0) {
+ if ((inp->inp_flags & (INP_HDRINCL | INP_HDRINCL_RAW)) == 0) {
if ((m->m_pkthdr.len + sizeof(struct ip)) > IP_MAXPACKET) {
error = EMSGSIZE;
goto release;
@@ -340,6 +340,8 @@
ip->ip_ttl = in4p_ip(inp).ip_ttl ? in4p_ip(inp).ip_ttl : MAXTTL;
opts = inp->inp_options;
} else {
+ int hlen, ip_len;
+
if (m->m_pkthdr.len > IP_MAXPACKET) {
error = EMSGSIZE;
goto release;
@@ -348,7 +350,26 @@
error = EINVAL;
goto release;
}
+
ip = mtod(m, struct ip *);
+ hlen = ip->ip_hl << 2;
+
+ if (inp->inp_flags & INP_HDRINCL_RAW)
+ ip_len = ntohs(ip->ip_len);
+ else
+ ip_len = ip->ip_len;
+
+ /* Don't allow packet length sizes that will crash. */
+ if (hlen < sizeof(struct ip) || hlen > ip_len ||
+ m->m_pkthdr.len != ip_len) {
+ error = EINVAL;
+ goto release;
+ }
+
+ if (ip->ip_v != IPVERSION) {
+ error = EINVAL;
+ goto release;
+ }
/*
* If the mbuf is read-only, we need to allocate
@@ -356,8 +377,6 @@
* modify the header.
*/
if (M_READONLY(m)) {
- int hlen = ip->ip_hl << 2;
-
m = m_copyup(m, hlen, (max_linkhdr + 3) & ~3);
if (m == NULL) {
error = ENOMEM;
@@ -366,13 +385,10 @@
ip = mtod(m, struct ip *);
}
- /* XXX userland passes ip_len and ip_off in host order */
- if (m->m_pkthdr.len != ip->ip_len) {
- error = EINVAL;
- goto release;
+ if ((inp->inp_flags & INP_HDRINCL_RAW) == 0) {
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_off);
}
- HTONS(ip->ip_len);
- HTONS(ip->ip_off);
if (ip->ip_id != 0 || m->m_pkthdr.len < IP_MINFRAGSIZE)
flags |= IP_NOIPNEWID;
@@ -415,7 +431,7 @@
if (error)
goto out;
if (optval) {
- inp->inp_flags &= ~INP_HDRINCL;
+ inp->inp_flags &= ~(INP_HDRINCL | INP_HDRINCL_RAW);
inp->inp_flags |= INP_NOHEADER;
} else
inp->inp_flags &= ~INP_NOHEADER;
@@ -437,6 +453,15 @@
else
inp->inp_flags &= ~INP_HDRINCL;
break;
+ case IP_HDRINCL_RAW:
+ error = sockopt_getint(sopt, &optval);
+ if (error)
+ break;
+ if (optval)
+ inp->inp_flags |= INP_HDRINCL_RAW;
+ else
+ inp->inp_flags &= ~INP_HDRINCL_RAW;
+ break;
#ifdef MROUTING
case MRT_INIT:
@@ -465,6 +490,10 @@
optval = inp->inp_flags & INP_HDRINCL;
error = sockopt_set(sopt, &optval, sizeof(optval));
break;
+ case IP_HDRINCL_RAW:
+ optval = inp->inp_flags & INP_HDRINCL_RAW;
+ error = sockopt_set(sopt, &optval, sizeof(optval));
+ break;
#ifdef MROUTING
case MRT_VERSION:
diff -r dd9486957114 sys/rump/net/lib/libsockin/sockin_user.c
--- a/sys/rump/net/lib/libsockin/sockin_user.c Sat Jun 21 02:45:57 2025 +0000
+++ b/sys/rump/net/lib/libsockin/sockin_user.c Mon Jun 23 16:21:49 2025 +0100
@@ -107,6 +107,7 @@
translate(IP_MULTICAST_IF);
translate(IP_ADD_MEMBERSHIP);
translate(IP_DROP_MEMBERSHIP);
+ translate(IP_HDRINCL_RAW);
default: return -1;
}
}
diff -r dd9486957114 usr.bin/kdump/kdump.c
--- a/usr.bin/kdump/kdump.c Sat Jun 21 02:45:57 2025 +0000
+++ b/usr.bin/kdump/kdump.c Mon Jun 23 16:21:49 2025 +0100
@@ -703,6 +703,7 @@
CASERETURN(IP_PKTINFO);
CASERETURN(IP_RECVPKTINFO);
CASERETURN(IP_BINDANY);
+ CASERETURN(IP_HDRINCL_RAW);
default:
return NULL;
}
diff -r dd9486957114 usr.sbin/mrouted/igmp.c
--- a/usr.sbin/mrouted/igmp.c Sat Jun 21 02:45:57 2025 +0000
+++ b/usr.sbin/mrouted/igmp.c Mon Jun 23 16:21:49 2025 +0100
@@ -152,7 +152,7 @@
}
iphdrlen = ip->ip_hl << 2;
- ipdatalen = ip->ip_len;
+ ipdatalen = ntohs(ip->ip_len);
if (iphdrlen + ipdatalen != recvlen) {
logit(LOG_WARNING, 0,
"received packet from %s shorter (%u bytes) than hdr+data length (%u+%u)",
@@ -311,7 +311,7 @@
ip = (struct ip *)send_buf;
ip->ip_src.s_addr = src;
ip->ip_dst.s_addr = dst;
- ip->ip_len = MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen;
+ ip->ip_len = htons(MIN_IP_HEADER_LEN + IGMP_MINLEN + datalen);
igmp = (struct igmp *)(send_buf + MIN_IP_HEADER_LEN);
igmp->igmp_type = type;
@@ -335,7 +335,7 @@
sdst.sin_len = sizeof(sdst);
#endif
sdst.sin_addr.s_addr = dst;
- if (sendto(igmp_socket, send_buf, ip->ip_len, 0,
+ if (sendto(igmp_socket, send_buf, ntohs(ip->ip_len), 0,
(struct sockaddr *)&sdst, sizeof(sdst)) < 0) {
if (errno == ENETDOWN)
check_vif_state();
diff -r dd9486957114 usr.sbin/mrouted/kern.c
--- a/usr.sbin/mrouted/kern.c Sat Jun 21 02:45:57 2025 +0000
+++ b/usr.sbin/mrouted/kern.c Mon Jun 23 16:21:49 2025 +0100
@@ -23,11 +23,17 @@
void k_hdr_include(int onoff)
{
+#ifdef IP_HDRINCL_RAW
+ if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL_RAW,
+ (char *)&onoff, sizeof(onoff)) < 0)
+ logit(LOG_ERR, errno, "setsockopt IP_HDRINCL_RAW %u", onoff);
+#else
#ifdef IP_HDRINCL
if (setsockopt(igmp_socket, IPPROTO_IP, IP_HDRINCL,
(char *)&onoff, sizeof(onoff)) < 0)
logit(LOG_ERR, errno, "setsockopt IP_HDRINCL %u", onoff);
#endif
+ #endif
}
diff -r dd9486957114 usr.sbin/traceroute/Makefile
--- a/usr.sbin/traceroute/Makefile Sat Jun 21 02:45:57 2025 +0000
+++ b/usr.sbin/traceroute/Makefile Mon Jun 23 16:21:49 2025 +0100
@@ -9,6 +9,7 @@
CPPFLAGS+=-DHAVE_STRERROR=1 -DHAVE_SETLINEBUF=1 -DHAVE_SOCKADDR_SA_LEN=1
CPPFLAGS+=-DHAVE_RAW_OPTIONS=1
CPPFLAGS+=-DHAVE_ICMP_NEXTMTU=1
+CPPFLAGS+=-DBYTESWAP_IP_HDR=1 -DBYTESWAP_IP_LEN=1
CPPFLAGS+=-DIPSEC
LDADD+= -lipsec
diff -r dd9486957114 usr.sbin/traceroute/traceroute.c
--- a/usr.sbin/traceroute/traceroute.c Sat Jun 21 02:45:57 2025 +0000
+++ b/usr.sbin/traceroute/traceroute.c Mon Jun 23 16:21:49 2025 +0100
@@ -805,7 +805,11 @@
sizeof(packlen)) < 0)
err(1, "SO_SNDBUF");
#endif
-#ifdef IP_HDRINCL
+#ifdef IP_HDRINCL_RAW
+ if (prog_setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL_RAW, (char *)&on,
+ sizeof(on)) < 0)
+ err(1, "IP_HDRINCL");
+#elif defined(IP_HDRINCL)
if (prog_setsockopt(sndsock, IPPROTO_IP, IP_HDRINCL, (char *)&on,
sizeof(on)) < 0)
err(1, "IP_HDRINCL");