mirror of
https://github.com/nmap/nmap.git
synced 2025-12-10 06:51:33 +00:00
Allow setting certain extension headers as ancillary data in send_ipv6_ip.
This commit is contained in:
@@ -3545,16 +3545,20 @@ bail:
|
|||||||
#elif !WIN32
|
#elif !WIN32
|
||||||
|
|
||||||
/* Add an ancillary cmsghdr data block to the list of blocks in a msghdr.
|
/* Add an ancillary cmsghdr data block to the list of blocks in a msghdr.
|
||||||
The list is stored in msg->msg_control, which is must be allocated to
|
The list is stored in msg->msg_control, which is dynamically allocated
|
||||||
hold at least maxlen bytes. msg->msg_controllen is also modified by this
|
and reallocated as needed. It must be freed after this function returns.
|
||||||
function. Returns -1 in case of error or 0 otherwise. */
|
msg->msg_controllen is also modified by this function. Returns -1 in case of
|
||||||
static int add_ancillary(struct msghdr *msg, size_t maxlen,
|
error or 0 otherwise. */
|
||||||
int level, int type, const void *data, size_t len)
|
static int add_ancillary(struct msghdr *msg, int level, int type,
|
||||||
|
const void *data, size_t len)
|
||||||
{
|
{
|
||||||
struct cmsghdr *cm;
|
struct cmsghdr *cm;
|
||||||
|
void *p;
|
||||||
|
|
||||||
if (maxlen < msg->msg_controllen + CMSG_SPACE(len))
|
p = realloc(msg->msg_control, msg->msg_controllen + CMSG_SPACE(len));
|
||||||
|
if (p == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
msg->msg_control = p;
|
||||||
|
|
||||||
cm = (struct cmsghdr *) ((char *) msg->msg_control + msg->msg_controllen);
|
cm = (struct cmsghdr *) ((char *) msg->msg_control + msg->msg_controllen);
|
||||||
msg->msg_controllen += CMSG_SPACE(len);
|
msg->msg_controllen += CMSG_SPACE(len);
|
||||||
@@ -3568,33 +3572,69 @@ static int add_ancillary(struct msghdr *msg, size_t maxlen,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int exthdr_type_to_cmsg_type(uint8_t type) {
|
||||||
|
switch (type) {
|
||||||
|
/* These are the only extension headers we can set directly through a
|
||||||
|
msghdr. */
|
||||||
|
case 0:
|
||||||
|
return IPV6_HOPOPTS;
|
||||||
|
case 43:
|
||||||
|
return IPV6_RTHDR;
|
||||||
|
case 60:
|
||||||
|
return IPV6_DSTOPTS;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const unsigned char *add_exthdr_ancillary(struct msghdr *msg,
|
||||||
|
const unsigned char *p, size_t len, unsigned char *proto) {
|
||||||
|
unsigned char nxt;
|
||||||
|
size_t extlen;
|
||||||
|
int cmsg_type;
|
||||||
|
|
||||||
|
cmsg_type = exthdr_type_to_cmsg_type(*proto);
|
||||||
|
if (cmsg_type == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (len < 2)
|
||||||
|
return NULL;
|
||||||
|
nxt = *p;
|
||||||
|
extlen = (*(p + 1) + 1) * 8;
|
||||||
|
if (len < extlen)
|
||||||
|
return NULL;
|
||||||
|
if (add_ancillary(msg, IPPROTO_IPV6, cmsg_type, p, extlen) == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
*proto = nxt;
|
||||||
|
|
||||||
|
return p + extlen;
|
||||||
|
}
|
||||||
|
|
||||||
/* Send an IPv6 packet over a raw socket. This function can control all header
|
/* Send an IPv6 packet over a raw socket. This function can control all header
|
||||||
fields except the flow label (and the payload length can only be controlled
|
fields except the flow label (and the payload length can only be controlled
|
||||||
indirectly through the length of the payload). While there are standard
|
indirectly through the length of the payload).
|
||||||
functions to add extension headers, we don't use them, instead opening the
|
|
||||||
socket with a protocol the same as the Next Header field in the packet. Then
|
For most extension header types, we initialize the socket with the given
|
||||||
we paste in the packet contents (including extension headers) verbatim. This
|
protocol, which causes the Next Header field to match when the packet is set.
|
||||||
allows controlling the order of headers, making broken headers, and sending
|
This allows stuffing arbitrary data into extension headers. However, for a
|
||||||
headers not understood by the underlying OS. */
|
few well-known headers (like Destination and Routing options), this fails
|
||||||
|
with EPROTOTYPE because there are specialized functions to add these headers
|
||||||
|
using the IPv6 socket API. These do not offer as much control because they
|
||||||
|
are controlled by the OS, and may be reordered, for example. */
|
||||||
static int send_ipv6_ip(const unsigned char *packet, size_t packetlen) {
|
static int send_ipv6_ip(const unsigned char *packet, size_t packetlen) {
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
struct sockaddr_in6 dest = { 0 };
|
struct sockaddr_in6 dest = { 0 };
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
|
|
||||||
|
const unsigned char *end;
|
||||||
struct ip6_hdr *hdr;
|
struct ip6_hdr *hdr;
|
||||||
|
unsigned char nxt;
|
||||||
int tclass, hoplimit;
|
int tclass, hoplimit;
|
||||||
|
|
||||||
char *control;
|
|
||||||
size_t controllen;
|
|
||||||
int sd;
|
int sd;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
/* Allocate a control buffer big enough to hold the IPV6_TCLASS and
|
|
||||||
IPV6_HOPLIMIT options. This is a byte buffer but must be aligned for
|
|
||||||
a struct cmsghdr. See section 15.7 (p. 425) of Unix Network
|
|
||||||
Programming, third edition. */
|
|
||||||
controllen = CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(int));
|
|
||||||
control = (char *) safe_zalloc(controllen);
|
|
||||||
sd = -1;
|
sd = -1;
|
||||||
n = -1;
|
n = -1;
|
||||||
|
|
||||||
@@ -3603,7 +3643,7 @@ static int send_ipv6_ip(const unsigned char *packet, size_t packetlen) {
|
|||||||
msg.msg_namelen = sizeof(dest);
|
msg.msg_namelen = sizeof(dest);
|
||||||
msg.msg_iov = &iov;
|
msg.msg_iov = &iov;
|
||||||
msg.msg_iovlen = 1;
|
msg.msg_iovlen = 1;
|
||||||
msg.msg_control = control;
|
msg.msg_control = NULL;
|
||||||
msg.msg_controllen = 0;
|
msg.msg_controllen = 0;
|
||||||
msg.msg_flags = 0;
|
msg.msg_flags = 0;
|
||||||
|
|
||||||
@@ -3615,20 +3655,17 @@ static int send_ipv6_ip(const unsigned char *packet, size_t packetlen) {
|
|||||||
memcpy(&dest.sin6_addr.s6_addr, &hdr->ip6_dst, sizeof(dest.sin6_addr.s6_addr));
|
memcpy(&dest.sin6_addr.s6_addr, &hdr->ip6_dst, sizeof(dest.sin6_addr.s6_addr));
|
||||||
dest.sin6_port = 0;
|
dest.sin6_port = 0;
|
||||||
|
|
||||||
iov.iov_base = (unsigned char *) packet + sizeof(*hdr);
|
|
||||||
iov.iov_len = packetlen - sizeof(*hdr);
|
|
||||||
|
|
||||||
/* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_TCLASS). */
|
/* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_TCLASS). */
|
||||||
#ifdef IPV6_TCLASS
|
#ifdef IPV6_TCLASS
|
||||||
tclass = ntohl(hdr->ip6_flow & IP6_FLOWINFO_MASK) >> 20;
|
tclass = ntohl(hdr->ip6_flow & IP6_FLOWINFO_MASK) >> 20;
|
||||||
if (add_ancillary(&msg, controllen, IPPROTO_IPV6,
|
if (add_ancillary(&msg, IPPROTO_IPV6,
|
||||||
IPV6_TCLASS, &tclass, sizeof(tclass)) == -1) {
|
IPV6_TCLASS, &tclass, sizeof(tclass)) == -1) {
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_UNICAST_HOPS). */
|
/* This can also be set with setsockopt(IPPROTO_IPV6, IPV6_UNICAST_HOPS). */
|
||||||
hoplimit = hdr->ip6_hlim;
|
hoplimit = hdr->ip6_hlim;
|
||||||
if (add_ancillary(&msg, controllen, IPPROTO_IPV6,
|
if (add_ancillary(&msg, IPPROTO_IPV6,
|
||||||
IPV6_HOPLIMIT, &hoplimit, sizeof(hoplimit)) == -1) {
|
IPV6_HOPLIMIT, &hoplimit, sizeof(hoplimit)) == -1) {
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
@@ -3636,18 +3673,47 @@ static int send_ipv6_ip(const unsigned char *packet, size_t packetlen) {
|
|||||||
length is set in the call to sendmsg. There's no way to set the flow
|
length is set in the call to sendmsg. There's no way to set the flow
|
||||||
label. */
|
label. */
|
||||||
|
|
||||||
sd = socket(AF_INET6, SOCK_RAW, hdr->ip6_nxt);
|
/* We must loop until we find a nh value acceptable to the operating system
|
||||||
|
(one that can be passed as the third parameter to socket). In my tests on
|
||||||
|
OS X, you get EPROTOTYPE "Protocol wrong type for socket" for
|
||||||
|
43 routing
|
||||||
|
44 fragment
|
||||||
|
50 ESP
|
||||||
|
51 AH
|
||||||
|
60 DSTOPT
|
||||||
|
108 IPcomp
|
||||||
|
Some of these we are able to handle with ancillary data. When that's
|
||||||
|
possible, we skip over the header, add the ancillary data, and try again
|
||||||
|
with the next header. */
|
||||||
|
end = packet + packetlen;
|
||||||
|
packet += sizeof(*hdr);
|
||||||
|
nxt = hdr->ip6_nxt;
|
||||||
|
for (;;) {
|
||||||
|
errno = 0;
|
||||||
|
sd = socket(AF_INET6, SOCK_RAW, nxt);
|
||||||
|
if (!(sd == -1 && errno == EPROTOTYPE))
|
||||||
|
break;
|
||||||
|
packet = add_exthdr_ancillary(&msg, packet, end - packet, &nxt);
|
||||||
|
if (packet == NULL) {
|
||||||
|
netutil_error("Can't add extension header %u as ancillary data", nxt);
|
||||||
|
goto bail;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (sd == -1) {
|
if (sd == -1) {
|
||||||
perror("socket");
|
perror("socket");
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(packet <= end);
|
||||||
|
iov.iov_base = (unsigned char *) packet;
|
||||||
|
iov.iov_len = end - packet;
|
||||||
|
|
||||||
n = sendmsg(sd, &msg, 0);
|
n = sendmsg(sd, &msg, 0);
|
||||||
if (n == -1)
|
if (n == -1)
|
||||||
perror("sendmsg");
|
perror("sendmsg");
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
free(control);
|
free(msg.msg_control);
|
||||||
if (sd != -1)
|
if (sd != -1)
|
||||||
close(sd);
|
close(sd);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user