Coverage Report

Created: 2025-08-01 06:58

/src/zeek/src/net_util.cc
Line
Count
Source (jump to first uncovered line)
1
// See the file "COPYING" in the main distribution directory for copyright.
2
3
#include "zeek/net_util.h"
4
5
#include <arpa/inet.h>
6
#include <netinet/in.h>
7
#include <sys/socket.h>
8
#include <sys/types.h>
9
#include <memory>
10
11
#include "zeek/IP.h"
12
#include "zeek/IPAddr.h"
13
#include "zeek/Reporter.h"
14
15
#include "zeek/3rdparty/doctest.h"
16
17
0
const char* transport_proto_string(TransportProto proto) {
18
0
    switch ( proto ) {
19
0
        case TRANSPORT_TCP: return "tcp";
20
0
        case TRANSPORT_UDP: return "udp";
21
0
        case TRANSPORT_ICMP: return "icmp";
22
0
        case TRANSPORT_UNKNOWN:
23
0
        default: return "unknown";
24
0
    }
25
0
}
26
27
namespace zeek {
28
29
0
uint16_t detail::ip4_in_cksum(const IPAddr& src, const IPAddr& dst, uint8_t next_proto, const uint8_t* data, int len) {
30
0
    constexpr auto nblocks = 2;
31
0
    detail::checksum_block blocks[nblocks];
32
33
0
    ipv4_pseudo_hdr ph;
34
0
    memset(&ph, 0, sizeof(ph));
35
36
0
    src.CopyIPv4(&ph.src);
37
0
    dst.CopyIPv4(&ph.dst);
38
0
    ph.len = htons(static_cast<uint16_t>(len));
39
0
    ph.next_proto = next_proto;
40
0
    blocks[0].block = reinterpret_cast<const uint8_t*>(&ph);
41
0
    blocks[0].len = sizeof(ph);
42
0
    blocks[1].block = data;
43
0
    blocks[1].len = len;
44
45
0
    return in_cksum(blocks, nblocks);
46
0
}
47
48
0
uint16_t detail::ip6_in_cksum(const IPAddr& src, const IPAddr& dst, uint8_t next_proto, const uint8_t* data, int len) {
49
0
    constexpr auto nblocks = 2;
50
0
    detail::checksum_block blocks[nblocks];
51
52
0
    ipv6_pseudo_hdr ph;
53
0
    memset(&ph, 0, sizeof(ph));
54
55
0
    src.CopyIPv6(&ph.src);
56
0
    dst.CopyIPv6(&ph.dst);
57
0
    ph.len = htonl(static_cast<uint32_t>(len));
58
0
    ph.next_proto = next_proto;
59
0
    blocks[0].block = reinterpret_cast<const uint8_t*>(&ph);
60
0
    blocks[0].len = sizeof(ph);
61
0
    blocks[1].block = data;
62
0
    blocks[1].len = len;
63
64
0
    return in_cksum(blocks, nblocks);
65
0
}
66
67
// Returns the ones-complement checksum of a chunk of 'b' bytes.
68
0
int ones_complement_checksum(const void* p, int b, uint32_t sum) {
69
0
    const unsigned char* sp = (unsigned char*)p;
70
71
0
    b /= 2; // convert to count of short's
72
73
    /* No need for endian conversions. */
74
0
    while ( --b >= 0 ) {
75
0
        sum += *sp + (*(sp + 1) << 8);
76
0
        sp += 2;
77
0
    }
78
79
0
    while ( sum > 0xffff )
80
0
        sum = (sum & 0xffff) + (sum >> 16);
81
82
0
    return sum;
83
0
}
84
85
0
int ones_complement_checksum(const IPAddr& a, uint32_t sum) {
86
0
    const uint32_t* bytes;
87
0
    int len = a.GetBytes(&bytes);
88
0
    return ones_complement_checksum(bytes, len * 4, sum);
89
0
}
90
91
0
int icmp_checksum(const struct icmp* icmpp, int len) {
92
0
    return detail::in_cksum(reinterpret_cast<const uint8_t*>(icmpp), len);
93
0
}
94
95
0
int mobility_header_checksum(const IP_Hdr* ip) {
96
0
    const ip6_mobility* mh = ip->MobilityHeader();
97
98
0
    if ( ! mh )
99
0
        return 0;
100
101
0
    uint32_t sum = 0;
102
0
    uint8_t mh_len = 8 + 8 * mh->ip6mob_len;
103
104
0
    if ( mh_len % 2 == 1 )
105
0
        reporter->Weird(ip->SrcAddr(), ip->DstAddr(), "odd_mobility_hdr_len");
106
107
0
    sum = ones_complement_checksum(ip->SrcAddr(), sum);
108
0
    sum = ones_complement_checksum(ip->DstAddr(), sum);
109
    // Note, for IPv6, strictly speaking the protocol and length fields are
110
    // 32 bits rather than 16 bits.  But because the upper bits are all zero,
111
    // we get the same checksum either way.
112
0
    sum += htons(IPPROTO_MOBILITY);
113
0
    sum += htons(mh_len);
114
0
    sum = ones_complement_checksum(mh, mh_len, sum);
115
116
0
    return sum;
117
0
}
118
119
0
int icmp6_checksum(const struct icmp* icmpp, const IP_Hdr* ip, int len) {
120
    // ICMP6 uses the same checksum function as ICMP4 but a different
121
    // pseudo-header over which it is computed.
122
0
    return detail::ip6_in_cksum(ip->SrcAddr(), ip->DstAddr(), IPPROTO_ICMPV6, reinterpret_cast<const uint8_t*>(icmpp),
123
0
                                len);
124
0
}
125
126
constexpr uint32_t CLASS_A = 0x00000000;
127
constexpr uint32_t CLASS_B = 0x80000000;
128
constexpr uint32_t CLASS_C = 0xc0000000;
129
constexpr uint32_t CLASS_D = 0xe0000000;
130
constexpr uint32_t CLASS_E = 0xf0000000;
131
132
0
char addr_to_class(uint32_t addr) {
133
0
    auto CHECK_CLASS = [](uint32_t addr, uint32_t cls) { return (addr & cls) == cls; };
134
0
    if ( CHECK_CLASS(addr, CLASS_E) )
135
0
        return 'E';
136
0
    else if ( CHECK_CLASS(addr, CLASS_D) )
137
0
        return 'D';
138
0
    else if ( CHECK_CLASS(addr, CLASS_C) )
139
0
        return 'C';
140
0
    else if ( CHECK_CLASS(addr, CLASS_B) )
141
0
        return 'B';
142
0
    else
143
0
        return 'A';
144
0
}
145
146
0
const char* fmt_conn_id(const IPAddr& src_addr, uint32_t src_port, const IPAddr& dst_addr, uint32_t dst_port) {
147
0
    static char buffer[512];
148
149
0
    snprintf(buffer, sizeof(buffer), "%s:%d > %s:%d", std::string(src_addr).c_str(), src_port,
150
0
             std::string(dst_addr).c_str(), dst_port);
151
152
0
    return buffer;
153
0
}
154
155
0
const char* fmt_conn_id(const uint32_t* src_addr, uint32_t src_port, const uint32_t* dst_addr, uint32_t dst_port) {
156
0
    IPAddr src(IPv6, src_addr, IPAddr::Network);
157
0
    IPAddr dst(IPv6, dst_addr, IPAddr::Network);
158
0
    return fmt_conn_id(src, src_port, dst, dst_port);
159
0
}
160
161
0
TEST_CASE("fmt_mac") {
162
0
    auto my_fmt_mac = [](const char* m, int len) {
163
        // allow working with literal strings
164
0
        return fmt_mac(reinterpret_cast<const unsigned char*>(m), len);
165
0
    };
166
167
0
    CHECK(my_fmt_mac("", 0) == "");
168
0
    CHECK(my_fmt_mac("\x01\x02\x03\x04\x05\x06", 4) == "");
169
0
    CHECK(my_fmt_mac("\x01\x02\x03\x04\x05\x06", 6) == "01:02:03:04:05:06");
170
    // NOLINTNEXTLINE(bugprone-string-literal-with-embedded-nul)
171
0
    CHECK(my_fmt_mac("\x01\x02\x03\x04\x05\x06\x00\x00", 8) == "01:02:03:04:05:06");
172
0
    CHECK(my_fmt_mac("\x01\x02\x03\x04\x05\x06\x07\x08", 8) == "01:02:03:04:05:06:07:08");
173
0
    CHECK(my_fmt_mac("\x08\x07\x06\x05\x04\x03\x02\x01", 8) == "08:07:06:05:04:03:02:01");
174
0
}
175
176
20.0k
std::string fmt_mac(const unsigned char* m, int len) {
177
20.0k
    auto [mac_bytes, slen] = fmt_mac_bytes(m, len);
178
20.0k
    return reinterpret_cast<char*>(mac_bytes.get());
179
20.0k
}
180
181
0
TEST_CASE("fmt_mac_bytes") {
182
0
    auto my_fmt_mac_bytes = [](const char* m, int len) {
183
        // allow working with literal strings
184
0
        return fmt_mac_bytes(reinterpret_cast<const unsigned char*>(m), len);
185
0
    };
186
187
0
    auto [buf1, len1] = my_fmt_mac_bytes("\x01\x02\x03\x04\x05\x06", 4);
188
0
    CHECK(len1 == 0);
189
0
    CHECK(memcmp(buf1.get(), "", 1) == 0); // still null terminated
190
191
0
    auto [buf2, len2] = my_fmt_mac_bytes("\x01\x02\x03\x04\x05\x06", 6);
192
0
    CHECK(len2 == 2 * 6 + 5);
193
0
    CHECK(memcmp(buf2.get(), "01:02:03:04:05:06", len2 + 1) == 0);
194
195
    // NOLINTNEXTLINE(bugprone-string-literal-with-embedded-nul)
196
0
    auto [buf3, len3] = my_fmt_mac_bytes("\x01\x02\x03\x04\x05\x06\x00\x00", 8);
197
0
    CHECK(len3 == 2 * 6 + 5);
198
0
    CHECK(memcmp(buf3.get(), "01:02:03:04:05:06", len3 + 1) == 0);
199
200
    // Check for no memory overreads
201
0
    auto [buf4, len4] = my_fmt_mac_bytes("\x01\x02\x03\x04\x05\x06\x07\x08\xff\xff", 42);
202
0
    CHECK(len4 == 2 * 8 + 7);
203
0
    CHECK(memcmp(buf4.get(), "01:02:03:04:05:06:07:08", len4 + 1) == 0);
204
0
}
205
206
20.9k
std::pair<std::unique_ptr<uint8_t[]>, int> fmt_mac_bytes(const unsigned char* m, int len) {
207
20.9k
    if ( len < 8 && len != 6 ) {
208
1.76k
        auto buf = std::make_unique<uint8_t[]>(1);
209
1.76k
        buf[0] = '\0';
210
1.76k
        return {std::move(buf), 0};
211
1.76k
    }
212
213
19.1k
    int elen = (len == 6 || (m[6] == 0 && m[7] == 0)) ? 6 : 8;
214
19.1k
    auto slen = 2 * elen + (elen - 1);
215
19.1k
    auto buf = std::make_unique<uint8_t[]>(slen + 1);
216
19.1k
    auto* bufp = reinterpret_cast<char*>(buf.get());
217
218
152k
    for ( int i = 0; i < elen; i++ ) {
219
133k
        zeek::util::bytetohex(m[i], &bufp[i * 2 + i]);
220
133k
        if ( i < elen - 1 )
221
114k
            bufp[i * 2 + i + 2] = ':';
222
133k
    }
223
19.1k
    bufp[slen] = '\0';
224
225
19.1k
    return {std::move(buf), slen};
226
20.9k
}
227
228
1.06k
uint32_t extract_uint32(const u_char* data) {
229
1.06k
    uint32_t val;
230
231
1.06k
    val = data[0] << 24;
232
1.06k
    val |= data[1] << 16;
233
1.06k
    val |= data[2] << 8;
234
1.06k
    val |= data[3];
235
236
1.06k
    return val;
237
1.06k
}
238
239
} // namespace zeek