/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 |