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