├── .travis.yml ├── Dockerfile ├── README.md ├── LICENSE └── main.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang 2 | 3 | RUN apt-get install -y git 4 | 5 | RUN go get github.com/czerwonk/udp-mirror 6 | 7 | ENTRYPOINT [ "udp-mirror", "-receivers" ] 8 | EXPOSE 9999 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # udp-mirror [![Build Status](https://travis-ci.org/czerwonk/udp-mirror.svg)][travis] 2 | Listens for UDP packets an sends copies to multiple receivers 3 | 4 | # Install 5 | ``` 6 | go get -u github.com/czerwonk/udp-mirror 7 | ``` 8 | # Application 9 | This tool is helpful if you want to use more than one netflow analysis tool at the same time. 10 | 11 | # Use 12 | In this example we want to listen for packets on port 4560. Each packet received should be mirrored and sent to 192.168.1.1:1234 and 192.168.1.2:3456 13 | ``` 14 | udp-mirror -listen-address ":4560" -receivers "192.168.1.1:1234,192.168.1.2:3456" 15 | ``` 16 | ## Docker 17 | ``` 18 | docker run -it -p 4560:9999 czerwonk/udp-mirror "192.168.1.1:1234,192.168.1.2:3456" 19 | ``` 20 | [travis]: https://travis-ci.org/czerwonk/udp-mirror 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Daniel Czerwonk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "net" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | const version string = "0.1" 13 | 14 | var ( 15 | listenAddress = flag.String("listen-address", ":9999", "UDP port to listen for incoming packets") 16 | receiverAddresses = flag.String("receivers", "", "comma seperated list of copy receivers") 17 | bufferSize = flag.Int("buffer-size", 1024, "size of read buffer") 18 | debug = flag.Bool("debug", false, "debug mode") 19 | showVersion = flag.Bool("version", false, "show version info") 20 | ) 21 | 22 | type receiver struct { 23 | address string 24 | channel chan []byte 25 | } 26 | 27 | func init() { 28 | flag.Usage = func() { 29 | fmt.Println("Usage: udp-mirror [ ... ]\n\nParameters:\n") 30 | flag.PrintDefaults() 31 | } 32 | } 33 | 34 | func main() { 35 | flag.Parse() 36 | 37 | if *showVersion { 38 | printVersion() 39 | os.Exit(0) 40 | } 41 | 42 | if len(*receiverAddresses) == 0 { 43 | fmt.Println("No receivers defined!") 44 | os.Exit(1) 45 | } 46 | 47 | receivers := getReceivers() 48 | 49 | for _, r := range receivers { 50 | go startReceiverWorker(r) 51 | } 52 | 53 | startServer(receivers) 54 | } 55 | 56 | func printVersion() { 57 | fmt.Println("udp-mirror") 58 | fmt.Printf("Version: %s\n", version) 59 | fmt.Println("Author: Daniel Czerwonk") 60 | fmt.Println("Source code: https://github.com/czerwonk/udp-mirror") 61 | } 62 | 63 | func startServer(receivers []*receiver) { 64 | log.Println("Starting listening") 65 | conn, err := net.ListenPacket("udp", *listenAddress) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | defer conn.Close() 70 | 71 | log.Printf("Listening on %s. Waiting for packets", *listenAddress) 72 | 73 | for { 74 | buf := make([]byte, *bufferSize) 75 | len, _, err := conn.ReadFrom(buf) 76 | if err != nil { 77 | log.Println(err) 78 | } 79 | 80 | if *debug { 81 | log.Printf("Received (%d)", len) 82 | } 83 | 84 | go func() { 85 | for _, r := range receivers { 86 | r.channel <- buf[:len] 87 | } 88 | }() 89 | } 90 | } 91 | 92 | func getReceivers() []*receiver { 93 | receivers := make([]*receiver, 0) 94 | 95 | for _, x := range strings.Split(*receiverAddresses, ",") { 96 | r := &receiver{address: strings.TrimSpace(x), channel: make(chan []byte)} 97 | receivers = append(receivers, r) 98 | } 99 | 100 | return receivers 101 | } 102 | 103 | func startReceiverWorker(r *receiver) { 104 | conn, err := net.Dial("udp", r.address) 105 | if err != nil { 106 | log.Println("Could not connect to receiver %s: %s", r.address, err) 107 | return 108 | } 109 | defer conn.Close() 110 | 111 | log.Printf("Adding receiver: %s\n", r.address) 112 | 113 | for { 114 | d := <-r.channel 115 | _, err := conn.Write(d) 116 | if err != nil { 117 | log.Printf("%s: %s", r.address, err) 118 | } 119 | 120 | if *debug { 121 | log.Printf("Packet sent to %s (%d)", r.address, len(d)) 122 | } 123 | } 124 | } 125 | --------------------------------------------------------------------------------