├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── logger ├── Logger.go └── constants.go ├── test.go └── vrrp ├── NetWork.go ├── VRRPPacket.go ├── VirtualRouter.go └── constants.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | .test.go 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 napw 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VRRP-go 2 | 由golang实现的[VRRP-v3](https://tools.ietf.org/html/rfc5798), 点击超链接获取关于VRRP的信息。 3 | [VRRP-v3](https://tools.ietf.org/html/rfc5798) implemented by golang,click hyperlink get details about VRRP 4 | 5 | ## example 6 | ```go 7 | package main 8 | 9 | import ( 10 | "VRRP/VRRP" 11 | "flag" 12 | "fmt" 13 | "time" 14 | ) 15 | 16 | var ( 17 | VRID int 18 | Priority int 19 | ) 20 | 21 | func init(){ 22 | flag.IntVar(&VRID,"vrid",233,"virtual router ID") 23 | flag.IntVar(&Priority,"pri",100,"router priority") 24 | } 25 | 26 | func main() { 27 | flag.Parse() 28 | var vr = VRRP.NewVirtualRouter(byte(VRID), "ens33", false, VRRP.IPv4) 29 | vr.SetPriorityAndMasterAdvInterval(byte(Priority),time.Millisecond*800) 30 | vr.Enroll(VRRP.Backup2Master, func() { 31 | fmt.Println("init to master") 32 | }) 33 | vr.Enroll(VRRP.Master2Init, func() { 34 | fmt.Println("master to init") 35 | }) 36 | vr.Enroll(VRRP.Master2Backup, func() { 37 | fmt.Println("master to backup") 38 | }) 39 | go func() { 40 | time.Sleep(time.Minute * 5) 41 | vr.Stop() 42 | }() 43 | vr.StartWithEventSelector() 44 | 45 | } 46 | ``` 47 | ```shell 48 | GOOS=linux go build -o vr test.go 49 | #execute on host1 50 | ./vr -vrid=200 -pri=150 51 | #execute on host2 52 | ./vr -vrid=200 -pri=230 53 | ``` 54 | 55 | ## To-DO 56 | 57 | 58 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module vrrp-go 2 | 3 | go 1.21.0 4 | 5 | require github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875 6 | 7 | require golang.org/x/text v0.9.0 // indirect 8 | 9 | require ( 10 | github.com/josharian/native v1.0.0 // indirect 11 | github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 // indirect 12 | github.com/mdlayher/ndp v1.0.1 13 | github.com/mdlayher/packet v1.0.0 // indirect 14 | github.com/mdlayher/socket v0.2.1 // indirect 15 | golang.org/x/net v0.9.0 // indirect 16 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect 17 | golang.org/x/sys v0.7.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 2 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 3 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 4 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 5 | github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= 6 | github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 7 | github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875 h1:ql8x//rJsHMjS+qqEag8n3i4azw1QneKh5PieH9UEbY= 8 | github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875/go.mod h1:kfOoFJuHWp76v1RgZCb9/gVUc7XdY877S2uVYbNliGc= 9 | github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE= 10 | github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og= 11 | github.com/mdlayher/ndp v1.0.1 h1:+yAD79/BWyFlvAoeG5ncPS0ItlHP/eVbH7bQ6/+LVA4= 12 | github.com/mdlayher/ndp v1.0.1/go.mod h1:rf3wKaWhAYJEXFKpgF8kQ2AxypxVbfNcZbqoAo6fVzk= 13 | github.com/mdlayher/packet v1.0.0 h1:InhZJbdShQYt6XV2GPj5XHxChzOfhJJOMbvnGAmOfQ8= 14 | github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU= 15 | github.com/mdlayher/socket v0.2.1 h1:F2aaOwb53VsBE+ebRS9bLd7yPOfYUMC8lOODdCBDY6w= 16 | github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= 17 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 18 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 19 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 20 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= 21 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 22 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 23 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 24 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 25 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 26 | golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 27 | golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= 28 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 29 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 30 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 31 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 32 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 33 | -------------------------------------------------------------------------------- /logger/Logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "os" 8 | ) 9 | 10 | type Logger struct { 11 | level LogLevel 12 | output *log.Logger 13 | } 14 | 15 | func (l *Logger) SetLevel(level LogLevel) { 16 | l.level = level 17 | if level == DEBUG { 18 | l.output.SetFlags(log.Ldate | log.Lmicroseconds) 19 | } 20 | } 21 | 22 | func (l *Logger) SetPrefix(pre string) { 23 | l.output.SetPrefix(pre) 24 | } 25 | 26 | func (l *Logger) Printf(level LogLevel, format string, a ...interface{}) { 27 | if level < l.level { 28 | return 29 | } else { 30 | l.output.Printf(format, a...) 31 | if level == FATAL { 32 | panic(fmt.Sprintf(format, a...)) 33 | } 34 | } 35 | 36 | } 37 | 38 | func NewLogger(o *io.Writer) *Logger { 39 | if o == nil { 40 | return &Logger{level: INFO, output: log.New(os.Stdout, "", log.LstdFlags)} 41 | } else { 42 | return &Logger{level: INFO, output: log.New(*o, "", log.LstdFlags)} 43 | } 44 | } 45 | 46 | var GLoger *Logger 47 | 48 | func init() { 49 | GLoger = NewLogger(nil) 50 | GLoger.SetLevel(INFO) 51 | } 52 | -------------------------------------------------------------------------------- /logger/constants.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | //Log 4 | 5 | type LogLevel int 6 | 7 | const ( 8 | DEBUG LogLevel = iota 9 | INFO 10 | ERROR 11 | FATAL 12 | ) 13 | -------------------------------------------------------------------------------- /test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "time" 7 | VRRP "vrrp-go/vrrp" 8 | ) 9 | 10 | var ( 11 | VRID int 12 | Priority int 13 | ) 14 | 15 | func init() { 16 | flag.IntVar(&VRID, "vrid", 233, "virtual router ID") 17 | flag.IntVar(&Priority, "pri", 100, "router priority") 18 | } 19 | 20 | func main() { 21 | flag.Parse() 22 | var vr = VRRP.NewVirtualRouter(byte(VRID), "ens3", false, VRRP.IPv4) 23 | vr.SetPriorityAndMasterAdvInterval(byte(Priority), time.Millisecond*800) 24 | vr.Enroll(VRRP.Backup2Master, func() { 25 | fmt.Println("init to master") 26 | }) 27 | vr.Enroll(VRRP.Master2Init, func() { 28 | fmt.Println("master to init") 29 | }) 30 | vr.Enroll(VRRP.Master2Backup, func() { 31 | fmt.Println("master to backup") 32 | }) 33 | go func() { 34 | time.Sleep(time.Minute * 5) 35 | vr.Stop() 36 | }() 37 | vr.StartWithEventSelector() 38 | 39 | } 40 | -------------------------------------------------------------------------------- /vrrp/NetWork.go: -------------------------------------------------------------------------------- 1 | package vrrp 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/netip" 7 | "vrrp-go/logger" 8 | 9 | "github.com/mdlayher/arp" 10 | "github.com/mdlayher/ndp" 11 | 12 | "syscall" 13 | "time" 14 | ) 15 | 16 | type IPConnection interface { 17 | WriteMessage(*VRRPPacket) error 18 | ReadMessage() (*VRRPPacket, error) 19 | } 20 | 21 | type AddrAnnouncer interface { 22 | AnnounceAll(vr *VirtualRouter) error 23 | } 24 | 25 | type IPv4AddrAnnouncer struct { 26 | ARPClient *arp.Client 27 | } 28 | 29 | type IPv6AddrAnnouncer struct { 30 | con *ndp.Conn 31 | } 32 | 33 | func NewIPIPv6AddrAnnouncer(nif *net.Interface) *IPv6AddrAnnouncer { 34 | var con, ip, errOfMakeNDPCon = ndp.Listen(nif, ndp.LinkLocal) 35 | if errOfMakeNDPCon != nil { 36 | logger.GLoger.Printf(logger.FATAL, "NewIPv6AddrAnnouncer: %v", errOfMakeNDPCon) 37 | } 38 | logger.GLoger.Printf(logger.INFO, "NDP client initialized, working on %v, source IP %v", nif.Name, ip) 39 | return &IPv6AddrAnnouncer{con: con} 40 | } 41 | 42 | func (nd *IPv6AddrAnnouncer) AnnounceAll(vr *VirtualRouter) error { 43 | for key := range vr.protectedIPaddrs { 44 | address := netip.AddrFrom16(key) 45 | var multicastgroup, errOfParseMulticastGroup = ndp.SolicitedNodeMulticast(address) 46 | if errOfParseMulticastGroup != nil { 47 | logger.GLoger.Printf(logger.ERROR, "IPv6AddrAnnouncer.AnnounceAll: %v", errOfParseMulticastGroup) 48 | return errOfParseMulticastGroup 49 | } else { 50 | //send unsolicited NeighborAdvertisement to refresh link layer address cache 51 | var msg = &ndp.NeighborAdvertisement{ 52 | Override: true, 53 | TargetAddress: address, 54 | Options: []ndp.Option{ 55 | &ndp.LinkLayerAddress{ 56 | Direction: ndp.Source, 57 | Addr: vr.netInterface.HardwareAddr, 58 | }, 59 | }, 60 | } 61 | if errOfWrite := nd.con.WriteTo(msg, nil, multicastgroup); errOfWrite != nil { 62 | logger.GLoger.Printf(logger.ERROR, "IPv6AddrAnnouncer.AnnounceAll: %v", errOfWrite) 63 | return errOfWrite 64 | } else { 65 | logger.GLoger.Printf(logger.INFO, "send unsolicited neighbor advertisement for %v", net.IP(key[:])) 66 | } 67 | } 68 | 69 | } 70 | 71 | return nil 72 | } 73 | 74 | // makeGratuitousPacket make gratuitous ARP packet with out payload 75 | func (ar *IPv4AddrAnnouncer) makeGratuitousPacket() *arp.Packet { 76 | var packet arp.Packet 77 | packet.HardwareType = 1 //ethernet10m 78 | packet.ProtocolType = 0x0800 //IPv4 79 | packet.HardwareAddrLength = 6 80 | packet.IPLength = 4 81 | packet.Operation = 2 //response 82 | return &packet 83 | } 84 | 85 | // AnnounceAll send gratuitous ARP response for all protected IPv4 addresses 86 | func (ar *IPv4AddrAnnouncer) AnnounceAll(vr *VirtualRouter) error { 87 | if errofSetDealLine := ar.ARPClient.SetWriteDeadline(time.Now().Add(500 * time.Microsecond)); errofSetDealLine != nil { 88 | return fmt.Errorf("IPv4AddrAnnouncer.AnnounceAll: %v", errofSetDealLine) 89 | } 90 | var packet = ar.makeGratuitousPacket() 91 | for k := range vr.protectedIPaddrs { 92 | address := netip.AddrFrom4(netip.AddrFrom16(k).As4()) 93 | packet.SenderHardwareAddr = vr.netInterface.HardwareAddr 94 | packet.SenderIP = address 95 | packet.TargetHardwareAddr = BaordcastHADDR 96 | packet.TargetIP = address 97 | logger.GLoger.Printf(logger.INFO, "send gratuitous arp for %v", net.IP(k[:])) 98 | if errofsendarp := ar.ARPClient.WriteTo(packet, BaordcastHADDR); errofsendarp != nil { 99 | return fmt.Errorf("IPv4AddrAnnouncer.AnnounceAll: %v", errofsendarp) 100 | } 101 | } 102 | return nil 103 | } 104 | 105 | func NewIPv4AddrAnnouncer(nif *net.Interface) *IPv4AddrAnnouncer { 106 | if aar, errofDialARP := arp.Dial(nif); errofDialARP != nil { 107 | panic(errofDialARP) 108 | } else { 109 | logger.GLoger.Printf(logger.DEBUG, "IPv4 addresses announcer created") 110 | return &IPv4AddrAnnouncer{ARPClient: aar} 111 | } 112 | 113 | } 114 | 115 | type IPv4Con struct { 116 | buffer []byte 117 | remote net.IP 118 | local net.IP 119 | SendCon *net.IPConn 120 | ReceiveCon *net.IPConn 121 | } 122 | 123 | type IPv6Con struct { 124 | buffer []byte 125 | oob []byte 126 | remote net.IP 127 | local net.IP 128 | Con *net.IPConn 129 | } 130 | 131 | func ipConnection(local, remote net.IP) (*net.IPConn, error) { 132 | 133 | var conn *net.IPConn 134 | var errOfListenIP error 135 | //redundant 136 | //todo simplify here 137 | if local.IsLinkLocalUnicast() { 138 | var itf, errOfFind = findInterfacebyIP(local) 139 | if errOfFind != nil { 140 | return nil, fmt.Errorf("ipConnection: can't find zone info of %v", local) 141 | } 142 | conn, errOfListenIP = net.ListenIP("ip:112", &net.IPAddr{IP: local, Zone: itf.Name}) 143 | } else { 144 | conn, errOfListenIP = net.ListenIP("ip:112", &net.IPAddr{IP: local}) 145 | } 146 | if errOfListenIP != nil { 147 | return nil, errOfListenIP 148 | } 149 | var fd, errOfGetFD = conn.File() 150 | if errOfGetFD != nil { 151 | return nil, errOfGetFD 152 | } 153 | defer fd.Close() 154 | if remote.To4() != nil { 155 | //IPv4 mode 156 | //set hop limit 157 | if errOfSetHopLimit := syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IP, syscall.IP_MULTICAST_TTL, VRRPMultiTTL); errOfSetHopLimit != nil { 158 | return nil, fmt.Errorf("ipConnection: %v", errOfSetHopLimit) 159 | } 160 | //set tos 161 | if errOfSetTOS := syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IP, syscall.IP_TOS, 7); errOfSetTOS != nil { 162 | return nil, fmt.Errorf("ipConnection: %v", errOfSetTOS) 163 | } 164 | //disable multicast loop 165 | if errOfSetLoop := syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 0); errOfSetLoop != nil { 166 | return nil, fmt.Errorf("ipConnection: %v", errOfSetLoop) 167 | } 168 | } else { 169 | //IPv6 mode 170 | //set hop limit 171 | if errOfSetHOPLimit := syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_HOPS, 255); errOfSetHOPLimit != nil { 172 | return nil, fmt.Errorf("ipConnection: %v", errOfSetHOPLimit) 173 | } 174 | //disable multicast loop 175 | if errOfSetLoop := syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, 0); errOfSetLoop != nil { 176 | return nil, fmt.Errorf("ipConnection: %v", errOfSetLoop) 177 | } 178 | //to receive the hop limit and dst address in oob 179 | if err := syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_2292HOPLIMIT, 1); err != nil { 180 | return nil, fmt.Errorf("ipConnection: %v", err) 181 | } 182 | if err := syscall.SetsockoptInt(int(fd.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_2292PKTINFO, 1); err != nil { 183 | return nil, fmt.Errorf("ipConnection: %v", err) 184 | } 185 | 186 | } 187 | logger.GLoger.Printf(logger.INFO, "IP virtual connection established %v ==> %v", local, remote) 188 | return conn, nil 189 | } 190 | 191 | func makeMulticastIPv4Conn(multi, local net.IP) (*net.IPConn, error) { 192 | var conn, errOfListenIP = net.ListenIP("ip4:112", &net.IPAddr{IP: multi}) 193 | if errOfListenIP != nil { 194 | return nil, fmt.Errorf("makeMulticastIPv4Conn: %v", errOfListenIP) 195 | } 196 | var fd, errOfGetFD = conn.File() 197 | if errOfGetFD != nil { 198 | return nil, fmt.Errorf("makeMulticastIPv4Conn: %v", errOfGetFD) 199 | } 200 | defer fd.Close() 201 | multi = multi.To4() 202 | local = local.To4() 203 | var mreq = &syscall.IPMreq{ 204 | Multiaddr: [4]byte{multi[0], multi[1], multi[2], multi[3]}, 205 | Interface: [4]byte{local[0], local[1], local[2], local[3]}, 206 | } 207 | if errSetMreq := syscall.SetsockoptIPMreq(int(fd.Fd()), syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq); errSetMreq != nil { 208 | return nil, fmt.Errorf("makeMulticastIPv4Conn: %v", errSetMreq) 209 | } 210 | return conn, nil 211 | } 212 | 213 | func joinIPv6MulticastGroup(con *net.IPConn, local, remote net.IP) error { 214 | var fd, errOfGetFD = con.File() 215 | if errOfGetFD != nil { 216 | return fmt.Errorf("joinIPv6MulticastGroup: %v", errOfGetFD) 217 | } 218 | defer fd.Close() 219 | var mreq = &syscall.IPv6Mreq{} 220 | copy(mreq.Multiaddr[:], remote.To16()) 221 | var IF, errOfGetIF = findInterfacebyIP(local) 222 | if errOfGetIF != nil { 223 | return fmt.Errorf("joinIPv6MulticastGroup: %v", errOfGetIF) 224 | } 225 | mreq.Interface = uint32(IF.Index) 226 | if errOfSetMreq := syscall.SetsockoptIPv6Mreq(int(fd.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq); errOfSetMreq != nil { 227 | return fmt.Errorf("joinIPv6MulticastGroup: %v", errOfSetMreq) 228 | } 229 | logger.GLoger.Printf(logger.INFO, "Join IPv6 multicast group %v on %v", remote, IF.Name) 230 | return nil 231 | } 232 | 233 | func NewIPv4Conn(local, remote net.IP) IPConnection { 234 | var SendConn, errOfMakeIPConn = ipConnection(local, remote) 235 | if errOfMakeIPConn != nil { 236 | panic(errOfMakeIPConn) 237 | } 238 | var receiveConn, errOfMakeRecv = makeMulticastIPv4Conn(VRRPMultiAddrIPv4, local) 239 | if errOfMakeRecv != nil { 240 | panic(errOfMakeRecv) 241 | } 242 | return &IPv4Con{ 243 | buffer: make([]byte, 2048), 244 | local: local, 245 | remote: remote, 246 | SendCon: SendConn, 247 | ReceiveCon: receiveConn, 248 | } 249 | 250 | } 251 | 252 | func (conn *IPv4Con) WriteMessage(packet *VRRPPacket) error { 253 | if _, err := conn.SendCon.WriteTo(packet.ToBytes(), &net.IPAddr{IP: conn.remote}); err != nil { 254 | return fmt.Errorf("IPv4Con.WriteMessage: %v", err) 255 | } 256 | return nil 257 | } 258 | 259 | func (conn *IPv4Con) ReadMessage() (*VRRPPacket, error) { 260 | var n, errOfRead = conn.ReceiveCon.Read(conn.buffer) 261 | if errOfRead != nil { 262 | return nil, fmt.Errorf("IPv4Con.ReadMessage: %v", errOfRead) 263 | } 264 | if n < 20 { 265 | return nil, fmt.Errorf("IPv4Con.ReadMessage: IP datagram lenght %v too small", n) 266 | } 267 | var hdrlen = (int(conn.buffer[0]) & 0x0f) << 2 268 | if hdrlen > n { 269 | return nil, fmt.Errorf("IPv4Con.ReadMessage: the header length %v is lagger than total length %V", hdrlen, n) 270 | } 271 | if conn.buffer[8] != 255 { 272 | return nil, fmt.Errorf("IPv4Con.ReadMessage: the TTL of IP datagram carring VRRP advertisment must equal to 255") 273 | } 274 | if advertisement, errOfUnmarshal := FromBytes(IPv4, conn.buffer[hdrlen:n]); errOfUnmarshal != nil { 275 | return nil, fmt.Errorf("IPv4Con.ReadMessage: %v", errOfUnmarshal) 276 | } else { 277 | if VRRPVersion(advertisement.GetVersion()) != VRRPv3 { 278 | return nil, fmt.Errorf("IPv4Con.ReadMessage: received an advertisement with %s", VRRPVersion(advertisement.GetVersion())) 279 | } 280 | var pshdr PseudoHeader 281 | pshdr.Saddr = net.IPv4(conn.buffer[12], conn.buffer[13], conn.buffer[14], conn.buffer[15]).To16() 282 | pshdr.Daddr = net.IPv4(conn.buffer[16], conn.buffer[17], conn.buffer[18], conn.buffer[19]).To16() 283 | pshdr.Protocol = VRRPIPProtocolNumber 284 | pshdr.Len = uint16(n - hdrlen) 285 | if !advertisement.ValidateCheckSum(&pshdr) { 286 | return nil, fmt.Errorf("IPv4Con.ReadMessage: validate the check sum of advertisement failed") 287 | } else { 288 | advertisement.Pshdr = &pshdr 289 | return advertisement, nil 290 | } 291 | } 292 | } 293 | 294 | func NewIPv6Con(local, remote net.IP) *IPv6Con { 295 | var con, errOfNewIPv6Con = ipConnection(local, remote) 296 | if errOfNewIPv6Con != nil { 297 | panic(fmt.Errorf("NewIPv6Con: %v", errOfNewIPv6Con)) 298 | } 299 | if errOfJoinMG := joinIPv6MulticastGroup(con, local, remote); errOfJoinMG != nil { 300 | panic(fmt.Errorf("NewIPv6Con: %v", errOfJoinMG)) 301 | } 302 | return &IPv6Con{ 303 | buffer: make([]byte, 4096), 304 | oob: make([]byte, 4096), 305 | local: local, 306 | remote: remote, 307 | Con: con, 308 | } 309 | } 310 | 311 | func (con *IPv6Con) WriteMessage(packet *VRRPPacket) error { 312 | if _, errOfWrite := con.Con.WriteToIP(packet.ToBytes(), &net.IPAddr{IP: con.remote}); errOfWrite != nil { 313 | return fmt.Errorf("IPv6Con.WriteMessage: %v", errOfWrite) 314 | } 315 | return nil 316 | } 317 | 318 | func (con *IPv6Con) ReadMessage() (*VRRPPacket, error) { 319 | var buffern, oobn, _, raddr, errOfRead = con.Con.ReadMsgIP(con.buffer, con.oob) 320 | if errOfRead != nil { 321 | return nil, fmt.Errorf("IPv6Con.ReadMessage: %v", errOfRead) 322 | } 323 | var oobdata, errOfParseOOB = syscall.ParseSocketControlMessage(con.oob[:oobn]) 324 | if errOfParseOOB != nil { 325 | return nil, fmt.Errorf("IPv6Con.ReadMessage: %v", errOfParseOOB) 326 | } 327 | var ( 328 | dst net.IP 329 | TTL byte 330 | GetTTL = false 331 | ) 332 | for index := range oobdata { 333 | if oobdata[index].Header.Level != syscall.IPPROTO_IPV6 { 334 | continue 335 | } 336 | switch oobdata[index].Header.Type { 337 | case syscall.IPV6_2292HOPLIMIT: 338 | if len(oobdata[index].Data) == 0 { 339 | return nil, fmt.Errorf("IPv6Con.ReadMessage: invalid HOPLIMIT") 340 | } 341 | TTL = oobdata[index].Data[0] 342 | GetTTL = true 343 | case syscall.IPV6_2292PKTINFO: 344 | if len(oobdata[index].Data) < 16 { 345 | return nil, fmt.Errorf("IPv6Con.ReadMessage: invalid destination IP addrress length") 346 | } 347 | dst = net.IP(oobdata[index].Data[:16]) 348 | } 349 | } 350 | if GetTTL == false { 351 | return nil, fmt.Errorf("IPv6Con.ReadMessage: HOPLIMIT not found") 352 | } 353 | if dst == nil { 354 | return nil, fmt.Errorf("IPv6Con.ReadMessage: destination address not found") 355 | } 356 | var pshdr = PseudoHeader{ 357 | Daddr: dst, 358 | Saddr: raddr.IP, 359 | Protocol: VRRPIPProtocolNumber, 360 | Len: uint16(buffern), 361 | } 362 | var advertisement, errOfUnmarshal = FromBytes(IPv6, con.buffer) 363 | if errOfUnmarshal != nil { 364 | return nil, fmt.Errorf("IPv6Con.ReadMessage: %v", errOfUnmarshal) 365 | } 366 | if TTL != 255 { 367 | return nil, fmt.Errorf("IPv6Con.ReadMessage: invalid HOPLIMIT") 368 | } 369 | if VRRPVersion(advertisement.GetVersion()) != VRRPv3 { 370 | return nil, fmt.Errorf("IPv6Con.ReadMessage: invalid VRRP version %v", advertisement.GetVersion()) 371 | } 372 | if !advertisement.ValidateCheckSum(&pshdr) { 373 | return nil, fmt.Errorf("IPv6Con.ReadMessage: invalid check sum") 374 | } 375 | advertisement.Pshdr = &pshdr 376 | return advertisement, nil 377 | } 378 | 379 | func findIPbyInterface(itf *net.Interface, IPvX byte) (net.IP, error) { 380 | var addrs, errOfListAddrs = itf.Addrs() 381 | if errOfListAddrs != nil { 382 | return nil, fmt.Errorf("findIPbyInterface: %v", errOfListAddrs) 383 | } 384 | for index := range addrs { 385 | var ipaddr, _, errOfParseIP = net.ParseCIDR(addrs[index].String()) 386 | if errOfParseIP != nil { 387 | return nil, fmt.Errorf("findIPbyInterface: %v", errOfParseIP) 388 | } 389 | if IPvX == IPv4 { 390 | if ipaddr.To4() != nil { 391 | if ipaddr.IsGlobalUnicast() { 392 | return ipaddr, nil 393 | } 394 | } 395 | } else { 396 | if ipaddr.To4() == nil { 397 | if ipaddr.IsLinkLocalUnicast() { 398 | return ipaddr, nil 399 | } 400 | } 401 | } 402 | } 403 | return nil, fmt.Errorf("findIPbyInterface: can not find valid IP addrs on %v", itf.Name) 404 | } 405 | 406 | func findInterfacebyIP(ip net.IP) (*net.Interface, error) { 407 | if itfs, errOfListInterface := net.Interfaces(); errOfListInterface != nil { 408 | return nil, fmt.Errorf("findInterfacebyIP: %v", errOfListInterface) 409 | } else { 410 | for index := range itfs { 411 | if addrs, errOfListAddrs := itfs[index].Addrs(); errOfListAddrs != nil { 412 | return nil, fmt.Errorf("findInterfacebyIP: %v", errOfListAddrs) 413 | } else { 414 | for index1 := range addrs { 415 | var ipaddr, _, errOfParseIP = net.ParseCIDR(addrs[index1].String()) 416 | if errOfParseIP != nil { 417 | return nil, fmt.Errorf("findInterfacebyIP: %v", errOfParseIP) 418 | } 419 | if ipaddr.Equal(ip) { 420 | return &itfs[index], nil 421 | } 422 | } 423 | } 424 | } 425 | } 426 | 427 | return nil, fmt.Errorf("findInterfacebyIP: can't find the corresponding interface of %v", ip) 428 | } 429 | -------------------------------------------------------------------------------- /vrrp/VRRPPacket.go: -------------------------------------------------------------------------------- 1 | package vrrp 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | "unsafe" 8 | ) 9 | 10 | type VRRPPacket struct { 11 | Header [8]byte 12 | IPAddress [][4]byte 13 | Pshdr *PseudoHeader 14 | } 15 | 16 | type PseudoHeader struct { 17 | Saddr net.IP 18 | Daddr net.IP 19 | Zero uint8 20 | Protocol uint8 21 | Len uint16 22 | } 23 | 24 | func (psh *PseudoHeader) ToBytes() []byte { 25 | var octets = make([]byte, 36) 26 | copy(octets, psh.Saddr) 27 | copy(octets[16:], psh.Daddr) 28 | copy(octets[32:], []byte{psh.Zero, psh.Protocol, byte(psh.Len >> 8), byte(psh.Len)}) 29 | return octets 30 | } 31 | 32 | func FromBytes(IPvXVersion byte, octets []byte) (*VRRPPacket, error) { 33 | if len(octets) < 8 { 34 | return nil, errors.New("faulty VRRP packet size") 35 | } 36 | var packet VRRPPacket 37 | for index := 0; index < 8; index++ { 38 | packet.Header[index] = octets[index] 39 | } 40 | //todo validate the number of IPvX addresses 41 | var countofaddrs = int(packet.GetIPvXAddrCount()) 42 | switch IPvXVersion { 43 | case 4: 44 | case 6: 45 | countofaddrs = countofaddrs * 4 46 | default: 47 | return nil, fmt.Errorf("faulty IPvX version %d", IPvXVersion) 48 | } 49 | //to compatible with VRRP v2 packet, ignore the auth info 50 | if 8+countofaddrs*4 > len(octets) { 51 | return nil, fmt.Errorf("The value of filed IPvXAddrCount doesn't match the length of octets") 52 | } 53 | for index := 0; index < countofaddrs; index++ { 54 | var addr [4]byte 55 | addr[0] = octets[8+4*index] 56 | addr[1] = octets[8+4*index+1] 57 | addr[2] = octets[8+4*index+2] 58 | addr[3] = octets[8+4*index+3] 59 | packet.IPAddress = append(packet.IPAddress, addr) 60 | } 61 | return &packet, nil 62 | } 63 | 64 | func (packet *VRRPPacket) GetIPvXAddr(version byte) (addrs []net.IP) { 65 | switch version { 66 | case 4: 67 | for index := range packet.IPAddress { 68 | addrs = append(addrs, net.IPv4( 69 | packet.IPAddress[index][0], 70 | packet.IPAddress[index][1], 71 | packet.IPAddress[index][2], 72 | packet.IPAddress[index][3])) 73 | } 74 | return addrs 75 | case 6: 76 | for index := 0; index < int(packet.GetIPvXAddrCount()); index++ { 77 | var p = make(net.IP, net.IPv6len) 78 | for i := 0; i < 4; i++ { 79 | copy(p[4*i:], packet.IPAddress[index*4+i][:]) 80 | } 81 | addrs = append(addrs, p) 82 | } 83 | return addrs 84 | default: 85 | return nil 86 | } 87 | } 88 | 89 | func (packet *VRRPPacket) AddIPvXAddr(version byte, ip net.IP) { 90 | switch version { 91 | case 4: 92 | packet.IPAddress = append(packet.IPAddress, [4]byte{ip[12], ip[13], ip[14], ip[15]}) 93 | packet.setIPvXAddrCount(packet.GetIPvXAddrCount() + 1) 94 | //todo byte maybe overflow 95 | case 6: 96 | for index := 0; index < 4; index++ { 97 | packet.IPAddress = append(packet.IPAddress, [4]byte{ip[index*4+0], ip[index*4+1], ip[index*4+2], ip[index*4+3]}) 98 | } 99 | packet.setIPvXAddrCount(packet.GetIPvXAddrCount() + 1) 100 | default: 101 | panic("VRRPPacket.AddIPvXAddr: only support IPv4 and IPv6 address") 102 | } 103 | } 104 | 105 | func (packet *VRRPPacket) GetVersion() byte { 106 | return (packet.Header[0] & 240) >> 4 107 | } 108 | 109 | func (packet *VRRPPacket) SetVersion(Version VRRPVersion) { 110 | packet.Header[0] = (packet.Header[0] & 15) | (byte(Version) << 4) 111 | } 112 | 113 | func (packet *VRRPPacket) GetType() byte { 114 | return packet.Header[0] & 15 115 | } 116 | 117 | func (packet *VRRPPacket) SetType() { 118 | packet.Header[0] = (packet.Header[0] & 240) | 1 119 | } 120 | 121 | func (packet *VRRPPacket) GetVirtualRouterID() byte { 122 | return packet.Header[1] 123 | } 124 | 125 | func (packet *VRRPPacket) SetVirtualRouterID(VirtualRouterID byte) { 126 | packet.Header[1] = VirtualRouterID 127 | } 128 | 129 | func (packet *VRRPPacket) GetPriority() byte { 130 | return packet.Header[2] 131 | } 132 | 133 | func (packet *VRRPPacket) SetPriority(Priority byte) { 134 | packet.Header[2] = Priority 135 | } 136 | 137 | func (packet *VRRPPacket) GetIPvXAddrCount() byte { 138 | return packet.Header[3] 139 | } 140 | 141 | func (packet *VRRPPacket) setIPvXAddrCount(count byte) { 142 | packet.Header[3] = count 143 | } 144 | 145 | func (packet *VRRPPacket) GetAdvertisementInterval() uint16 { 146 | return uint16(packet.Header[4]&15)<<8 | uint16(packet.Header[5]) 147 | } 148 | 149 | func (packet *VRRPPacket) SetAdvertisementInterval(interval uint16) { 150 | packet.Header[4] = (packet.Header[4] & 240) | byte((interval>>8)&15) 151 | packet.Header[5] = byte(interval) 152 | } 153 | 154 | func (packet *VRRPPacket) GetCheckSum() uint16 { 155 | return uint16(packet.Header[6])<<8 | uint16(packet.Header[7]) 156 | } 157 | 158 | func (packet *VRRPPacket) SetCheckSum(pshdr *PseudoHeader) { 159 | var PointerAdd = func(ptr unsafe.Pointer, bytes int) unsafe.Pointer { 160 | return unsafe.Pointer(uintptr(ptr) + uintptr(bytes)) 161 | } 162 | var octets = pshdr.ToBytes() 163 | octets = append(octets, packet.ToBytes()...) 164 | var x = len(octets) 165 | var ptr = unsafe.Pointer(&octets[0]) 166 | var sum uint32 167 | for x > 1 { 168 | sum = sum + uint32(*(*uint16)(ptr)) 169 | ptr = PointerAdd(ptr, 2) 170 | x = x - 2 171 | } 172 | if x > 0 { 173 | sum = sum + uint32(*((*uint8)(ptr))) 174 | } 175 | for (sum >> 16) > 0 { 176 | sum = sum&65535 + sum>>16 177 | } 178 | sum = ^sum 179 | packet.Header[7] = byte(sum >> 8) 180 | packet.Header[6] = byte(sum) 181 | } 182 | 183 | func (packet *VRRPPacket) ValidateCheckSum(pshdr *PseudoHeader) bool { 184 | var PointerAdd = func(ptr unsafe.Pointer, bytes int) unsafe.Pointer { 185 | return unsafe.Pointer(uintptr(ptr) + uintptr(bytes)) 186 | } 187 | var octets = pshdr.ToBytes() 188 | octets = append(octets, packet.ToBytes()...) 189 | var x = len(octets) 190 | var ptr = unsafe.Pointer(&octets[0]) 191 | var sum uint32 192 | for x > 1 { 193 | sum = sum + uint32(*(*uint16)(ptr)) 194 | ptr = PointerAdd(ptr, 2) 195 | x = x - 2 196 | } 197 | if x > 0 { 198 | sum = sum + uint32(*((*uint8)(ptr))) 199 | } 200 | for (sum >> 16) > 0 { 201 | sum = sum&65535 + sum>>16 202 | } 203 | if uint16(sum) == 65535 { 204 | return true 205 | } else { 206 | return false 207 | } 208 | } 209 | 210 | func (packet *VRRPPacket) ToBytes() []byte { 211 | var payload = make([]byte, 8+len(packet.IPAddress)*4) 212 | copy(payload, packet.Header[:]) 213 | for index := range packet.IPAddress { 214 | copy(payload[8+index*4:], packet.IPAddress[index][:]) 215 | } 216 | return payload 217 | } 218 | -------------------------------------------------------------------------------- /vrrp/VirtualRouter.go: -------------------------------------------------------------------------------- 1 | package vrrp 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | "vrrp-go/logger" 8 | ) 9 | 10 | type VirtualRouter struct { 11 | vrID byte 12 | priority byte 13 | advertisementInterval uint16 14 | advertisementIntervalOfMaster uint16 15 | skewTime uint16 16 | masterDownInterval uint16 17 | preempt bool 18 | owner bool 19 | virtualRouterMACAddressIPv4 net.HardwareAddr 20 | virtualRouterMACAddressIPv6 net.HardwareAddr 21 | // 22 | netInterface *net.Interface 23 | ipvX byte 24 | preferredSourceIP net.IP 25 | protectedIPaddrs map[[16]byte]bool 26 | state int 27 | iplayerInterface IPConnection 28 | ipAddrAnnouncer AddrAnnouncer 29 | eventChannel chan EVENT 30 | packetQueue chan *VRRPPacket 31 | advertisementTicker *time.Ticker 32 | masterDownTimer *time.Timer 33 | transitionHandler map[transition]func() 34 | } 35 | 36 | // NewVirtualRouter create a new virtual router with designated parameters 37 | func NewVirtualRouter(VRID byte, nif string, Owner bool, IPvX byte) *VirtualRouter { 38 | if IPvX != IPv4 && IPvX != IPv6 { 39 | logger.GLoger.Printf(logger.FATAL, "NewVirtualRouter: parameter IPvx must be IPv4 or IPv6") 40 | } 41 | var vr = &VirtualRouter{} 42 | vr.vrID = VRID 43 | vr.virtualRouterMACAddressIPv4, _ = net.ParseMAC(fmt.Sprintf("00-00-5E-00-01-%X", VRID)) 44 | vr.virtualRouterMACAddressIPv6, _ = net.ParseMAC(fmt.Sprintf("00-00-5E-00-02-%X", VRID)) 45 | vr.owner = Owner 46 | //default values that defined by RFC 5798 47 | if Owner { 48 | vr.priority = 255 49 | } 50 | vr.state = INIT 51 | vr.preempt = defaultPreempt 52 | vr.SetAdvInterval(defaultAdvertisementInterval) 53 | vr.SetPriorityAndMasterAdvInterval(defaultPriority, defaultAdvertisementInterval) 54 | 55 | //make 56 | vr.protectedIPaddrs = make(map[[16]byte]bool) 57 | vr.eventChannel = make(chan EVENT, EVENTCHANNELSIZE) 58 | vr.packetQueue = make(chan *VRRPPacket, PACKETQUEUESIZE) 59 | vr.transitionHandler = make(map[transition]func()) 60 | 61 | vr.ipvX = IPvX 62 | var NetworkInterface, errOfGetIF = net.InterfaceByName(nif) 63 | if errOfGetIF != nil { 64 | logger.GLoger.Printf(logger.FATAL, "NewVirtualRouter: %v", errOfGetIF) 65 | } 66 | vr.netInterface = NetworkInterface 67 | //find preferred local IP address 68 | if preferred, errOfGetPreferred := findIPbyInterface(NetworkInterface, IPvX); errOfGetPreferred != nil { 69 | logger.GLoger.Printf(logger.FATAL, "NewVirtualRouter: %v", errOfGetPreferred) 70 | } else { 71 | vr.preferredSourceIP = preferred 72 | } 73 | if IPvX == IPv4 { 74 | //set up ARP client 75 | vr.ipAddrAnnouncer = NewIPv4AddrAnnouncer(NetworkInterface) 76 | //set up IPv4 interface 77 | vr.iplayerInterface = NewIPv4Conn(vr.preferredSourceIP, VRRPMultiAddrIPv4) 78 | } else { 79 | //set up ND client 80 | vr.ipAddrAnnouncer = NewIPIPv6AddrAnnouncer(NetworkInterface) 81 | //set up IPv6 interface 82 | vr.iplayerInterface = NewIPv6Con(vr.preferredSourceIP, VRRPMultiAddrIPv6) 83 | } 84 | logger.GLoger.Printf(logger.INFO, "virtual router %v initialized, working on %v", VRID, nif) 85 | return vr 86 | 87 | } 88 | 89 | func (r *VirtualRouter) setPriority(Priority byte) *VirtualRouter { 90 | if r.owner { 91 | return r 92 | } 93 | r.priority = Priority 94 | return r 95 | } 96 | 97 | func (r *VirtualRouter) SetAdvInterval(Interval time.Duration) *VirtualRouter { 98 | if Interval < 10*time.Millisecond { 99 | panic("interval can not less than 10 ms") 100 | } 101 | r.advertisementInterval = uint16(Interval / (10 * time.Millisecond)) 102 | return r 103 | } 104 | 105 | func (r *VirtualRouter) SetPriorityAndMasterAdvInterval(priority byte, interval time.Duration) *VirtualRouter { 106 | r.setPriority(priority) 107 | if interval < 10*time.Millisecond { 108 | panic("interval can not less than 10 ms") 109 | } 110 | r.setMasterAdvInterval(uint16(interval / (10 * time.Millisecond))) 111 | return r 112 | } 113 | 114 | func (r *VirtualRouter) setMasterAdvInterval(Interval uint16) *VirtualRouter { 115 | r.advertisementIntervalOfMaster = Interval 116 | r.skewTime = r.advertisementIntervalOfMaster - uint16(float32(r.advertisementIntervalOfMaster)*float32(r.priority)/256) 117 | r.masterDownInterval = 3*r.advertisementIntervalOfMaster + r.skewTime 118 | //从MasterDownInterval和SkewTime的计算方式来看,同一组VirtualRouter中,Priority越高的Router越快地认为某个Master失效 119 | return r 120 | } 121 | 122 | func (r *VirtualRouter) SetPreemptMode(flag bool) *VirtualRouter { 123 | r.preempt = flag 124 | return r 125 | } 126 | 127 | func (r *VirtualRouter) AddIPvXAddr(ip net.IP) { 128 | var key [16]byte 129 | copy(key[:], ip) 130 | if _, ok := r.protectedIPaddrs[key]; ok { 131 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.AddIPvXAddr: add redundant IP addr %v", ip) 132 | } else { 133 | r.protectedIPaddrs[key] = true 134 | } 135 | } 136 | 137 | func (r *VirtualRouter) RemoveIPvXAddr(ip net.IP) { 138 | var key [16]byte 139 | copy(key[:], ip) 140 | if _, ok := r.protectedIPaddrs[key]; ok { 141 | delete(r.protectedIPaddrs, key) 142 | logger.GLoger.Printf(logger.INFO, "IP %v removed", ip) 143 | } else { 144 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.RemoveIPvXAddr: remove inexistent IP addr %v", ip) 145 | } 146 | } 147 | 148 | func (r *VirtualRouter) sendAdvertMessage() { 149 | for k := range r.protectedIPaddrs { 150 | logger.GLoger.Printf(logger.DEBUG, "send advert message of IP %v", net.IP(k[:])) 151 | } 152 | var x = r.assembleVRRPPacket() 153 | if errOfWrite := r.iplayerInterface.WriteMessage(x); errOfWrite != nil { 154 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.WriteMessage: %v", errOfWrite) 155 | } 156 | } 157 | 158 | // assembleVRRPPacket assemble VRRP advert packet 159 | func (r *VirtualRouter) assembleVRRPPacket() *VRRPPacket { 160 | 161 | var packet VRRPPacket 162 | packet.SetPriority(r.priority) 163 | packet.SetVersion(VRRPv3) 164 | packet.SetVirtualRouterID(r.vrID) 165 | packet.SetAdvertisementInterval(r.advertisementInterval) 166 | packet.SetType() 167 | for k := range r.protectedIPaddrs { 168 | packet.AddIPvXAddr(r.ipvX, net.IP(k[:])) 169 | } 170 | var pshdr PseudoHeader 171 | pshdr.Protocol = VRRPIPProtocolNumber 172 | if r.ipvX == IPv4 { 173 | pshdr.Daddr = VRRPMultiAddrIPv4 174 | } else { 175 | pshdr.Daddr = VRRPMultiAddrIPv6 176 | } 177 | pshdr.Len = uint16(len(packet.ToBytes())) 178 | pshdr.Saddr = r.preferredSourceIP 179 | packet.SetCheckSum(&pshdr) 180 | return &packet 181 | } 182 | 183 | // fetchVRRPPacket read VRRP packet from IP layer then push into Packet queue 184 | func (r *VirtualRouter) fetchVRRPPacket() { 185 | for { 186 | if packet, errofFetch := r.iplayerInterface.ReadMessage(); errofFetch != nil { 187 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.fetchVRRPPacket: %v", errofFetch) 188 | } else { 189 | if r.vrID == packet.GetVirtualRouterID() { 190 | r.packetQueue <- packet 191 | } else { 192 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.fetchVRRPPacket: received a advertisement with different ID: %v", packet.GetVirtualRouterID()) 193 | } 194 | 195 | } 196 | logger.GLoger.Printf(logger.DEBUG, "VirtualRouter.fetchVRRPPacket: received one advertisement") 197 | } 198 | } 199 | 200 | func (r *VirtualRouter) makeAdvertTicker() { 201 | r.advertisementTicker = time.NewTicker(time.Duration(r.advertisementInterval*10) * time.Millisecond) 202 | } 203 | 204 | func (r *VirtualRouter) stopAdvertTicker() { 205 | r.advertisementTicker.Stop() 206 | } 207 | 208 | func (r *VirtualRouter) makeMasterDownTimer() { 209 | if r.masterDownTimer == nil { 210 | r.masterDownTimer = time.NewTimer(time.Duration(r.masterDownInterval*10) * time.Millisecond) 211 | } else { 212 | r.resetMasterDownTimer() 213 | } 214 | } 215 | 216 | func (r *VirtualRouter) stopMasterDownTimer() { 217 | logger.GLoger.Printf(logger.DEBUG, "master down timer stopped") 218 | if !r.masterDownTimer.Stop() { 219 | select { 220 | case <-r.masterDownTimer.C: 221 | default: 222 | } 223 | logger.GLoger.Printf(logger.DEBUG, "master down timer expired before we stop it, drain the channel") 224 | } 225 | } 226 | 227 | func (r *VirtualRouter) resetMasterDownTimer() { 228 | r.stopMasterDownTimer() 229 | r.masterDownTimer.Reset(time.Duration(r.masterDownInterval*10) * time.Millisecond) 230 | } 231 | 232 | func (r *VirtualRouter) resetMasterDownTimerToSkewTime() { 233 | r.stopMasterDownTimer() 234 | r.masterDownTimer.Reset(time.Duration(r.skewTime*10) * time.Millisecond) 235 | } 236 | 237 | func (r *VirtualRouter) Enroll(transition2 transition, handler func()) bool { 238 | if _, ok := r.transitionHandler[transition2]; ok { 239 | logger.GLoger.Printf(logger.INFO, fmt.Sprintf("VirtualRouter.Enroll(): handler of transition [%s] overwrited", transition2)) 240 | r.transitionHandler[transition2] = handler 241 | return true 242 | } 243 | logger.GLoger.Printf(logger.INFO, fmt.Sprintf("VirtualRouter.Enroll(): handler of transition [%s] enrolled", transition2)) 244 | r.transitionHandler[transition2] = handler 245 | return false 246 | } 247 | 248 | func (r *VirtualRouter) transitionDoWork(t transition) { 249 | var work, ok = r.transitionHandler[t] 250 | if ok == false { 251 | //return fmt.Errorf("VirtualRouter.transitionDoWork(): handler of [%s] does not exist", t) 252 | return 253 | } 254 | work() 255 | logger.GLoger.Printf(logger.INFO, fmt.Sprintf("handler of transition [%s] called", t)) 256 | return 257 | } 258 | 259 | // /////////////////////////////////////// 260 | func largerThan(ip1, ip2 net.IP) bool { 261 | if len(ip1) != len(ip2) { 262 | logger.GLoger.Printf(logger.FATAL, "largerThan: two compared IP addresses must have the same length") 263 | } 264 | for index := range ip1 { 265 | if ip1[index] > ip2[index] { 266 | return true 267 | } else if ip1[index] < ip2[index] { 268 | return false 269 | } 270 | } 271 | return false 272 | } 273 | 274 | // eventLoop VRRP event loop to handle various triggered events 275 | func (r *VirtualRouter) eventLoop() { 276 | for { 277 | switch r.state { 278 | case INIT: 279 | select { 280 | case event := <-r.eventChannel: 281 | if event == START { 282 | logger.GLoger.Printf(logger.INFO, "event %v received", event) 283 | if r.priority == 255 || r.owner { 284 | logger.GLoger.Printf(logger.INFO, "enter owner mode") 285 | r.sendAdvertMessage() 286 | if errOfarp := r.ipAddrAnnouncer.AnnounceAll(r); errOfarp != nil { 287 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.EventLoop: %v", errOfarp) 288 | } 289 | //set up advertisement timer 290 | r.makeAdvertTicker() 291 | logger.GLoger.Printf(logger.DEBUG, "enter MASTER state") 292 | r.state = MASTER 293 | r.transitionDoWork(Init2Master) 294 | } else { 295 | logger.GLoger.Printf(logger.INFO, "VR is not the owner of protected IP addresses") 296 | r.setMasterAdvInterval(r.advertisementInterval) 297 | //set up master down timer 298 | r.makeMasterDownTimer() 299 | logger.GLoger.Printf(logger.DEBUG, "enter BACKUP state") 300 | r.state = BACKUP 301 | r.transitionDoWork(Init2Backup) 302 | } 303 | } 304 | } 305 | case MASTER: 306 | //check if shutdown event received 307 | select { 308 | case event := <-r.eventChannel: 309 | if event == SHUTDOWN { 310 | //close advert timer 311 | r.stopAdvertTicker() 312 | //send advertisement with priority 0 313 | var priority = r.priority 314 | r.setPriority(0) 315 | r.sendAdvertMessage() 316 | r.setPriority(priority) 317 | //transition into INIT 318 | r.state = INIT 319 | r.transitionDoWork(Master2Init) 320 | logger.GLoger.Printf(logger.INFO, "event %v received", event) 321 | //maybe we can break out the event loop 322 | } 323 | case <-r.advertisementTicker.C: //check if advertisement timer fired 324 | r.sendAdvertMessage() 325 | default: 326 | //nothing to do, just break 327 | } 328 | //process incoming advertisement 329 | select { 330 | case packet := <-r.packetQueue: 331 | if packet.GetPriority() == 0 { 332 | //I don't think we should anything here 333 | } else { 334 | if packet.GetPriority() > r.priority || (packet.GetPriority() == r.priority && largerThan(packet.Pshdr.Saddr, r.preferredSourceIP)) { 335 | 336 | //cancel Advertisement timer 337 | r.stopAdvertTicker() 338 | //set up master down timer 339 | r.setMasterAdvInterval(packet.GetAdvertisementInterval()) 340 | r.makeMasterDownTimer() 341 | r.state = BACKUP 342 | r.transitionDoWork(Master2Backup) 343 | } else { 344 | //just discard this one 345 | } 346 | } 347 | default: 348 | //nothing to do 349 | } 350 | case BACKUP: 351 | select { 352 | case event := <-r.eventChannel: 353 | if event == SHUTDOWN { 354 | //close master down timer 355 | r.stopMasterDownTimer() 356 | //transition into INIT 357 | r.state = INIT 358 | r.transitionDoWork(Backup2Init) 359 | logger.GLoger.Printf(logger.INFO, "event %s received", event) 360 | } 361 | default: 362 | } 363 | //process incoming advertisement 364 | select { 365 | case packet := <-r.packetQueue: 366 | if packet.GetPriority() == 0 { 367 | logger.GLoger.Printf(logger.INFO, "received an advertisement with priority 0, transit into MASTER state", r.vrID) 368 | //Set the Master_Down_Timer to Skew_Time 369 | r.resetMasterDownTimerToSkewTime() 370 | } else { 371 | if r.preempt == false || packet.GetPriority() > r.priority || (packet.GetPriority() == r.priority && largerThan(packet.Pshdr.Saddr, r.preferredSourceIP)) { 372 | //reset master down timer 373 | r.setMasterAdvInterval(packet.GetAdvertisementInterval()) 374 | r.resetMasterDownTimer() 375 | } else { 376 | //nothing to do, just discard this one 377 | } 378 | } 379 | default: 380 | //nothing to do 381 | } 382 | select { 383 | //Master_Down_Timer fired 384 | case <-r.masterDownTimer.C: 385 | // Send an ADVERTISEMENT 386 | r.sendAdvertMessage() 387 | if errOfARP := r.ipAddrAnnouncer.AnnounceAll(r); errOfARP != nil { 388 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.EventLoop: %v", errOfARP) 389 | } 390 | //Set the Advertisement Timer to Advertisement interval 391 | r.makeAdvertTicker() 392 | 393 | r.state = MASTER 394 | r.transitionDoWork(Backup2Master) 395 | default: 396 | //nothing to do 397 | } 398 | 399 | } 400 | } 401 | } 402 | 403 | // eventSelector VRRP event selector to handle various triggered events 404 | func (r *VirtualRouter) eventSelector() { 405 | for { 406 | switch r.state { 407 | case INIT: 408 | select { 409 | case event := <-r.eventChannel: 410 | if event == START { 411 | logger.GLoger.Printf(logger.INFO, "event %v received", event) 412 | if r.priority == 255 || r.owner { 413 | logger.GLoger.Printf(logger.INFO, "enter owner mode") 414 | r.sendAdvertMessage() 415 | if errOfarp := r.ipAddrAnnouncer.AnnounceAll(r); errOfarp != nil { 416 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.EventLoop: %v", errOfarp) 417 | } 418 | //set up advertisement timer 419 | r.makeAdvertTicker() 420 | 421 | logger.GLoger.Printf(logger.DEBUG, "enter MASTER state") 422 | r.state = MASTER 423 | r.transitionDoWork(Init2Master) 424 | } else { 425 | logger.GLoger.Printf(logger.INFO, "VR is not the owner of protected IP addresses") 426 | r.setMasterAdvInterval(r.advertisementInterval) 427 | //set up master down timer 428 | r.makeMasterDownTimer() 429 | logger.GLoger.Printf(logger.DEBUG, "enter BACKUP state") 430 | r.state = BACKUP 431 | r.transitionDoWork(Init2Backup) 432 | } 433 | } 434 | } 435 | case MASTER: 436 | //check if shutdown event received 437 | select { 438 | case event := <-r.eventChannel: 439 | if event == SHUTDOWN { 440 | //close advert timer 441 | r.stopAdvertTicker() 442 | //send advertisement with priority 0 443 | var priority = r.priority 444 | r.setPriority(0) 445 | r.sendAdvertMessage() 446 | r.setPriority(priority) 447 | //transition into INIT 448 | r.state = INIT 449 | r.transitionDoWork(Master2Init) 450 | logger.GLoger.Printf(logger.INFO, "event %v received", event) 451 | //maybe we can break out the event loop 452 | } 453 | case <-r.advertisementTicker.C: //check if advertisement timer fired 454 | r.sendAdvertMessage() 455 | case packet := <-r.packetQueue: //process incoming advertisement 456 | if packet.GetPriority() == 0 { 457 | //I don't think we should anything here 458 | } else { 459 | if packet.GetPriority() > r.priority || (packet.GetPriority() == r.priority && largerThan(packet.Pshdr.Saddr, r.preferredSourceIP)) { 460 | 461 | //cancel Advertisement timer 462 | r.stopAdvertTicker() 463 | //set up master down timer 464 | r.setMasterAdvInterval(packet.GetAdvertisementInterval()) 465 | r.makeMasterDownTimer() 466 | r.state = BACKUP 467 | r.transitionDoWork(Master2Backup) 468 | } else { 469 | //just discard this one 470 | } 471 | } 472 | } 473 | 474 | case BACKUP: 475 | select { 476 | case event := <-r.eventChannel: 477 | if event == SHUTDOWN { 478 | //close master down timer 479 | r.stopMasterDownTimer() 480 | //transition into INIT 481 | r.state = INIT 482 | r.transitionDoWork(Backup2Init) 483 | logger.GLoger.Printf(logger.INFO, "event %s received", event) 484 | } 485 | case packet := <-r.packetQueue: //process incoming advertisement 486 | if packet.GetPriority() == 0 { 487 | logger.GLoger.Printf(logger.INFO, "received an advertisement with priority 0, transit into MASTER state", r.vrID) 488 | //Set the Master_Down_Timer to Skew_Time 489 | r.resetMasterDownTimerToSkewTime() 490 | } else { 491 | if r.preempt == false || packet.GetPriority() > r.priority || (packet.GetPriority() == r.priority && largerThan(packet.Pshdr.Saddr, r.preferredSourceIP)) { 492 | //reset master down timer 493 | r.setMasterAdvInterval(packet.GetAdvertisementInterval()) 494 | r.resetMasterDownTimer() 495 | } else { 496 | //nothing to do, just discard this one 497 | } 498 | } 499 | case <-r.masterDownTimer.C: //Master_Down_Timer fired 500 | // Send an ADVERTISEMENT 501 | r.sendAdvertMessage() 502 | if errOfARP := r.ipAddrAnnouncer.AnnounceAll(r); errOfARP != nil { 503 | logger.GLoger.Printf(logger.ERROR, "VirtualRouter.EventLoop: %v", errOfARP) 504 | } 505 | //Set the Advertisement Timer to Advertisement interval 506 | r.makeAdvertTicker() 507 | r.state = MASTER 508 | r.transitionDoWork(Backup2Master) 509 | } 510 | 511 | } 512 | } 513 | } 514 | 515 | func (vr *VirtualRouter) StartWithEventLoop() { 516 | go vr.fetchVRRPPacket() 517 | go func() { 518 | vr.eventChannel <- START 519 | }() 520 | vr.eventLoop() 521 | } 522 | 523 | func (vr *VirtualRouter) StartWithEventSelector() { 524 | go vr.fetchVRRPPacket() 525 | go func() { 526 | vr.eventChannel <- START 527 | }() 528 | vr.eventSelector() 529 | } 530 | 531 | func (vr *VirtualRouter) Stop() { 532 | vr.eventChannel <- SHUTDOWN 533 | } 534 | -------------------------------------------------------------------------------- /vrrp/constants.go: -------------------------------------------------------------------------------- 1 | package vrrp 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | type VRRPVersion byte 9 | 10 | const ( 11 | VRRPv1 VRRPVersion = 1 12 | VRRPv2 VRRPVersion = 2 13 | VRRPv3 VRRPVersion = 3 14 | ) 15 | 16 | func (v VRRPVersion) String() string { 17 | switch v { 18 | case VRRPv1: 19 | return "VRRPVersion1" 20 | case VRRPv2: 21 | return "VRRPVersion2" 22 | case VRRPv3: 23 | return "VRRPVersion3" 24 | default: 25 | return "unknown VRRP version" 26 | } 27 | } 28 | 29 | const ( 30 | IPv4 = 4 31 | IPv6 = 6 32 | ) 33 | 34 | const ( 35 | INIT = iota 36 | MASTER 37 | BACKUP 38 | ) 39 | 40 | const ( 41 | VRRPMultiTTL = 255 42 | VRRPIPProtocolNumber = 112 43 | ) 44 | 45 | var VRRPMultiAddrIPv4 = net.IPv4(224, 0, 0, 18) 46 | var VRRPMultiAddrIPv6 = net.ParseIP("FF02:0:0:0:0:0:0:12") 47 | 48 | var BaordcastHADDR, _ = net.ParseMAC("ff:ff:ff:ff:ff:ff") 49 | 50 | type EVENT byte 51 | 52 | const ( 53 | SHUTDOWN EVENT = iota 54 | START 55 | ) 56 | 57 | func (e EVENT) String() string { 58 | switch e { 59 | case START: 60 | return "START" 61 | case SHUTDOWN: 62 | return "SHUTDOWN" 63 | default: 64 | return "unknown event" 65 | } 66 | } 67 | 68 | const PACKETQUEUESIZE = 1000 69 | const EVENTCHANNELSIZE = 1 70 | 71 | type transition int 72 | 73 | func (t transition) String() string { 74 | switch t { 75 | case Master2Backup: 76 | return "master to backup" 77 | case Backup2Master: 78 | return "backup to master" 79 | case Init2Master: 80 | return "init to master" 81 | case Init2Backup: 82 | return "init to backup" 83 | case Backup2Init: 84 | return "backup to init" 85 | case Master2Init: 86 | return "master to init" 87 | default: 88 | return "unknown transition" 89 | } 90 | } 91 | 92 | const ( 93 | Master2Backup transition = iota 94 | Backup2Master 95 | Init2Master 96 | Init2Backup 97 | Master2Init 98 | Backup2Init 99 | ) 100 | 101 | var ( 102 | defaultPreempt = true 103 | defaultPriority byte = 100 104 | defaultAdvertisementInterval = 1 * time.Second 105 | ) 106 | --------------------------------------------------------------------------------