From 930ffc2cb41708a9b0c12a273cd16fc69434ec70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Sun, 8 Jan 2023 16:00:40 +0100 Subject: [PATCH 1/3] mldq6: fix required argument to long --sleep option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like the short option, the long option of --sleep|-z should have a required argument of seconds to sleep. Signed-off-by: Linus Lüssing --- tools/mldq6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mldq6.c b/tools/mldq6.c index 5378596..95350be 100644 --- a/tools/mldq6.c +++ b/tools/mldq6.c @@ -157,7 +157,7 @@ int main(int argc, char **argv){ {"mld-resp-delay", required_argument, 0, 'r'}, {"flood-sources", required_argument, 0, 'F'}, {"loop", no_argument, 0, 'l'}, - {"sleep", no_argument, 0, 'z'}, + {"sleep", required_argument, 0, 'z'}, {"verbose", no_argument, 0, 'v'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0 } From bf81972d086294065ff87e789c3356662d7b7cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Sun, 8 Jan 2023 16:08:27 +0100 Subject: [PATCH 2/3] mldq6: fix default sleep interval, to adhere to RFCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default MLD querier interval according to RFC2710 and RFC3810 is 125 seconds, not 120. Signed-off-by: Linus Lüssing --- manuals/mldq6.1 | 2 +- tools/mldq6.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/manuals/mldq6.1 b/manuals/mldq6.1 index c1668a8..927a46b 100644 --- a/manuals/mldq6.1 +++ b/manuals/mldq6.1 @@ -97,7 +97,7 @@ This option instructs the mldq6 tool to send periodic MLD Queries to the destina .TP \-\-sleep, \-z -This option instructs the mldq6 tool to the amount of time to pause between sending MLD Query messages. If left unspecified, it defaults to 120 seconds. +This option instructs the mldq6 tool to the amount of time to pause between sending MLD Query messages. If left unspecified, it defaults to 125 seconds. .TP \-\-verbose, \-v diff --git a/tools/mldq6.c b/tools/mldq6.c index 95350be..fb56255 100644 --- a/tools/mldq6.c +++ b/tools/mldq6.c @@ -557,7 +557,7 @@ int main(int argc, char **argv){ nsources=1; if(!sleep_f) - nsleep=120; + nsleep=125; if( !idata.fragh_f && dstoptuhdr_f){ puts("Dst. Options Header (Unfragmentable Part) set, but Fragmentation not specified"); From 443d1defaa1f20cf5aa7332711c0d443209c0e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Mon, 9 Jan 2023 08:14:49 +0100 Subject: [PATCH 3/3] mldq6: implement MLDv2 Queries and use v2 by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Next to the already implemented MLDv1 Queries (RFC2710) this now implements MLDv2 Queries (RFC3810). And uses MLDv2 Queries by default, unless specified otherwise with the new --mld-version|-V option. The MLDv2 QQIC is calculated internally from the --sleep option (which defaults to 125). Other than that the MLDv2 Query S-Flag (--mld-sflag|-M) and QRV (--mld-qrv|-R) values can be configured. MLDv2 Query messages for "Multicast Address and Source Specific Queries" are not yet implemented and still to do. Therefore the "Number of Sources" field is not configurable yet either. Signed-off-by: Linus Lüssing --- manuals/mldq6.1 | 19 ++++- tools/mldq6.c | 184 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 185 insertions(+), 18 deletions(-) diff --git a/manuals/mldq6.1 b/manuals/mldq6.1 index 927a46b..99b02cc 100644 --- a/manuals/mldq6.1 +++ b/manuals/mldq6.1 @@ -82,7 +82,22 @@ This option specifies the MLD Multicast Address of the MLD Query messages. If le .TP .BI \-r\ MLD_RESP_DELAY ,\ \-\-mld\-resp\-delay\ MLD_RESP_DELAY -This option specifies the MLD Maximum Respones Delay of the MLD Query messages in milliseconds. A multicast listener will reply with its MLD Report with a random delay between zero and the MLD_RESP_DELAY. Defaults to 10000 milliseconds (10 seconds). +This option specifies the MLD Maximum Respones Delay of the MLD Query messages in milliseconds. A multicast listener will reply with its MLD Report with a random delay between zero and the MLD_RESP_DELAY. For MLD Version 2 if set to >=32768 then this specifies the Maximum Response Code instead. Defaults to 10000 milliseconds (10 seconds). + +.TP +\-\-mld\-sflag, \-M + +For MLD Version 2, sets the MLDv2 Query Suppress Router-Side Processing flag. Default: off. + +.TP +.BI \-R\ MLDV2_ROBUSTNESS_VARIABLE ,\ \-\-mld\-qrv\ MLDV2_ROBUSTNESS_VARIABLE + +For MLD Version 2, sets the MLD2 Query Robustness Variable. Specifies how many MLD packets minus 1 may be missed without hindering protocol performance. Higher values increase the leave latency. RFC3810, section 5.1.8 specifies QRV values from 0-7, where 0 is the special value of a Robustness Variable > 7. Defaults to 2. + +.TP +.BI \-V\ MLD_VERSION ,\ \-\-mld\-version\ MLD_VERSION + +This option specifies if an MLDv1 or MLDv2 Query message is sent. An MLDv2 Query starts identical to an MLDv1 Query, for compatibility. But an MLDv2 Query message is at least 4 bytes longer which contain an additional S-Flag, Query Robustness Variable (QRV) and "Number of Sources" field. Optionally a list of IPv6 source addresses for multicast and source specific queries may follow. May be set to 1 or 2, defaults to 2. .TP \-\-flood\-sources, \-F @@ -97,7 +112,7 @@ This option instructs the mldq6 tool to send periodic MLD Queries to the destina .TP \-\-sleep, \-z -This option instructs the mldq6 tool to the amount of time to pause between sending MLD Query messages. If left unspecified, it defaults to 125 seconds. +This option instructs the mldq6 tool to the amount of time to pause between sending MLD Query messages. If left unspecified, it defaults to 125 seconds. For MLDv2 this also sets and codifies the MLDv2 Querier's Query Interval Code (QQIC) field. For sleep values > 128 they will get rounded up to the next QQIC if necessary. .TP \-\-verbose, \-v diff --git a/tools/mldq6.c b/tools/mldq6.c index fb56255..6a41a05 100644 --- a/tools/mldq6.c +++ b/tools/mldq6.c @@ -52,6 +52,30 @@ void print_attack_info(struct iface_data *); void usage(void); void print_help(void); +/* + * RFC3810, section 5.1.9: + * + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |1| exp | mant | + * +-+-+-+-+-+-+-+-+ + * + * QQI = (mant | 0x10) << (exp + 3) + */ +#define MLD2_QQI(exp, mant) \ + (((mant) | 0x10) << ((exp) + 3)) + +#define MLD2_MAX_QQIC_EXP 0b111 +#define MLD2_MAX_QQIC_MANT 0b1111 +/* MLD2_MAX_QQI == 31744 */ +#define MLD2_MAX_QQI MLD2_QQI(MLD2_MAX_QQIC_EXP, MLD2_MAX_QQIC_MANT) + +struct mld2_hdr { + struct mld_hdr mld_hdr; + uint8_t sqrv; + uint8_t qqic; + uint16_t nsrcs; +}; struct pcap_pkthdr *pkthdr; const u_char *pktdata; @@ -66,7 +90,8 @@ unsigned char buffer[65556]; unsigned char *v6buffer, *ptr, *startofprefixes; struct ip6_hdr *ipv6, *pkt_ipv6; -struct mld_hdr *mldq; +struct mld2_hdr *mldq; +int mldq_len = sizeof(struct mld2_hdr); struct ether_header *ethernet, *pkt_ether; struct nd_opt_slla *sllaopt; char *lasts, *endptr; @@ -80,6 +105,8 @@ uint16_t mask; uint8_t hoplimit = 1; struct in6_addr mldaddr; uint32_t mldrespdelay = 10000; +uint8_t mldqrv = 2; +uint8_t mldversion = 2; struct ether_addr linkaddr[MAX_SLLA_OPTION]; @@ -91,7 +118,7 @@ char *charptr; char plinkaddr[ETHER_ADDR_PLEN], phsrcaddr[ETHER_ADDR_PLEN], phdstaddr[ETHER_ADDR_PLEN]; char psrcaddr[INET6_ADDRSTRLEN], pdstaddr[INET6_ADDRSTRLEN], pprefix[INET6_ADDRSTRLEN]; unsigned char sllopt_f=0, sllopta_f=0, loop_f = 0, sleep_f=0, floods_f=0, hoplimit_f=0; -unsigned char mldaddr_f=0, mldrespdelay_f=0; +unsigned char mldaddr_f=0, mldrespdelay_f=0, mldsflag_f=0; unsigned char newdata_f=0; @@ -103,6 +130,7 @@ unsigned char *hbhopthdr[MAX_HBH_OPT_HDR]; unsigned int dstopthdrlen[MAX_DST_OPT_HDR], dstoptuhdrlen[MAX_DST_OPT_U_HDR]; unsigned int hbhopthdrlen[MAX_HBH_OPT_HDR], m, pad; + /* The Hop-by-hop option used in MLD query messages */ const struct { @@ -155,6 +183,9 @@ int main(int argc, char **argv){ {"src-link-opt", required_argument, 0, 'E'}, {"mld-addr", required_argument, 0, 'm'}, {"mld-resp-delay", required_argument, 0, 'r'}, + {"mld-sflag", no_argument, 0, 'M'}, + {"mld-qrv", required_argument, 0, 'R'}, + {"mld-version", required_argument, 0, 'V'}, {"flood-sources", required_argument, 0, 'F'}, {"loop", no_argument, 0, 'l'}, {"sleep", required_argument, 0, 'z'}, @@ -163,7 +194,7 @@ int main(int argc, char **argv){ {0, 0, 0, 0 } }; - const char shortopts[]= "i:s:d:A:u:U:H:y:S:D:eE:m:r:F:lz:vh"; + const char shortopts[]= "i:s:d:A:u:U:H:y:S:D:eE:m:r:MR:V:F:lz:vh"; char option; if(argc<=1){ @@ -422,6 +453,32 @@ int main(int argc, char **argv){ mldrespdelay_f= 1; break; + case 'M': /* MLDv2 Query Suppress Router-Side Processing */ + mldsflag_f = 1; + break; + + case 'R': /* MLDv2 Query Robustness Variable */ + mldqrv= atoi(optarg); + if(optarg[0] == '\0' || mldqrv < 0 || mldqrv > 7 || + (mldqrv == 0 && optarg[0] != '0')){ + puts("Invalid MLDv2 Robustness Variable, must be 0-7"); + exit(EXIT_FAILURE); + } + + break; + + case 'V': /* MLD Query Version */ + mldversion= atoi(optarg); + if(optarg[0] == '\0' || (mldversion != 1 && mldversion != 2)){ + puts("Invalid MLD version, must be either 1 or 2"); + exit(EXIT_FAILURE); + } + + if (mldversion == 1) + mldq_len = sizeof(struct mld_hdr); + + break; + case 'F': /* Flood sources */ nsources= atoi(optarg); if(nsources == 0){ @@ -588,7 +645,91 @@ int main(int argc, char **argv){ exit(EXIT_SUCCESS); } +uint8_t mld2_enc_qqic_get_exp(unsigned int t){ + uint8_t exp; + + if (t > MLD2_MAX_QQI) + return MLD2_MAX_QQIC_EXP; + + /* 8 possible exponent values [0-7] + * 0: 128 <= t < 256 + * 1: 256 <= t < 512 + * ... + * 7: 16384 <= t < 32768 + * + * Find largest exponent for which MLD2_QQI(exp, mant=0) + * is still smaller than or equal to t: + */ + for (exp = 0; i <= 7; exp++) + if (MLD2_QQI((exp+1), 0) > t) + break; + return exp; +} + +uint8_t mld2_enc_qqic_get_mant(unsigned int t, uint8_t *exp){ + uint8_t mant = 0; + uint16_t m_step, m_rem; + + if (t > MLD2_MAX_QQI) { + printf("Warning: interval %u > maximum QQI (%u), setting max. QQIC\n", + t, MLD2_MAX_QQI); + return MLD2_MAX_QQIC_MANT; + } + + m_step = 8 << (*exp); + m_rem = t - MLD2_QQI((*exp), 0); + + /* Get smallest mantisse for which the QQI(exp, mant) is >= 't' + * -> so we are likely rounding up here. + */ + if (m_rem != 0) + mant = (m_rem-1) / m_step + 1; + + /* Carry-over, happens if: + * QQI(exp, mant_max==15) < 't' < QQI(exp+1, mant_min==0) + */ + if (mant == 16) { + (*exp)++; + mant = 0; + } + + /* Assertions, should not happen: */ + if (mant > MLD2_MAX_QQIC_MANT) { + fprintf(stderr, + "%s: Error: mantisse %u > %u (should not happen)", + __func__, mant, MLD2_MAX_QQIC_MANT); + exit(EXIT_FAILURE); + } + if (*exp > MLD2_MAX_QQIC_EXP) { + fprintf(stderr, + "%s: Error: exponent %u > %u (should not happen)", + __func__, *exp, MLD2_MAX_QQIC_EXP); + exit(EXIT_FAILURE); + } + + if (MLD2_QQI((*exp), mant) != t) + printf("Note: No exact QQIC representation, rounding %u up to QQI %u\n", + t, MLD2_QQI((*exp), mant)); + + return mant; +} + +/** + * Get the clossest Querier Query Interval Code, + * rounded up. + */ +uint8_t mld2_encode_qqic(unsigned int t){ + uint8_t exp, mant; + + if (t < 128) + return (uint8_t)t; + + exp = mld2_enc_qqic_get_exp(t); + mant = mld2_enc_qqic_get_mant(t, &exp); + + return 0b10000000 | (exp << 4) | mant; +} /* * Function: init_packet_data() @@ -715,23 +856,31 @@ void init_packet_data(struct iface_data *idata){ *prev_nh = IPPROTO_ICMPV6; - if( (ptr+sizeof(struct mld_hdr)) > (v6buffer+idata->max_packet_size)){ + if( (ptr+mldq_len) > (v6buffer+idata->max_packet_size)){ puts("Packet too large while inserting MLD Query header (should be using Frag. option?)"); exit(EXIT_FAILURE); } - mldq= (struct mld_hdr *) (ptr); - mldq->mld_type = MLD_LISTENER_QUERY; - mldq->mld_code = 0; - mldq->mld_maxdelay = htons(mldrespdelay); - mldq->mld_reserved = 0; + mldq= (struct mld2_hdr *) (ptr); + mldq->mld_hdr.mld_type = MLD_LISTENER_QUERY; + mldq->mld_hdr.mld_code = 0; + mldq->mld_hdr.mld_maxdelay = htons(mldrespdelay); + mldq->mld_hdr.mld_reserved = 0; if (mldaddr_f) - memcpy(&mldq->mld_addr, &mldaddr, sizeof(mldq->mld_addr)); + memcpy(&mldq->mld_hdr.mld_addr, &mldaddr, sizeof(mldq->mld_hdr.mld_addr)); else - memset(&mldq->mld_addr, 0, sizeof(mldq->mld_addr)); + memset(&mldq->mld_hdr.mld_addr, 0, sizeof(mldq->mld_hdr.mld_addr)); + + /* MLDv2 Query is at least 4 bytes longer */ + if (mldversion == 2) { + mldq->sqrv = mldsflag_f << 3; + mldq->sqrv |= mldqrv; + mldq->qqic = mld2_encode_qqic(nsleep); + mldq->nsrcs = 0; + } - ptr += sizeof(struct mld_hdr); + ptr += mldq_len; /* If a single source link-layer address is specified, it is included in all packets */ if(sllopt_f && nlinkaddr==1){ @@ -802,8 +951,8 @@ void send_packet(struct iface_data *idata){ } - mldq->mld_cksum = 0; - mldq->mld_cksum = in_chksum(v6buffer, mldq, ptr-((unsigned char *)mldq), IPPROTO_ICMPV6); + mldq->mld_hdr.mld_cksum = 0; + mldq->mld_hdr.mld_cksum = in_chksum(v6buffer, mldq, ptr-((unsigned char *)mldq), IPPROTO_ICMPV6); if(!idata->fragh_f){ @@ -925,10 +1074,13 @@ void print_help(void){ " --src-link-opt, -E Source link-layer address option\n" " --add-slla-opt, -e Add Source link-layer address option\n" " --mld-addr, -m MLD Query Multicast Address\n" - " --mld-resp-delay, -r MLD Query Maximum Response Delay [ms]\n" + " --mld-resp-delay, -r MLD Query Maximum Response Code [<32768: ms], default: 10000\n" + " --mld-sflag, -M MLDv2 Query Suppress Router-Side Processing flag\n" + " --mld-qrv, -R MLDv2 Query Robustness Variable [0-7], default: 2\n" + " --mld-version, -V MLD Query Version [1-2], default: 2\n" " --flood-sources, -F Number of Source Addresses to forge randomly\n" " --loop, -l Send MLD Query periodically\n" - " --sleep, -z Pause between peiodic MLD Queries [sec]\n" + " --sleep, -z Pause between periodic MLD Queries and MLDv2 QQI [sec], default: 125\n" " --help, -h Print help for the mldq6 tool\n" " --verbose, -v Be verbose\n" "\n"