1
0
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:
david
2011-09-18 23:46:54 +00:00
parent 074e91c0e0
commit 1887aef067

View File

@@ -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);