├── .dockerignore ├── .github └── workflows │ └── image-build.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── binaries └── nflow-generator-x86_64-linux ├── go.mod ├── go.sum ├── nflow-generator.go ├── nflow_logging.go ├── nflow_payload.go └── nflow_spike.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | binaries 3 | .gitignore 4 | LICENSE 5 | README.md 6 | -------------------------------------------------------------------------------- /.github/workflows/image-build.yml: -------------------------------------------------------------------------------- 1 | name: nflow-generator image CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | - cron: '0 0 * * *' # daily at midnight 10 | workflow_dispatch: 11 | 12 | jobs: 13 | 14 | build: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Build the nflow-generator image 21 | run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Intellij 27 | .idea/ 28 | *.iml 29 | 30 | # OSX generated files 31 | .DS_Store 32 | 33 | # Project binary 34 | nflow-generator 35 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine as build 2 | COPY . /src 3 | WORKDIR /src 4 | RUN go build -v . 5 | 6 | FROM alpine:latest 7 | MAINTAINER Brent Salisbury 8 | 9 | COPY --from=build /src/nflow-generator /usr/local/bin/ 10 | ENTRYPOINT ["/usr/local/bin/nflow-generator"] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Usage - nflow-generator 2 | 3 | [![nflow-generator image CI](https://github.com/nerdalert/nflow-generator/actions/workflows/image-build.yml/badge.svg)](https://github.com/nerdalert/nflow-generator/actions/workflows/image-build.yml) 4 | 5 | This program generates mock netflow (v5) data that can be used to test netflow collector programs. 6 | The program simulates a router that is exporting flow records to the collector. 7 | It is useful for determining whether the netflow collector is operating and/or receiving netflow datagrams. 8 | 9 | nflow-generator generates several netflow datagrams per second, each with 8 or 16 records for varying kinds of traffic (HTTP, SSH, SNMP, DNS, MySQL, and many others.) 10 | 11 | ### Docker Image Run (Easiest) 12 | 13 | Simply run in a container and pass any arguments at runtime. Below is an example passing the `--help` flag: 14 | 15 | ``` 16 | docker run -it --rm networkstatic/nflow-generator --help 17 | # or podman/quay repos 18 | podman run -it --rm /quay.io/networkstatic/nflow-generator --help 19 | ``` 20 | 21 | To generate mock flow data simply add the target IP and port: 22 | 23 | ``` 24 | docker run -it --rm networkstatic/nflow-generator -t -p 25 | # or podman/quay repos 26 | podman run -it --rm /quay.io/networkstatic/nflow-generator -t -p 27 | ``` 28 | 29 | ### Download the binary 30 | 31 | You can download the Linux binary here [nflow-generator-x86_64-linux](https://github.com/nerdalert/nflow-generator/blob/master/binaries/nflow-generator-x86_64-linux). 32 | ### Build 33 | 34 | Install [Go](http://golang.org/doc/install), then: 35 | 36 | git clone https://github.com/nerdalert/nflow-generator.git 37 | cd nflow-generator 38 | go build 39 | 40 | Go build will leave a binary in the root directory that can be run. 41 | 42 | ### RUN 43 | 44 | Feed it the target collector and port, and optional "false-index" flag: 45 | 46 | ./nflow-generator -t -p [ -f | --false-index ] 47 | 48 | ### Run a Test Collection 49 | 50 | You can run a simple test collection using nfcapd from the nfdump package with the following. 51 | 52 | - Start a netflow collector 53 | 54 | ``` 55 | sudo apt-get install nfdump 56 | mkdir /tmp/nfcap-test 57 | nfcapd -E -p 9001 -l /tmp/nfcap-test 58 | ``` 59 | 60 | In a seperate console, run the netflow-generator pointing at an IP on the host the collector is running on (in this case the VM has an IP of 192.168.1.113). 61 | 62 | ``` 63 | sudo docker run -it --rm networkstatic/nflow-generator -t 192.168.1.113 -p 9001 64 | ``` 65 | 66 | - You should start seeing records displayed to the output of the screen running nfcapd like the following. 67 | 68 | ``` 69 | $> nfcapd -E -p 9001 -l /tmp/nfcap-test 70 | Add extension: 2 byte input/output interface index 71 | Add extension: 4 byte input/output interface index 72 | Add extension: 2 byte src/dst AS number 73 | Add extension: 4 byte src/dst AS number 74 | Bound to IPv4 host/IP: any, Port: 9001 75 | Startup. 76 | Init IPFIX: Max number of IPFIX tags: 62 77 | 78 | Flow Record: 79 | Flags = 0x00 FLOW, Unsampled 80 | export sysid = 1 81 | size = 56 82 | first = 1552592037 [2019-03-14 15:33:57] 83 | last = 1552592038 [2019-03-14 15:33:58] 84 | msec_first = 973 85 | msec_last = 414 86 | src addr = 112.10.20.10 87 | dst addr = 172.30.190.10 88 | src port = 40 89 | dst port = 80 90 | fwd status = 0 91 | tcp flags = 0x00 ...... 92 | proto = 6 TCP 93 | (src)tos = 0 94 | (in)packets = 792 95 | (in)bytes = 23 96 | input = 0 97 | output = 0 98 | src as = 48730 99 | dst as = 15401 100 | 101 | 102 | Flow Record: 103 | Flags = 0x00 FLOW, Unsampled 104 | export sysid = 1 105 | size = 56 106 | first = 1552592038 [2019-03-14 15:33:58] 107 | last = 1552592038 [2019-03-14 15:33:58] 108 | msec_first = 229 109 | msec_last = 379 110 | src addr = 192.168.20.10 111 | dst addr = 202.12.190.10 112 | src port = 40 113 | dst port = 443 114 | fwd status = 0 115 | tcp flags = 0x00 ...... 116 | proto = 6 TCP 117 | (src)tos = 0 118 | (in)packets = 599 119 | (in)bytes = 602 120 | input = 0 121 | output = 0 122 | src as = 1115 123 | dst as = 50617 124 | 125 | ``` 126 | 127 | ### Notes 128 | 129 | The original mock netflow generator placed random values in several fields which confused 130 | certain netflow collectors that complained about inaccurate time stamps, 131 | and were confused by the random values sent in the input and output interface fields. 132 | 133 | Changes: 134 | 135 | * Sets the `SysUptime`, `unix_secs`, and `unix_nsecs` fields of the Netflow datagrams to sensible (UTC) values 136 | * Generates a unique `flow_sequence` value for each netflow datagram 137 | * Creates reasonable start/stop times for flows, so the First is set to (now-X) and Last to (now-Y), where X & Y are random times, and X > Y. 138 | * If the --false-index (-f) flag is set on the command line, 139 | use this algorithm to set the interface indexes to 1 or 2: 140 | If the source address > dest address, input interface is set to 1, and set to 2 otherwise, 141 | and the output interface is set to the opposite value. 142 | If the -f is missing, both snmp interface indexes will be set to 0. [Default] 143 | 144 | To learn more about Netflow version 5 datagram formats, see the [Cisco Netflow documentation](http://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html) 145 | -------------------------------------------------------------------------------- /binaries/nflow-generator-x86_64-linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nerdalert/nflow-generator/b7cd1199871c7ad9a74d8e0efae1768277019d0e/binaries/nflow-generator-x86_64-linux -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/nerdalert/nflow-generator 2 | 3 | go 1.12 4 | 5 | 6 | require ( 7 | github.com/jessevdk/go-flags v1.5.0 8 | github.com/sirupsen/logrus v1.8.1 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= 4 | github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= 8 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 9 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 10 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 11 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 12 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= 13 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 14 | 15 | -------------------------------------------------------------------------------- /nflow-generator.go: -------------------------------------------------------------------------------- 1 | // Run using: 2 | // go run nflow-generator.go nflow_logging.go nflow_payload.go -t 172.16.86.138 -p 9995 3 | // Or: 4 | // go build 5 | // ./nflow-generator -t -p 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "github.com/jessevdk/go-flags" 11 | "math/rand" 12 | "net" 13 | "os" 14 | "time" 15 | ) 16 | 17 | type Proto int 18 | 19 | const ( 20 | FTP Proto = iota + 1 21 | SSH 22 | DNS 23 | HTTP 24 | HTTPS 25 | NTP 26 | SNMP 27 | IMAPS 28 | MYSQL 29 | HTTPS_ALT 30 | P2P 31 | BITTORRENT 32 | ) 33 | 34 | var opts struct { 35 | CollectorIP string `short:"t" long:"target" description:"target ip address of the netflow collector"` 36 | CollectorPort string `short:"p" long:"port" description:"port number of the target netflow collector"` 37 | SpikeProto string `short:"s" long:"spike" description:"run a second thread generating a spike for the specified protocol"` 38 | FalseIndex bool `short:"f" long:"false-index" description:"generate false SNMP interface indexes, otherwise set to 0"` 39 | FlowCount int `short:"c" long:"flow-count" description:"set the number of flows to generate in each iteration" default:"16" max:"128" min:"8"` 40 | Help bool `short:"h" long:"help" description:"show nflow-generator help"` 41 | } 42 | 43 | func main() { 44 | 45 | _, err := flags.Parse(&opts) 46 | if err != nil { 47 | showUsage() 48 | os.Exit(1) 49 | } 50 | if opts.Help == true { 51 | showUsage() 52 | os.Exit(1) 53 | } 54 | if opts.CollectorIP == "" || opts.CollectorPort == "" { 55 | showUsage() 56 | os.Exit(1) 57 | } 58 | collector := opts.CollectorIP + ":" + opts.CollectorPort 59 | udpAddr, err := net.ResolveUDPAddr("udp", collector) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | conn, err := net.DialUDP("udp", nil, udpAddr) 64 | if err != nil { 65 | log.Fatal("Error connecting to the target collector: ", err) 66 | } 67 | log.Infof("sending netflow data to a collector ip: %s and port: %s. \n"+ 68 | "Use ctrl^c to terminate the app.", opts.CollectorIP, opts.CollectorPort) 69 | 70 | for { 71 | rand.Seed(time.Now().Unix()) 72 | n := randomNum(50, 1000) 73 | // add spike data 74 | if opts.SpikeProto != "" { 75 | GenerateSpike() 76 | } 77 | if n > 900 { 78 | data := GenerateNetflow(8) 79 | buffer := BuildNFlowPayload(data) 80 | _, err := conn.Write(buffer.Bytes()) 81 | if err != nil { 82 | log.Fatal("Error connecting to the target collector: ", err) 83 | } 84 | } else { 85 | data := GenerateNetflow(opts.FlowCount) 86 | buffer := BuildNFlowPayload(data) 87 | _, err := conn.Write(buffer.Bytes()) 88 | if err != nil { 89 | log.Fatal("Error connecting to the target collector: ", err) 90 | } 91 | } 92 | // add some periodic spike data 93 | if n < 150 { 94 | sleepInt := time.Duration(3000) 95 | time.Sleep(sleepInt * time.Millisecond) 96 | } 97 | sleepInt := time.Duration(n) 98 | time.Sleep(sleepInt * time.Millisecond) 99 | } 100 | } 101 | 102 | func randomNum(min, max int) int { 103 | return rand.Intn(max-min) + min 104 | } 105 | 106 | func showUsage() { 107 | var usage string 108 | usage = ` 109 | Usage: 110 | main [OPTIONS] [collector IP address] [collector port number] 111 | 112 | Send mock Netflow version 5 data to designated collector IP & port. 113 | Time stamps in all datagrams are set to UTC. 114 | 115 | Application Options: 116 | -t, --target= target ip address of the netflow collector 117 | -p, --port= port number of the target netflow collector 118 | -s, --spike run a second thread generating a spike for the specified protocol 119 | protocol options are as follows: 120 | ftp - generates tcp/21 121 | ssh - generates tcp/22 122 | dns - generates udp/54 123 | http - generates tcp/80 124 | https - generates tcp/443 125 | ntp - generates udp/123 126 | snmp - generates ufp/161 127 | imaps - generates tcp/993 128 | mysql - generates tcp/3306 129 | https_alt - generates tcp/8080 130 | p2p - generates udp/6681 131 | bittorrent - generates udp/6682 132 | -f, --false-index generate a false snmp index values of 1 or 2. The default is 0. (Optional) 133 | -c, --flow-count set the number of flows to generate in each iteration. The default is 16. (Optional) 134 | 135 | Example Usage: 136 | 137 | -first build from source (one time) 138 | go build 139 | 140 | -generate default flows to device 172.16.86.138, port 9995 141 | ./nflow-generator -t 172.16.86.138 -p 9995 142 | 143 | -generate default flows along with a spike in the specified protocol: 144 | ./nflow-generator -t 172.16.86.138 -p 9995 -s ssh 145 | 146 | -generate default flows with "false index" settings for snmp interfaces 147 | ./nflow-generator -t 172.16.86.138 -p 9995 -f 148 | 149 | -generate default flows with up to 256 flows 150 | ./nflow-generator -c 128 -t 172.16.86.138 -p 9995 151 | 152 | Help Options: 153 | -h, --help Show this help message 154 | ` 155 | fmt.Print(usage) 156 | } 157 | -------------------------------------------------------------------------------- /nflow_logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | ) 6 | 7 | var log = logrus.New() 8 | 9 | func SetLogger(l *logrus.Logger) { 10 | log = l 11 | } 12 | -------------------------------------------------------------------------------- /nflow_payload.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "math/rand" 7 | "net" 8 | "time" 9 | ) 10 | 11 | // Start time for this instance, used to compute sysUptime 12 | var StartTime = time.Now().UnixNano() 13 | 14 | // current sysUptime in msec - recalculated in CreateNFlowHeader() 15 | var sysUptime uint32 = 0 16 | 17 | // Counter of flow packets that have been sent 18 | var flowSequence uint32 = 0 19 | 20 | const ( 21 | FTP_PORT = 21 22 | SSH_PORT = 22 23 | DNS_PORT = 53 24 | HTTP_PORT = 80 25 | HTTPS_PORT = 443 26 | NTP_PORT = 123 27 | SNMP_PORT = 161 28 | IMAPS_PORT = 993 29 | MYSQL_PORT = 3306 30 | HTTPS_ALT_PORT = 8080 31 | P2P_PORT = 6681 32 | BITTORRENT_PORT = 6682 33 | UINT16_MAX = 65535 34 | PAYLOAD_AVG_MD = 1024 35 | PAYLOAD_AVG_SM = 256 36 | ) 37 | 38 | // struct data from fach 39 | type NetflowHeader struct { 40 | Version uint16 41 | FlowCount uint16 42 | SysUptime uint32 43 | UnixSec uint32 44 | UnixMsec uint32 45 | FlowSequence uint32 46 | EngineType uint8 47 | EngineId uint8 48 | SampleInterval uint16 49 | } 50 | 51 | type NetflowPayload struct { 52 | SrcIP uint32 53 | DstIP uint32 54 | NextHopIP uint32 55 | SnmpInIndex uint16 56 | SnmpOutIndex uint16 57 | NumPackets uint32 58 | NumOctets uint32 59 | SysUptimeStart uint32 60 | SysUptimeEnd uint32 61 | SrcPort uint16 62 | DstPort uint16 63 | Padding1 uint8 64 | TcpFlags uint8 65 | IpProtocol uint8 66 | IpTos uint8 67 | SrcAsNumber uint16 68 | DstAsNumber uint16 69 | SrcPrefixMask uint8 70 | DstPrefixMask uint8 71 | Padding2 uint16 72 | } 73 | 74 | //Complete netflow records 75 | type Netflow struct { 76 | Header NetflowHeader 77 | Records []NetflowPayload 78 | } 79 | 80 | //Marshall NetflowData into a buffer 81 | func BuildNFlowPayload(data Netflow) bytes.Buffer { 82 | buffer := new(bytes.Buffer) 83 | err := binary.Write(buffer, binary.BigEndian, &data.Header) 84 | if err != nil { 85 | log.Println("Writing netflow header failed:", err) 86 | } 87 | for _, record := range data.Records { 88 | err := binary.Write(buffer, binary.BigEndian, &record) 89 | if err != nil { 90 | log.Println("Writing netflow record failed:", err) 91 | } 92 | } 93 | return *buffer 94 | } 95 | 96 | //Generate a netflow packet w/ user-defined record count 97 | func GenerateNetflow(recordCount int) Netflow { 98 | data := new(Netflow) 99 | header := CreateNFlowHeader(recordCount) 100 | records := []NetflowPayload{} 101 | if recordCount == 8 { 102 | // overwrite payload to add some variations for traffic spikes. 103 | records = CreateVariablePayload(recordCount) 104 | } else { 105 | records = CreateNFlowPayload(recordCount) 106 | } 107 | 108 | data.Header = header 109 | data.Records = records 110 | return *data 111 | } 112 | 113 | //Generate and initialize netflow header 114 | func CreateNFlowHeader(recordCount int) NetflowHeader { 115 | 116 | t := time.Now().UnixNano() 117 | sec := t / int64(time.Second) 118 | nsec := t - sec*int64(time.Second) 119 | sysUptime = uint32((t-StartTime) / int64(time.Millisecond))+1000 120 | flowSequence++ 121 | 122 | // log.Infof("Time: %d; Seconds: %d; Nanoseconds: %d\n", t, sec, nsec) 123 | // log.Infof("StartTime: %d; sysUptime: %d", StartTime, sysUptime) 124 | // log.Infof("FlowSequence %d", flowSequence) 125 | 126 | h := new(NetflowHeader) 127 | h.Version = 5 128 | h.FlowCount = uint16(recordCount) 129 | h.SysUptime = sysUptime 130 | h.UnixSec = uint32(sec) 131 | h.UnixMsec = uint32(nsec) 132 | h.FlowSequence = flowSequence 133 | h.EngineType = 1 134 | h.EngineId = 0 135 | h.SampleInterval = 0 136 | return *h 137 | } 138 | 139 | func CreateVariablePayload(recordCount int) []NetflowPayload { 140 | payload := make([]NetflowPayload, recordCount) 141 | 142 | for i := 0; i < recordCount; i++ { 143 | payload[0] = CreateHttpFlow() 144 | payload[1] = CreateHttpsFlow() 145 | payload[2] = CreateHttpAltFlow() 146 | payload[3] = CreateDnsFlow() 147 | payload[5] = CreateNtpFlow() 148 | payload[6] = CreateImapsFlow() 149 | payload[7] = CreateMySqlFlow() 150 | } 151 | 152 | return payload 153 | } 154 | 155 | func CreateNFlowPayload(recordCount int) []NetflowPayload { 156 | payload := make([]NetflowPayload, recordCount) 157 | for i := 0; i < recordCount; i++ { 158 | payload[0] = CreateHttpFlow() 159 | payload[1] = CreateHttpsFlow() 160 | payload[2] = CreateHttpAltFlow() 161 | payload[3] = CreateDnsFlow() 162 | payload[4] = CreateIcmpFlow() 163 | payload[5] = CreateNtpFlow() 164 | payload[6] = CreateImapsFlow() 165 | payload[7] = CreateMySqlFlow() 166 | payload[8] = CreateRandomFlow() 167 | payload[9] = CreateSshFlow() 168 | payload[10] = CreateP2pFlow() 169 | payload[11] = CreateBitorrentFlow() 170 | payload[12] = CreateFTPFlow() 171 | payload[13] = CreateSnmpFlow() 172 | payload[14] = CreateIcmpFlow() 173 | payload[15] = CreateRandomFlow() 174 | } 175 | return payload 176 | } 177 | 178 | //Initialize netflow record with random data 179 | func CreateIcmpFlow() NetflowPayload { 180 | payload := new(NetflowPayload) 181 | payload.SrcIP = IPtoUint32("172.16.50.10") 182 | payload.DstIP = IPtoUint32("132.12.130.10") 183 | payload.NextHopIP = IPtoUint32("132.12.130.1") 184 | payload.SrcPort = 0 185 | payload.DstPort = 0 186 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 187 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 188 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_SM) 189 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_SM) 190 | // payload.SysUptimeStart = rand.Uint32() 191 | // payload.SysUptimeEnd = rand.Uint32() 192 | // payload.Padding1 = 0 193 | // payload.IpProtocol = 1 194 | // payload.IpTos = 0 195 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 196 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 197 | // payload.Padding2 = 0 198 | FillCommonFields(payload, PAYLOAD_AVG_SM, 1, rand.Intn(32)) 199 | return *payload 200 | } 201 | 202 | //Initialize netflow record with random data 203 | func CreateHttpFlow() NetflowPayload { 204 | payload := new(NetflowPayload) 205 | payload.SrcIP = IPtoUint32("112.10.20.10") 206 | payload.DstIP = IPtoUint32("172.30.190.10") 207 | payload.NextHopIP = IPtoUint32("172.199.15.1") 208 | payload.SrcPort = uint16(40) 209 | payload.DstPort = uint16(HTTP_PORT) 210 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 211 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 212 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 213 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 214 | // payload.SysUptimeStart = rand.Uint32() 215 | // payload.SysUptimeEnd = rand.Uint32() 216 | // payload.Padding1 = 0 217 | // payload.IpProtocol = 6 218 | // payload.IpTos = 0 219 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 220 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 221 | // payload.Padding2 = 0 222 | FillCommonFields(payload, PAYLOAD_AVG_MD, 6, rand.Intn(32)) 223 | return *payload 224 | } 225 | 226 | //Initialize netflow record with random data 227 | func CreateSnmpFlow() NetflowPayload { 228 | payload := new(NetflowPayload) 229 | payload.SrcIP = IPtoUint32("112.10.20.10") 230 | payload.DstIP = IPtoUint32("172.30.190.10") 231 | payload.NextHopIP = IPtoUint32("172.199.15.1") 232 | payload.SrcPort = uint16(40) 233 | payload.DstPort = uint16(SNMP_PORT) 234 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 235 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 236 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 237 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 238 | // payload.SysUptimeStart = rand.Uint32() 239 | // payload.SysUptimeEnd = rand.Uint32() 240 | // payload.Padding1 = 0 241 | // payload.IpProtocol = 17 242 | // payload.IpTos = 0 243 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 244 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 245 | // payload.Padding2 = 0 246 | FillCommonFields(payload, PAYLOAD_AVG_MD, 17, rand.Intn(32)) 247 | return *payload 248 | } 249 | 250 | func CreateFTPFlow() NetflowPayload { 251 | payload := new(NetflowPayload) 252 | payload.SrcIP = IPtoUint32("112.10.100.10") 253 | payload.DstIP = IPtoUint32("192.168.120.10") 254 | payload.NextHopIP = IPtoUint32("172.199.15.1") 255 | payload.SrcPort = uint16(40) 256 | payload.DstPort = uint16(FTP_PORT) 257 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 258 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 259 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 260 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 261 | // payload.SysUptimeStart = rand.Uint32() 262 | // payload.SysUptimeEnd = rand.Uint32() 263 | // payload.Padding1 = 0 264 | // payload.IpProtocol = 6 265 | // payload.IpTos = 0 266 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 267 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 268 | // payload.Padding2 = 0 269 | FillCommonFields(payload, PAYLOAD_AVG_MD, 6, rand.Intn(32)) 270 | return *payload 271 | } 272 | 273 | func CreateNtpFlow() NetflowPayload { 274 | payload := new(NetflowPayload) 275 | payload.SrcIP = IPtoUint32("247.104.20.202") 276 | payload.DstIP = IPtoUint32("10.12.190.10") 277 | payload.NextHopIP = IPtoUint32("192.199.15.1") 278 | payload.SrcPort = uint16(40) 279 | payload.DstPort = uint16(NTP_PORT) 280 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 281 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 282 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 283 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 284 | // payload.SysUptimeStart = rand.Uint32() 285 | // payload.SysUptimeEnd = rand.Uint32() 286 | // payload.Padding1 = 0 287 | // payload.IpProtocol = 17 288 | // payload.IpTos = 0 289 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 290 | // payload.SrcPrefixMask = uint8(32) 291 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 292 | // payload.Padding2 = 0 293 | FillCommonFields(payload, PAYLOAD_AVG_MD, 17, 32) 294 | return *payload 295 | } 296 | 297 | func CreateP2pFlow() NetflowPayload { 298 | payload := new(NetflowPayload) 299 | payload.SrcIP = IPtoUint32("247.104.20.202") 300 | payload.DstIP = IPtoUint32("10.12.190.10") 301 | payload.NextHopIP = IPtoUint32("192.199.15.1") 302 | payload.SrcPort = uint16(40) 303 | payload.DstPort = uint16(P2P_PORT) 304 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 305 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 306 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 307 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 308 | // payload.SysUptimeStart = rand.Uint32() 309 | // payload.SysUptimeEnd = rand.Uint32() 310 | // payload.Padding1 = 0 311 | // payload.IpProtocol = 17 312 | // payload.IpTos = 0 313 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 314 | 315 | // payload.SrcPrefixMask = uint8(32) 316 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 317 | // payload.Padding2 = 0 318 | FillCommonFields(payload, PAYLOAD_AVG_MD, 17, 32) 319 | return *payload 320 | } 321 | 322 | func CreateBitorrentFlow() NetflowPayload { 323 | payload := new(NetflowPayload) 324 | 325 | payload.SrcIP = IPtoUint32("192.168.20.202") 326 | payload.DstIP = IPtoUint32("42.12.190.10") 327 | payload.NextHopIP = IPtoUint32("192.199.15.1") 328 | payload.SrcPort = uint16(40) 329 | payload.DstPort = uint16(BITTORRENT_PORT) 330 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 331 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 332 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 333 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 334 | // payload.SysUptimeStart = rand.Uint32() 335 | // payload.SysUptimeEnd = rand.Uint32() 336 | // payload.Padding1 = 0 337 | // payload.IpProtocol = 17 338 | // payload.IpTos = 0 339 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 340 | 341 | // payload.SrcPrefixMask = uint8(32) 342 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 343 | // payload.Padding2 = 0 344 | FillCommonFields(payload, PAYLOAD_AVG_MD, 17, 32) 345 | return *payload 346 | } 347 | 348 | func CreateSshFlow() NetflowPayload { 349 | payload := new(NetflowPayload) 350 | 351 | payload.SrcIP = IPtoUint32("172.30.20.102") 352 | payload.DstIP = IPtoUint32("222.12.190.10") 353 | payload.NextHopIP = IPtoUint32("192.199.15.1") 354 | payload.SrcPort = uint16(40) 355 | payload.DstPort = uint16(SSH_PORT) 356 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 357 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 358 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 359 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 360 | // payload.SysUptimeStart = rand.Uint32() 361 | // payload.SysUptimeEnd = rand.Uint32() 362 | // payload.Padding1 = 0 363 | // payload.IpProtocol = 6 364 | // payload.IpTos = 0 365 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 366 | 367 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 368 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 369 | // payload.Padding2 = 0 370 | FillCommonFields(payload, PAYLOAD_AVG_MD, 6, rand.Intn(32)) 371 | return *payload 372 | } 373 | 374 | func CreateHttpsFlow() NetflowPayload { 375 | payload := new(NetflowPayload) 376 | 377 | payload.SrcIP = IPtoUint32("192.168.20.10") 378 | payload.DstIP = IPtoUint32("202.12.190.10") 379 | payload.NextHopIP = IPtoUint32("172.199.15.1") 380 | payload.SrcPort = uint16(40) 381 | payload.DstPort = uint16(HTTPS_PORT) 382 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 383 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 384 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 385 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 386 | // payload.SysUptimeStart = rand.Uint32() 387 | // payload.SysUptimeEnd = rand.Uint32() 388 | // payload.Padding1 = 0 389 | // payload.IpProtocol = 6 390 | // payload.IpTos = 0 391 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 392 | 393 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 394 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 395 | // payload.Padding2 = 0 396 | FillCommonFields(payload, PAYLOAD_AVG_MD, 6, rand.Intn(32)) 397 | return *payload 398 | } 399 | 400 | func CreateHttpAltFlow() NetflowPayload { 401 | payload := new(NetflowPayload) 402 | 403 | payload.SrcIP = IPtoUint32("10.10.20.122") 404 | payload.DstIP = IPtoUint32("84.12.190.210") 405 | payload.NextHopIP = IPtoUint32("192.199.15.1") 406 | payload.SrcPort = uint16(12001) 407 | payload.DstPort = uint16(HTTPS_ALT_PORT) 408 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 409 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 410 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 411 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 412 | // payload.SysUptimeStart = rand.Uint32() 413 | // payload.SysUptimeEnd = rand.Uint32() 414 | // payload.Padding1 = 0 415 | // payload.IpProtocol = 6 416 | // payload.IpTos = 0 417 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 418 | 419 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 420 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 421 | // payload.Padding2 = 0 422 | FillCommonFields(payload, PAYLOAD_AVG_MD, 6, rand.Intn(32)) 423 | return *payload 424 | } 425 | 426 | func CreateDnsFlow() NetflowPayload { 427 | payload := new(NetflowPayload) 428 | 429 | payload.SrcIP = IPtoUint32("59.220.158.122") 430 | payload.DstIP = IPtoUint32("10.12.233.210") 431 | payload.NextHopIP = IPtoUint32("39.199.15.1") 432 | payload.SrcPort = uint16(9221) 433 | payload.DstPort = uint16(DNS_PORT) 434 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 435 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 436 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 437 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 438 | // payload.SysUptimeStart = rand.Uint32() 439 | // payload.SysUptimeEnd = rand.Uint32() 440 | // payload.Padding1 = 0 441 | // payload.IpProtocol = 17 442 | // payload.IpTos = 0 443 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 444 | 445 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 446 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 447 | // payload.Padding2 = 0 448 | FillCommonFields(payload, PAYLOAD_AVG_MD, 17, rand.Intn(32)) 449 | return *payload 450 | } 451 | 452 | func CreateImapsFlow() NetflowPayload { 453 | payload := new(NetflowPayload) 454 | 455 | payload.SrcIP = IPtoUint32("172.30.20.102") 456 | payload.DstIP = IPtoUint32("62.12.190.10") 457 | payload.NextHopIP = IPtoUint32("131.199.15.1") 458 | payload.SrcPort = uint16(9010) 459 | payload.DstPort = uint16(IMAPS_PORT) 460 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 461 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 462 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 463 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 464 | // payload.SysUptimeStart = rand.Uint32() 465 | // payload.SysUptimeEnd = rand.Uint32() 466 | // payload.Padding1 = 0 467 | // payload.IpProtocol = 6 468 | // payload.IpTos = 0 469 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 470 | 471 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 472 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 473 | // payload.Padding2 = 0 474 | FillCommonFields(payload, PAYLOAD_AVG_MD, 6, rand.Intn(32)) 475 | return *payload 476 | } 477 | 478 | func CreateMySqlFlow() NetflowPayload { 479 | payload := new(NetflowPayload) 480 | 481 | payload.SrcIP = IPtoUint32("10.154.20.12") 482 | payload.DstIP = IPtoUint32("77.12.190.94") 483 | payload.NextHopIP = IPtoUint32("150.20.145.1") 484 | payload.SrcPort = uint16(9010) 485 | payload.DstPort = uint16(MYSQL_PORT) 486 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 487 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 488 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 489 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 490 | // payload.SysUptimeStart = rand.Uint32() 491 | // payload.SysUptimeEnd = rand.Uint32() 492 | // payload.Padding1 = 0 493 | // payload.IpProtocol = 6 494 | // payload.IpTos = 0 495 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 496 | 497 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 498 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 499 | // payload.Padding2 = 0 500 | FillCommonFields(payload, PAYLOAD_AVG_MD, 6, rand.Intn(32)) 501 | return *payload 502 | } 503 | 504 | func CreateRandomFlow() NetflowPayload { 505 | payload := new(NetflowPayload) 506 | 507 | payload.SrcIP = rand.Uint32() 508 | payload.DstIP = rand.Uint32() 509 | payload.NextHopIP = rand.Uint32() 510 | payload.SrcPort = genRandUint16(UINT16_MAX) 511 | payload.DstPort = genRandUint16(UINT16_MAX) 512 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 513 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 514 | // payload.NumPackets = genRandUint32(PAYLOAD_AVG_MD) 515 | // payload.NumOctets = genRandUint32(PAYLOAD_AVG_MD) 516 | // payload.SysUptimeStart = rand.Uint32() 517 | // payload.SysUptimeEnd = rand.Uint32() 518 | // payload.Padding1 = 0 519 | // payload.IpProtocol = 6 520 | // payload.IpTos = 0 521 | // payload.SrcAsNumber = genRandUint16(UINT16_MAX) 522 | 523 | // payload.SrcPrefixMask = uint8(rand.Intn(32)) 524 | // payload.DstPrefixMask = uint8(rand.Intn(32)) 525 | // payload.Padding2 = 0 526 | FillCommonFields(payload, PAYLOAD_AVG_MD, 6, rand.Intn(32)) 527 | return *payload 528 | } 529 | 530 | // patch up the common fields of the packets 531 | func FillCommonFields ( 532 | payload *NetflowPayload, 533 | numPktOct int, 534 | ipProtocol int, 535 | srcPrefixMask int) NetflowPayload { 536 | 537 | // Fill template with values not filled by caller 538 | // payload.SrcIP = IPtoUint32("10.154.20.12") 539 | // payload.DstIP = IPtoUint32("77.12.190.94") 540 | // payload.NextHopIP = IPtoUint32("150.20.145.1") 541 | // payload.SrcPort = uint16(9010) 542 | // payload.DstPort = uint16(MYSQL_PORT) 543 | // payload.SnmpInIndex = genRandUint16(UINT16_MAX) 544 | // payload.SnmpOutIndex = genRandUint16(UINT16_MAX) 545 | payload.NumPackets = genRandUint32(numPktOct) 546 | payload.NumOctets = genRandUint32(numPktOct) 547 | // payload.SysUptimeStart = rand.Uint32() 548 | // payload.SysUptimeEnd = rand.Uint32() 549 | payload.Padding1 = 0 550 | payload.IpProtocol = uint8(ipProtocol) 551 | payload.IpTos = 0 552 | payload.SrcAsNumber = genRandUint16(UINT16_MAX) 553 | payload.DstAsNumber = genRandUint16(UINT16_MAX) 554 | 555 | payload.SrcPrefixMask = uint8(srcPrefixMask) 556 | payload.DstPrefixMask = uint8(rand.Intn(32)) 557 | payload.Padding2 = 0 558 | 559 | // now handle computed values 560 | if !opts.FalseIndex { // default interfaces are zero 561 | payload.SnmpInIndex = 0 562 | payload.SnmpOutIndex = 0 563 | } else if payload.SrcIP > payload.DstIP { // false-index 564 | payload.SnmpInIndex = 1 565 | payload.SnmpOutIndex = 2 566 | } else { 567 | payload.SnmpInIndex = 2 568 | payload.SnmpOutIndex = 1 569 | } 570 | 571 | uptime := int(sysUptime) 572 | payload.SysUptimeEnd = uint32(uptime - randomNum(10,500)) 573 | payload.SysUptimeStart = payload.SysUptimeEnd - uint32(randomNum(10,500)) 574 | 575 | // log.Infof("S&D : %x %x %d, %d", payload.SrcIP, payload.DstIP, payload.DstPort, payload.SnmpInIndex) 576 | // log.Infof("Time: %d %d %d", sysUptime, payload.SysUptimeStart, payload.SysUptimeEnd) 577 | 578 | return *payload 579 | } 580 | 581 | func genRandUint16(max int) uint16 { 582 | return uint16(rand.Intn(max)) 583 | } 584 | 585 | func IPtoUint32(s string) uint32 { 586 | ip := net.ParseIP(s) 587 | return binary.BigEndian.Uint32(ip.To4()) 588 | } 589 | 590 | func genRandUint32(max int) uint32 { 591 | return uint32(rand.Intn(max)) 592 | } 593 | -------------------------------------------------------------------------------- /nflow_spike.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //import "fmt" 4 | 5 | //Generate a netflow packet w/ user-defined record count 6 | func GenerateSpike() Netflow { 7 | data := new(Netflow) 8 | header := CreateNFlowHeader(1) 9 | records := []NetflowPayload{} 10 | records = spikeFlowPayload() 11 | data.Header = header 12 | data.Records = records 13 | return *data 14 | } 15 | 16 | func spikeFlowPayload() []NetflowPayload { 17 | payload := make([]NetflowPayload, 1) 18 | switch opts.SpikeProto { 19 | case "ssh": 20 | payload[0] = CreateSshFlow() 21 | case "ftp": 22 | payload[0] = CreateFTPFlow() 23 | case "http": 24 | payload[0] = CreateHttpFlow() 25 | case "https": 26 | payload[0] = CreateHttpsFlow() 27 | case "ntp": 28 | payload[0] = CreateNtpFlow() 29 | case "snmp": 30 | payload[0] = CreateSnmpFlow() 31 | case "imaps": 32 | payload[0] = CreateImapsFlow() 33 | case "mysql": 34 | payload[0] = CreateMySqlFlow() 35 | case "https_alt": 36 | payload[0] = CreateHttpAltFlow() 37 | case "p2p": 38 | payload[0] = CreateP2pFlow() 39 | case "bittorrent": 40 | payload[0] = CreateBitorrentFlow() 41 | default: 42 | log.Fatalf("protocol option %s is not valid, see --help for options", opts.SpikeProto) 43 | } 44 | return payload 45 | } 46 | --------------------------------------------------------------------------------