1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2010 Alexander V. Chernikov <melifaro@ipfw.ru> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #include "opt_inet6.h" 31 #include "opt_route.h" 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/counter.h> 35 #include <sys/kernel.h> 36 #include <sys/ktr.h> 37 #include <sys/limits.h> 38 #include <sys/malloc.h> 39 #include <sys/mbuf.h> 40 #include <sys/syslog.h> 41 #include <sys/socket.h> 42 #include <vm/uma.h> 43 44 #include <net/if.h> 45 #include <net/route.h> 46 #include <net/ethernet.h> 47 #include <netinet/in.h> 48 #include <netinet/in_systm.h> 49 #include <netinet/ip.h> 50 #include <netinet/ip6.h> 51 #include <netinet/tcp.h> 52 #include <netinet/udp.h> 53 54 #include <netgraph/ng_message.h> 55 #include <netgraph/netgraph.h> 56 57 #include <netgraph/netflow/netflow.h> 58 #include <netgraph/netflow/ng_netflow.h> 59 #include <netgraph/netflow/netflow_v9.h> 60 61 MALLOC_DECLARE(M_NETFLOW_GENERAL); 62 MALLOC_DEFINE(M_NETFLOW_GENERAL, "netflow_general", "plog, V9 templates data"); 63 64 /* 65 * Base V9 templates for L4+ IPv4/IPv6 protocols 66 */ 67 struct netflow_v9_template _netflow_v9_record_ipv4_tcp[] = 68 { 69 { NETFLOW_V9_FIELD_IPV4_SRC_ADDR, 4}, 70 { NETFLOW_V9_FIELD_IPV4_DST_ADDR, 4}, 71 { NETFLOW_V9_FIELD_IPV4_NEXT_HOP, 4}, 72 { NETFLOW_V9_FIELD_INPUT_SNMP, 2}, 73 { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2}, 74 { NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)}, 75 { NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)}, 76 { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)}, 77 { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)}, 78 { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4}, 79 { NETFLOW_V9_FIELD_LAST_SWITCHED, 4}, 80 { NETFLOW_V9_FIELD_L4_SRC_PORT, 2}, 81 { NETFLOW_V9_FIELD_L4_DST_PORT, 2}, 82 { NETFLOW_V9_FIELD_TCP_FLAGS, 1}, 83 { NETFLOW_V9_FIELD_PROTOCOL, 1}, 84 { NETFLOW_V9_FIELD_TOS, 1}, 85 { NETFLOW_V9_FIELD_SRC_AS, 4}, 86 { NETFLOW_V9_FIELD_DST_AS, 4}, 87 { NETFLOW_V9_FIELD_SRC_MASK, 1}, 88 { NETFLOW_V9_FIELD_DST_MASK, 1}, 89 {0, 0} 90 }; 91 92 struct netflow_v9_template _netflow_v9_record_ipv6_tcp[] = 93 { 94 { NETFLOW_V9_FIELD_IPV6_SRC_ADDR, 16}, 95 { NETFLOW_V9_FIELD_IPV6_DST_ADDR, 16}, 96 { NETFLOW_V9_FIELD_IPV6_NEXT_HOP, 16}, 97 { NETFLOW_V9_FIELD_INPUT_SNMP, 2}, 98 { NETFLOW_V9_FIELD_OUTPUT_SNMP, 2}, 99 { NETFLOW_V9_FIELD_IN_PKTS, sizeof(CNTR)}, 100 { NETFLOW_V9_FIELD_IN_BYTES, sizeof(CNTR)}, 101 { NETFLOW_V9_FIELD_OUT_PKTS, sizeof(CNTR)}, 102 { NETFLOW_V9_FIELD_OUT_BYTES, sizeof(CNTR)}, 103 { NETFLOW_V9_FIELD_FIRST_SWITCHED, 4}, 104 { NETFLOW_V9_FIELD_LAST_SWITCHED, 4}, 105 { NETFLOW_V9_FIELD_L4_SRC_PORT, 2}, 106 { NETFLOW_V9_FIELD_L4_DST_PORT, 2}, 107 { NETFLOW_V9_FIELD_TCP_FLAGS, 1}, 108 { NETFLOW_V9_FIELD_PROTOCOL, 1}, 109 { NETFLOW_V9_FIELD_TOS, 1}, 110 { NETFLOW_V9_FIELD_SRC_AS, 4}, 111 { NETFLOW_V9_FIELD_DST_AS, 4}, 112 { NETFLOW_V9_FIELD_SRC_MASK, 1}, 113 { NETFLOW_V9_FIELD_DST_MASK, 1}, 114 {0, 0} 115 }; 116 117 /* 118 * Pre-compiles flow exporter for all possible FlowSets 119 * so we can add flowset to packet via simple memcpy() 120 */ 121 static void 122 generate_v9_templates(priv_p priv) 123 { 124 uint16_t *p, *template_fields_cnt; 125 int cnt; 126 127 int flowset_size = sizeof(struct netflow_v9_flowset_header) + 128 _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv4_tcp) + /* netflow_v9_record_ipv4_tcp */ 129 _NETFLOW_V9_TEMPLATE_SIZE(_netflow_v9_record_ipv6_tcp); /* netflow_v9_record_ipv6_tcp */ 130 131 priv->v9_flowsets[0] = malloc(flowset_size, M_NETFLOW_GENERAL, M_WAITOK | M_ZERO); 132 133 if (flowset_size % 4) 134 flowset_size += 4 - (flowset_size % 4); /* Padding to 4-byte boundary */ 135 136 priv->flowsets_count = 1; 137 p = (uint16_t *)priv->v9_flowsets[0]; 138 *p++ = 0; /* Flowset ID, 0 is reserved for Template FlowSets */ 139 *p++ = htons(flowset_size); /* Total FlowSet length */ 140 141 /* 142 * Most common TCP/UDP IPv4 template, ID = 256 143 */ 144 *p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V4_L4); 145 template_fields_cnt = p++; 146 for (cnt = 0; _netflow_v9_record_ipv4_tcp[cnt].field_id != 0; cnt++) { 147 *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_id); 148 *p++ = htons(_netflow_v9_record_ipv4_tcp[cnt].field_length); 149 } 150 *template_fields_cnt = htons(cnt); 151 152 /* 153 * TCP/UDP IPv6 template, ID = 257 154 */ 155 *p++ = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + NETFLOW_V9_FLOW_V6_L4); 156 template_fields_cnt = p++; 157 for (cnt = 0; _netflow_v9_record_ipv6_tcp[cnt].field_id != 0; cnt++) { 158 *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_id); 159 *p++ = htons(_netflow_v9_record_ipv6_tcp[cnt].field_length); 160 } 161 *template_fields_cnt = htons(cnt); 162 163 priv->flowset_records[0] = 2; 164 } 165 166 /* Closes current data flowset */ 167 static void inline 168 close_flowset(struct mbuf *m, struct netflow_v9_packet_opt *t) 169 { 170 struct mbuf *m_old; 171 uint32_t zero = 0; 172 int offset = 0; 173 uint16_t *flowset_length, len; 174 175 /* Hack to ensure we are not crossing mbuf boundary, length is uint16_t */ 176 m_old = m_getptr(m, t->flow_header + offsetof(struct netflow_v9_flowset_header, length), &offset); 177 flowset_length = (uint16_t *)(mtod(m_old, char *) + offset); 178 179 len = (uint16_t)(m_pktlen(m) - t->flow_header); 180 /* Align on 4-byte boundary (RFC 3954, Clause 5.3) */ 181 if (len % 4) { 182 if (m_append(m, 4 - (len % 4), (void *)&zero) != 1) 183 panic("ng_netflow: m_append() failed!"); 184 185 len += 4 - (len % 4); 186 } 187 188 *flowset_length = htons(len); 189 } 190 191 /* 192 * Non-static functions called from ng_netflow.c 193 */ 194 195 /* We have full datagram in fib data. Send it to export hook. */ 196 int 197 export9_send(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags) 198 { 199 struct mbuf *m = NGI_M(item); 200 struct netflow_v9_export_dgram *dgram = mtod(m, 201 struct netflow_v9_export_dgram *); 202 struct netflow_v9_header *header = &dgram->header; 203 struct timespec ts; 204 int error = 0; 205 206 if (t == NULL) { 207 CTR0(KTR_NET, "export9_send(): V9 export packet without tag"); 208 NG_FREE_ITEM(item); 209 return (0); 210 } 211 212 /* Close flowset if not closed already */ 213 if (m_pktlen(m) != t->flow_header) 214 close_flowset(m, t); 215 216 /* Fill export header. */ 217 header->count = t->count; 218 header->sys_uptime = htonl(MILLIUPTIME(time_uptime)); 219 getnanotime(&ts); 220 header->unix_secs = htonl(ts.tv_sec); 221 header->seq_num = htonl(atomic_fetchadd_32(&fe->flow9_seq, 1)); 222 header->count = htons(t->count); 223 header->source_id = htonl(fe->domain_id); 224 225 if (priv->export9 != NULL) 226 NG_FWD_ITEM_HOOK_FLAGS(error, item, priv->export9, flags); 227 else 228 NG_FREE_ITEM(item); 229 230 free(t, M_NETFLOW_GENERAL); 231 232 return (error); 233 } 234 235 /* Add V9 record to dgram. */ 236 int 237 export9_add(item_p item, struct netflow_v9_packet_opt *t, struct flow_entry *fle) 238 { 239 size_t len = 0; 240 struct netflow_v9_flowset_header fsh; 241 struct netflow_v9_record_general rg; 242 struct mbuf *m = NGI_M(item); 243 uint16_t flow_type; 244 struct flow_entry_data *fed; 245 #ifdef INET6 246 struct flow6_entry_data *fed6; 247 #endif 248 if (t == NULL) { 249 CTR0(KTR_NET, "ng_netflow: V9 export packet without tag!"); 250 return (0); 251 } 252 253 /* Prepare flow record */ 254 fed = (struct flow_entry_data *)&fle->f; 255 #ifdef INET6 256 fed6 = (struct flow6_entry_data *)&fle->f; 257 #endif 258 /* We can use flow_type field since fle6 offset is equal to fle */ 259 flow_type = fed->r.flow_type; 260 261 switch (flow_type) { 262 case NETFLOW_V9_FLOW_V4_L4: 263 { 264 /* IPv4 TCP/UDP/[SCTP] */ 265 struct netflow_v9_record_ipv4_tcp *rec = &rg.rec.v4_tcp; 266 267 rec->src_addr = fed->r.r_src.s_addr; 268 rec->dst_addr = fed->r.r_dst.s_addr; 269 rec->next_hop = fed->next_hop.s_addr; 270 rec->i_ifx = htons(fed->fle_i_ifx); 271 rec->o_ifx = htons(fed->fle_o_ifx); 272 rec->i_packets = htonl(fed->packets); 273 rec->i_octets = htonl(fed->bytes); 274 rec->o_packets = htonl(0); 275 rec->o_octets = htonl(0); 276 rec->first = htonl(MILLIUPTIME(fed->first)); 277 rec->last = htonl(MILLIUPTIME(fed->last)); 278 rec->s_port = fed->r.r_sport; 279 rec->d_port = fed->r.r_dport; 280 rec->flags = fed->tcp_flags; 281 rec->prot = fed->r.r_ip_p; 282 rec->tos = fed->r.r_tos; 283 rec->dst_mask = fed->dst_mask; 284 rec->src_mask = fed->src_mask; 285 286 /* Not supported fields. */ 287 rec->src_as = rec->dst_as = 0; 288 289 len = sizeof(struct netflow_v9_record_ipv4_tcp); 290 break; 291 } 292 #ifdef INET6 293 case NETFLOW_V9_FLOW_V6_L4: 294 { 295 /* IPv6 TCP/UDP/[SCTP] */ 296 struct netflow_v9_record_ipv6_tcp *rec = &rg.rec.v6_tcp; 297 298 rec->src_addr = fed6->r.src.r_src6; 299 rec->dst_addr = fed6->r.dst.r_dst6; 300 rec->next_hop = fed6->n.next_hop6; 301 rec->i_ifx = htons(fed6->fle_i_ifx); 302 rec->o_ifx = htons(fed6->fle_o_ifx); 303 rec->i_packets = htonl(fed6->packets); 304 rec->i_octets = htonl(fed6->bytes); 305 rec->o_packets = htonl(0); 306 rec->o_octets = htonl(0); 307 rec->first = htonl(MILLIUPTIME(fed6->first)); 308 rec->last = htonl(MILLIUPTIME(fed6->last)); 309 rec->s_port = fed6->r.r_sport; 310 rec->d_port = fed6->r.r_dport; 311 rec->flags = fed6->tcp_flags; 312 rec->prot = fed6->r.r_ip_p; 313 rec->tos = fed6->r.r_tos; 314 rec->dst_mask = fed6->dst_mask; 315 rec->src_mask = fed6->src_mask; 316 317 /* Not supported fields. */ 318 rec->src_as = rec->dst_as = 0; 319 320 len = sizeof(struct netflow_v9_record_ipv6_tcp); 321 break; 322 } 323 #endif 324 default: 325 { 326 CTR1(KTR_NET, "export9_add(): Don't know what to do with %d flow type!", flow_type); 327 return (0); 328 } 329 } 330 331 /* Check if new records has the same template */ 332 if (flow_type != t->flow_type) { 333 /* close old flowset */ 334 if (t->flow_type != 0) 335 close_flowset(m, t); 336 337 t->flow_type = flow_type; 338 t->flow_header = m_pktlen(m); 339 340 /* Generate data flowset ID */ 341 fsh.id = htons(NETFLOW_V9_MAX_RESERVED_FLOWSET + flow_type); 342 fsh.length = 0; 343 344 /* m_append should not fail since all data is already allocated */ 345 if (m_append(m, sizeof(fsh), (void *)&fsh) != 1) 346 panic("ng_netflow: m_append() failed"); 347 348 } 349 350 if (m_append(m, len, (void *)&rg.rec) != 1) 351 panic("ng_netflow: m_append() failed"); 352 353 t->count++; 354 355 if (m_pktlen(m) + sizeof(struct netflow_v9_record_general) + sizeof(struct netflow_v9_flowset_header) >= _NETFLOW_V9_MAX_SIZE(t->mtu)) 356 return (1); /* end of datagram */ 357 return (0); 358 } 359 360 /* 361 * Detach export datagram from fib instance, if there is any. 362 * If there is no, allocate a new one. 363 */ 364 item_p 365 get_export9_dgram(priv_p priv, fib_export_p fe, struct netflow_v9_packet_opt **tt) 366 { 367 item_p item = NULL; 368 struct netflow_v9_packet_opt *t = NULL; 369 370 mtx_lock(&fe->export9_mtx); 371 if (fe->exp.item9 != NULL) { 372 item = fe->exp.item9; 373 fe->exp.item9 = NULL; 374 t = fe->exp.item9_opt; 375 fe->exp.item9_opt = NULL; 376 } 377 mtx_unlock(&fe->export9_mtx); 378 379 if (item == NULL) { 380 struct netflow_v9_export_dgram *dgram; 381 struct mbuf *m; 382 uint16_t mtu = priv->mtu; 383 384 /* Allocate entire packet at once, allowing easy m_append() calls */ 385 m = m_getm(NULL, mtu, M_NOWAIT, MT_DATA); 386 if (m == NULL) 387 return (NULL); 388 389 t = malloc(sizeof(struct netflow_v9_packet_opt), M_NETFLOW_GENERAL, M_NOWAIT | M_ZERO); 390 if (t == NULL) { 391 m_free(m); 392 return (NULL); 393 } 394 395 item = ng_package_data(m, NG_NOFLAGS); 396 if (item == NULL) { 397 free(t, M_NETFLOW_GENERAL); 398 return (NULL); 399 } 400 401 dgram = mtod(m, struct netflow_v9_export_dgram *); 402 dgram->header.count = 0; 403 dgram->header.version = htons(NETFLOW_V9); 404 /* Set mbuf current data length */ 405 m->m_len = m->m_pkthdr.len = sizeof(struct netflow_v9_header); 406 407 t->count = 0; 408 t->mtu = mtu; 409 t->flow_header = m->m_len; 410 411 /* 412 * Check if we need to insert templates into packet 413 */ 414 415 struct netflow_v9_flowset_header *fl; 416 417 if ((time_uptime >= priv->templ_time + fe->templ_last_ts) || 418 (fe->sent_packets >= priv->templ_packets + fe->templ_last_pkt)) { 419 fe->templ_last_ts = time_uptime; 420 fe->templ_last_pkt = fe->sent_packets; 421 422 fl = priv->v9_flowsets[0]; 423 m_append(m, ntohs(fl->length), (void *)fl); 424 t->flow_header = m->m_len; 425 t->count += priv->flowset_records[0]; 426 } 427 } 428 429 *tt = t; 430 return (item); 431 } 432 433 /* 434 * Re-attach incomplete datagram back to fib instance. 435 * If there is already another one, then send incomplete. 436 */ 437 void 438 return_export9_dgram(priv_p priv, fib_export_p fe, item_p item, struct netflow_v9_packet_opt *t, int flags) 439 { 440 /* 441 * It may happen on SMP, that some thread has already 442 * put its item there, in this case we bail out and 443 * send what we have to collector. 444 */ 445 mtx_lock(&fe->export9_mtx); 446 if (fe->exp.item9 == NULL) { 447 fe->exp.item9 = item; 448 fe->exp.item9_opt = t; 449 mtx_unlock(&fe->export9_mtx); 450 } else { 451 mtx_unlock(&fe->export9_mtx); 452 export9_send(priv, fe, item, t, flags); 453 } 454 } 455 456 /* Allocate memory and set up flow cache */ 457 void 458 ng_netflow_v9_cache_init(priv_p priv) 459 { 460 generate_v9_templates(priv); 461 462 priv->templ_time = NETFLOW_V9_MAX_TIME_TEMPL; 463 priv->templ_packets = NETFLOW_V9_MAX_PACKETS_TEMPL; 464 priv->mtu = BASE_MTU; 465 } 466 467 /* Free all flow cache memory. Called from ng_netflow_cache_flush() */ 468 void 469 ng_netflow_v9_cache_flush(priv_p priv) 470 { 471 int i; 472 473 /* Free flowsets*/ 474 for (i = 0; i < priv->flowsets_count; i++) 475 free(priv->v9_flowsets[i], M_NETFLOW_GENERAL); 476 } 477 478 /* Get a snapshot of NetFlow v9 settings */ 479 void 480 ng_netflow_copyv9info(priv_p priv, struct ng_netflow_v9info *i) 481 { 482 483 i->templ_time = priv->templ_time; 484 i->templ_packets = priv->templ_packets; 485 i->mtu = priv->mtu; 486 } 487