Use TCPOptions class for IPv6 vectorization

This commit is contained in:
dmiller 2026-06-24 20:21:57 +00:00
parent c86a45ef6e
commit 84b525f61d
3 changed files with 36 additions and 95 deletions

View file

@ -833,6 +833,33 @@ static int vectorize_icmpv6_code(const PacketElement *pe) {
return icmpv6->getCode();
}
struct tcpopt_vectorize_ctx {
feature_node *features;
const unsigned int base;
unsigned int optnum;
int mss;
int sackok;
int wscale;
tcpopt_vectorize_ctx(feature_node *f, unsigned int i)
: features(f), base(i), optnum(0), mss(-1), sackok(-1), wscale(-1) {}
};
static const u8 MODEL_NUM_OPTS = 16;
static bool tcpopt_vectorize(u8 op, u8 oplen, const u8 *data, void *ctx) {
tcpopt_vectorize_ctx *c = static_cast<tcpopt_vectorize_ctx *>(ctx);
c->features[c->base + c->optnum].value = op;
c->features[c->base + c->optnum + MODEL_NUM_OPTS].value = oplen;
if (op == TCPOPT_MSS && oplen == 4 && c->mss == -1)
c->mss = (data[2] << 8) + data[3];
else if (op == TCPOPT_SACKOK && oplen == 2 && c->sackok == -1)
c->sackok = 1;
else if (op == TCPOPT_WSCALE && oplen == 3 && c->wscale == -1)
c->wscale = data[2];
if (c->optnum++ < MODEL_NUM_OPTS)
return true;
return false;
}
static struct feature_node *vectorize(const FingerPrintResultsIPv6 *FPR) {
const char * const IPV6_PROBE_NAMES[] = {"S1", "S2", "S3", "S4", "S5", "S6", "IE1", "IE2", "NS", "U1", "TECN", "T2", "T3", "T4", "T5", "T6", "T7"};
const char * const TCP_PROBE_NAMES[] = {"S1", "S2", "S3", "S4", "S5", "S6", "TECN", "T2", "T3", "T4", "T5", "T6", "T7"};
@ -877,17 +904,9 @@ static struct feature_node *vectorize(const FingerPrintResultsIPv6 *FPR) {
const TCPHeader *tcp;
u16 flags;
u16 mask;
unsigned int j;
int mss;
int sackok;
int wscale;
probe_name = TCP_PROBE_NAMES[i];
mss = -1;
sackok = -1;
wscale = -1;
tcp = find_tcp(resps[probe_name].getPacket());
if (tcp == NULL) {
/* 49 TCP features. */
@ -899,40 +918,17 @@ static struct feature_node *vectorize(const FingerPrintResultsIPv6 *FPR) {
for (mask = 0x001; mask <= 0x800; mask <<= 1)
features[idx++].value = (flags & mask) != 0;
for (j = 0; j < 16; j++) {
nping_tcp_opt_t opt;
opt = tcp->getOption(j);
if (opt.value == NULL)
break;
features[idx++].value = opt.type;
/* opt.len includes the two (type, len) bytes. */
if (opt.type == TCPOPT_MSS && opt.len == 4 && mss == -1)
mss = ntohs(*(u16 *) opt.value);
else if (opt.type == TCPOPT_SACKOK && opt.len == 2 && sackok == -1)
sackok = 1;
else if (opt.type == TCPOPT_WSCALE && opt.len == 3 && wscale == -1)
wscale = *(u8 *) opt.value;
TCPOptions opts;
tcpopt_vectorize_ctx ctx(features, idx);
if (opts.fromTCPHeader(*tcp)) {
opts.foreachOpt(tcpopt_vectorize, &ctx);
}
for (; j < 16; j++)
idx++;
idx += MODEL_NUM_OPTS * 2;
for (j = 0; j < 16; j++) {
nping_tcp_opt_t opt;
opt = tcp->getOption(j);
if (opt.value == NULL)
break;
features[idx++].value = opt.len;
}
for (; j < 16; j++)
idx++;
features[idx++].value = mss;
features[idx++].value = sackok;
features[idx++].value = wscale;
if (mss != 0 && mss != -1)
features[idx++].value = (float)tcp->getWindow() / mss;
else
features[idx++].value = -1;
features[idx++].value = ctx.mss;
features[idx++].value = ctx.sackok;
features[idx++].value = ctx.wscale;
features[idx++].value = (ctx.mss > 0) ? (float)tcp->getWindow() / ctx.mss : -1;
}
/* ICMPv6 features */
for (i = 0; i < NELEMS(ICMPV6_PROBE_NAMES); i++) {

View file

@ -813,47 +813,6 @@ const u8 *TCPHeader::getOptions(size_t *optslen) const {
return this->h.options;
} /* End of getOptions() */
struct tcpopt_atindex_ctx {
unsigned int index;
unsigned int found;
nping_tcp_opt_t result;
tcpopt_atindex_ctx() : index(0), found(0) {
memset(&result, 0, sizeof(result));
}
};
static bool tcpopt_atindex(u8 op, u8 oplen, const u8 *data, void *ctx)
{
tcpopt_atindex_ctx *args = static_cast<tcpopt_atindex_ctx *>(ctx);
if (args->index == args->found) {
args->result.type = op;
args->result.len = oplen;
args->result.value = data + 2;
return false;
}
args->found += 1;
return true;
}
/* Returns the index-th option in the TCP header. On success it returns a
* structure filled with option information. If there is no index-th option,
* it returns a structure with st.value==NULL. Note that this function does
* not perform strict validity checking. It does check that the length claimed
* by the options does not exceed the available buffer but it does not check,
* for example, that the MSS option always contains a length of 4. Also,
* if the returned option type is TCPOPT_EOL or TCPOPT_NOOP, the len field
* would be set to zero and the "value" field should NOT be accessed, as it
* will not contain reliable information. */
nping_tcp_opt_t TCPHeader::getOption(unsigned int index) const {
TCPOptions opts;
tcpopt_atindex_ctx ctx;
if (opts.fromTCPHeader(*this)) {
ctx.index = index;
opts.foreachOpt(tcpopt_atindex, &ctx);
}
return ctx.result;
}
/* Returns a textual representation of a TCP Options code */
const char *TCPHeader::optcode2str(u8 optcode){

View file

@ -115,19 +115,6 @@
/*
+--------+--------+---------+--------...
| Type | Len | Value
+--------+--------+---------+--------...
*/
struct nping_tcp_opt {
u8 type; /* Option type code. */
u8 len; /* Option length. */
const u8 *value; /* Option value */
}__attribute__((__packed__));
typedef struct nping_tcp_opt nping_tcp_opt_t;
class TCPHeader : public TransportLayerElement {
private:
@ -252,7 +239,6 @@ class TCPHeader : public TransportLayerElement {
int setOptions(const u8 *optsbuff, size_t optslen);
const u8 *getOptions(size_t *optslen) const;
nping_tcp_opt_t getOption(unsigned int index) const;
static const char *optcode2str(u8 optcode);
}; /* End of class TCPHeader */