Use libnetutil's IPv6 sending for Nping

This commit is contained in:
dmiller 2026-06-15 15:14:07 +00:00
parent fc305b0187
commit 60592283d3
5 changed files with 30 additions and 247 deletions

View file

@ -848,17 +848,6 @@ bool NpingOps::ipv6(){
} /* End of ipv6() */
/* Returns true if we are sending IPv6 packets at raw TCP level (using a
* useless and boring IPv6 socket that doesn't let us include our own IPv6
* header)*/
bool NpingOps::ipv6UsingSocket(){
if( this->getIPVersion() == IP_VERSION_6 && this->sendEth()==false)
return true;
else
return false;
} /* End of ipv6UsingSocket() */
/* Returns AF_INET or AF_INET6, depending on current configuration */
int NpingOps::af(){
if( this->getIPVersion() == IP_VERSION_6 )

View file

@ -372,7 +372,6 @@ class NpingOps {
bool issetIPVersion();
bool ipv4();
bool ipv6();
bool ipv6UsingSocket();
int af();
/* Privileges */

View file

@ -304,13 +304,8 @@ int ProbeMode::start(){
if( o.getMode()!=ARP && o.sendEth()==false ){
/* Get socket descriptor. No need for it in ARP since we send at eth level */
if ((rawipsd = obtainRawSocket()) < 0 )
if ((rawipsd = netutil_raw_socket(o.issetDevice() ? o.getDevice() : NULL)) < 0 )
nping_fatal(QT_3,"Couldn't acquire raw socket. Are you root?");
if ( o.issetDevice() ) {
if (!socket_bindtodevice(rawipsd, o.getDevice()) && errno != EPERM) {
nping_warning(QT_2, "Error binding socket to device %s", o.getDevice() );
}
}
}
/* Check if we have enough information to get the party started */
@ -675,75 +670,9 @@ int ProbeMode::createIPv6(IPv6Header *i, PacketElement *next_element, const char
} /* End of createIPv6() */
/** This function is a bit tricky. The thing is that some engineer had
* the brilliant idea to remove IP_HDRINCL support in IPv6. As a result, it's
* a big pain in the ass to create raw IPv6 headers because we can only do it
* if we are sending packets at raw Ethernet level. So if we want our own IPv6
* header (for source IP spoofing, etc) we have to do things like determine
* source and dest MAC addresses (this is even more complicated in IPv6 than
* in IPv4 because we don't have ARP anymore, we have to use something new, the
* NDP, Neighbor Discovery Protocol.)
* So the thing is that, if the user does not want to play with the IPv6 header,
* why bother with all that link layer work? So what we do is create raw
* transport layer packets and then send them through a raw IPv6 socket. The
* socket will encapsulate our packets into a nice clean IPv6 header
* automatically so we don't have to worry about low level details anymore.
*
* So this function basically takes a raw IPv6 socket descriptor and then tries
* to set some basic parameters (like Hop Limit) using setsockopt() calls.
* It always returns OP_SUCCESS. However, if errors are found, they are printed
* (QT_2 level) using nping_warning();
* */
int ProbeMode::doIPv6ThroughSocket(int rawfd){
/* Hop Limit */
int hoplimit=0;
if( o.issetHopLimit() )
hoplimit= o.getHopLimit();
else if ( o.issetTraceroute() ){
hoplimit= (o.getCurrentRound()<255)? o.getCurrentRound() : (o.getCurrentRound()%255)+1;
}else{
hoplimit=DEFAULT_IPv6_TTL;
}
if( setsockopt(rawfd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&hoplimit, sizeof(hoplimit)) != 0 )
nping_warning(QT_2, "doIPv6ThroughSocket(): setsockopt() for Unicast Hop Limit on IPv6 socket failed");
if( setsockopt(rawfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hoplimit, sizeof(hoplimit)) != 0 )
nping_warning(QT_2, "doIPv6ThroughSocket(): setsockopt() for Multicast Hop Limit on IPv6 socket failed");
#ifdef IPV6_CHECKSUM /* This is not available in when compiling with MinGW */
/* Transport layer checksum */
/* This is totally crazy. We have to tell the kernel EXPLICITLY that we
* want it to set the TCP/UDP checksum for us. Why the hell is this the
* default behavior if it's so incredibly difficult to get the IPv6 source
* address?
* Additionally, we have to be very careful not to set this option when
* dealing with ICMPv6 because in that case the kernel computes the
* checksum automatically and Nping can actually crash if we've set
* this option manually, can you believe it? */
if( o.getMode()==TCP || o.getMode()==UDP){
/* We don't request valid TCP checksums if the user requested bogus sums */
if( o.getBadsum()==false ){
int offset = 16;
if( setsockopt (rawfd, IPPROTO_IPV6, IPV6_CHECKSUM, (char *)&offset, sizeof(offset)) != 0 )
nping_warning(QT_2, "doIPv6ThroughSocket(): failed to set IPV6_CHECKSUM option on IPv6 socket. ");
}
}
#endif
return OP_SUCCESS;
} /* End of doIPv6ThroughSocket() */
/** This function handles TCP packet creation. However, the final packet that
* it produces also includes an IP header.
* There is one exception. When we are sending IPv6 packet at raw TCP level,
* the returned packet does not contain an IPv6 header but the supplied
* rawfd socket descriptor is ready to go because some options have been
* set on it by doIPv6ThroughSocket(). */
*/
int ProbeMode::fillPacketTCP(NpingTarget *target, u16 port, u8 *buff, int bufflen, int *filledlen, int rawfd){
IPv4Header i;
@ -813,47 +742,19 @@ int ProbeMode::fillPacketTCP(NpingTarget *target, u16 port, u8 *buff, int buffle
break;
case IP_VERSION_6:
if( o.sendEth() ){
/* Fill the IPv6Header object with the info from NpingOps */
createIPv6(&i6, &t, "TCP", target);
/* Fill the IPv6Header object with the info from NpingOps */
createIPv6(&i6, &t, "TCP", target);
if( o.getBadsum() == true )
t.setSumRandom();
else{
*filledlen = i6.dumpToBinaryBuffer(buff, bufflen);
ip6_checksum(buff, *filledlen); /* Provided by dnet */
return OP_SUCCESS;
}
/* Store result in user supplied buffer */
if( o.getBadsum() == true )
t.setSumRandom();
else{
*filledlen = i6.dumpToBinaryBuffer(buff, bufflen);
}else{
doIPv6ThroughSocket(rawfd);
/* Set some bogus checksum */
if( o.getBadsum()==true )
t.setSumRandom();
/* Set checksum to zero and pray for the kernel to set it to
* the right value. Brothers and sisters:
*
* Our TCP/IP stack, Who is in the kernel,
* Holy is Your Name;
* Your kingdom come,
* Your will be done,
* on userland as it is in kernel space.
* Give us this day our TCP checksum,
* and forgive us for our raw sockets,
* as we forgive you for your kernel panics;
* and lead us not into /dev/null,
* but deliver our packet to the next hop. Amen.
* */
else
t.setSum(0);
/* Since we cannot include our own header like we do in IPv4, the
* buffer we return is the TCP one. */
*filledlen = t.dumpToBinaryBuffer(buff, bufflen);
ip6_checksum(buff, *filledlen); /* Provided by dnet */
return OP_SUCCESS;
}
/* Store result in user supplied buffer */
*filledlen = i6.dumpToBinaryBuffer(buff, bufflen);
break;
default:
@ -873,10 +774,7 @@ int ProbeMode::fillPacketTCP(NpingTarget *target, u16 port, u8 *buff, int buffle
/** This function handles UDP packet creation. However, the final packet that
* it produces also includes an IP header.
* There is one exception. When we are sending IPv6 packet at raw TCP level,
* the returned packet does not contain an IPv6 header but the supplied
* rawfd socket descriptor is ready to go because some options have been
* set on it by doIPv6ThroughSocket(). */
*/
int ProbeMode::fillPacketUDP(NpingTarget *target, u16 port, u8 *buff, int bufflen, int *filledlen, int rawfd){
IPv4Header i;
@ -932,36 +830,18 @@ int ProbeMode::fillPacketUDP(NpingTarget *target, u16 port, u8 *buff, int buffle
break;
case IP_VERSION_6:
/* Fill the IPv6Header object with the info from NpingOps */
createIPv6(&i6, &u, "UDP", target);
if( o.sendEth() ){
/* Fill the IPv6Header object with the info from NpingOps */
createIPv6(&i6, &u, "UDP", target);
if( o.getBadsum() == true ){
u.setSumRandom();
/* Store result in user supplied buffer */
*filledlen = i6.dumpToBinaryBuffer(buff, bufflen);
}
else{
*filledlen = i6.dumpToBinaryBuffer(buff, bufflen);
ip6_checksum(buff, *filledlen); /* Provided by dnet */
return OP_SUCCESS;
}
}else{
doIPv6ThroughSocket(rawfd);
/* Set some bogus checksum */
if( o.getBadsum()==true )
u.setSumRandom();
/* Set checksum to zero and assume the kernel is gonna set the
* right value. If it doesn't, it's not that important since
* UDP checksum is optional and can safely be set to zero */
else
u.setSum(0);
/* Since we cannot include our own header like we do in IPv4, the
* buffer we return is the UDP one. */
*filledlen = u.dumpToBinaryBuffer(buff, bufflen);
if( o.getBadsum() == true ){
u.setSumRandom();
/* Store result in user supplied buffer */
*filledlen = i6.dumpToBinaryBuffer(buff, bufflen);
}
else{
*filledlen = i6.dumpToBinaryBuffer(buff, bufflen);
ip6_checksum(buff, *filledlen); /* Provided by dnet */
return OP_SUCCESS;
}
break;
@ -1599,14 +1479,6 @@ void ProbeMode::probe_nping_event_handler(nsock_pool nsp, nsock_event nse, void
if( mypacket->type==PKT_TYPE_ARP_RAW )
getPacketStrInfo("ARP",mypacket->pkt+14, mypacket->pktLen-14, pktinfobuffer, 512);
else if ( o.ipv6UsingSocket() ){
size_t sslen;
struct sockaddr_storage ss_src;
struct sockaddr_storage ss_dst;
mypacket->target->getSourceSockAddr(&ss_src, &sslen);
mypacket->target->getTargetSockAddr(&ss_dst, &sslen);
getPacketStrInfo("IPv6_NO_HEADER", mypacket->pkt, mypacket->pktLen, pktinfobuffer, 512, &ss_src, &ss_dst );
}
else
getPacketStrInfo("IP", mypacket->pkt+link_offset, mypacket->pktLen-link_offset, pktinfobuffer, 512);

View file

@ -110,7 +110,6 @@ class ProbeMode {
static int createIPv4(IPv4Header *i, PacketElement *next_element, const char *next_proto, NpingTarget *target);
static int createIPv6(IPv6Header *i, PacketElement *next_element, const char *next_proto, NpingTarget *target);
static int doIPv6ThroughSocket(int rawfd);
static int fillPacket(NpingTarget *target, u16 port, u8 *buff, int bufflen, int *filledlen, int rawfd);
static int fillPacketTCP(NpingTarget *target, u16 port, u8 *buff, int bufflen, int *filledlen, int rawfd);
static int fillPacketUDP(NpingTarget *target, u16 port, u8 *buff, int bufflen, int *filledlen, int rawfd);

View file

@ -375,8 +375,6 @@ bool isICMPCode(u8 code, u8 type){
const char *arppackethdrinfo(const u8 *packet, u32 len, int detail );
int arppackethdrinfo(const u8 *packet, u32 len, u8 *dstbuff, u32 dstlen);
int tcppackethdrinfo(const u8 *packet, size_t len, u8 *dstbuff, size_t dstlen, int detail, const char *src, const char *dst);
int udppackethdrinfo(const u8 *packet, size_t len, u8 *dstbuff, size_t dstlen, int detail, const char *src, const char *dst);
/* This function fills buffer "dstbuff" with a printable string that
* represents the supplied packet. When sending IPv6 packet at raw TCP
@ -398,39 +396,12 @@ int getPacketStrInfo(const char *proto, const u8 *packet, u32 len, u8 *dstbuff,
else
detail=LOW_DETAIL;
if( !strcasecmp(proto, "IP") || !strcasecmp(proto, "IPv4") || !strcasecmp(proto, "IPv6")){
b=(char *)ippackethdrinfo(packet, len, detail);
strncpy((char*)dstbuff, b, dstlen);
dstbuff[dstlen-1]=0; /* Just to be sure, NULL-terminate the last position*/
}else if( !strcasecmp(proto, "ARP") || !strcasecmp(proto, "RARP") ){
if( !strcasecmp(proto, "ARP") || !strcasecmp(proto, "RARP") ){
return arppackethdrinfo(packet, len, dstbuff, dstlen);
}else if( !strcasecmp(proto, "IPv6_NO_HEADER") || o.ipv6UsingSocket() ){
char srcipstring[128];
const char *src = NULL;
char dstipstring[128];
const char *dst = NULL;
if (ss_src) {
src = inet_ntop_ez(ss_src, sizeof(*ss_src));
if (src) {
Strncpy(srcipstring, src, sizeof(srcipstring));
src = srcipstring;
}
}
if (ss_dst) {
dst = inet_ntop_ez(ss_dst, sizeof(*ss_dst));
if (dst) {
Strncpy(dstipstring, dst, sizeof(dstipstring));
dst = dstipstring;
}
}
if( o.getMode()==TCP )
return tcppackethdrinfo(packet, len, dstbuff, dstlen, detail, src, dst);
else if ( o.getMode()==UDP )
return udppackethdrinfo(packet, len, dstbuff, dstlen, detail, src, dst);
else
nping_fatal(QT_3, "getPacketStrInfo(): Unable to determinate transport layer protocol");
}else{
nping_fatal(QT_3, "getPacketStrInfo(): Unknown protocol");
}
else {
b=(char *)ippackethdrinfo(packet, len, detail);
strncpy((char*)dstbuff, b, dstlen-1);
}
return OP_SUCCESS;
} /* getPacketStrInfo() */
@ -903,27 +874,6 @@ int arppackethdrinfo(const u8 *packet, u32 len, u8 *dstbuff, u32 dstlen){
} /* End of arppackethdrinfo() */
int tcppackethdrinfo(const u8 *packet, size_t len, u8 *dstbuff, size_t dstlen,
int detail, const char *src, const char *dst){
assert(packet);
assert(dstbuff);
return (tcppackethdrinfo(packet, len, (char *)dstbuff, dstlen,
detail, 0, src, dst) > 0 ? OP_SUCCESS : OP_FAILURE);
} /* End of tcppackethdrinfo() */
int udppackethdrinfo(const u8 *packet, size_t len, u8 *dstbuff, size_t dstlen,
int detail, const char *src, const char *dst){
assert(packet);
assert(dstbuff);
return (udppackethdrinfo(packet, len, (char *)dstbuff, dstlen,
detail, 0, src, dst) > 0 ? OP_SUCCESS : OP_FAILURE);
} /* End of udppackethdrinfo() */
/** Returns a random (null-terminated) ASCII string with no special
* meaning. Returned string may be between 1 and 512 bytes and contain
* random letters and some whitespace.
@ -969,33 +919,7 @@ int send_packet(NpingTarget *target, int rawfd, u8 *pkt, size_t pktLen){
memset(&s6, 0, sizeof(struct sockaddr_in6));
s6.sin6_family=AF_INET6;
s6.sin6_addr = target->getIPv6Address();
/*
if( o.getMode()==TCP ){
dport=getDstPortFromTCPHeader(pkt, pktLen);
if(dport!=NULL)
s6.sin6_port = *dport;
else
nping_fatal(QT_3, "send_packet(): Could not determine TCP destination port.");
}
else if( o.getMode()==UDP){
dport=getDstPortFromUDPHeader(pkt, pktLen);
if(dport!=NULL)
s6.sin6_port = *dport;
else
nping_fatal(QT_3, "send_packet(): Could not determine UDP destination port.");
}
*/
/* Linux doesn't seem to like sin6_port to be set to other value
* than 0. Unless we set it to zero, the sendto() call returns
* "Invalid argument" error. Does this happen in other systems?
* TODO: Should we check here if #ifdef LINUX and set the port to
* zero? */
s6.sin6_port=0;
res = Sendto("send_packet", rawfd, pkt, pktLen, 0, (struct sockaddr *)&s6, (int) sizeof(struct sockaddr_in6));
/*Sendto returns errors as -1 according to netutil.cc so lets catch that and return OP_FAILURE*/
res = send_ipv6_packet_eth_or_sd(rawfd, NULL, &s6, pkt, pktLen);
if (res == -1) return OP_FAILURE;
}else{ /* IPv4 */
struct sockaddr_storage dst;