summaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/zebra
diff options
context:
space:
mode:
authorFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 13:48:38 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 20:44:25 +0900
commitc4775c42510d1f1ddd55036dc19e982712fa6a0b (patch)
tree6ec8b61d4338c809e239e3003a2d32d480898e22 /internal/pkg/zebra
parentb3079759aa13172fcb548a83da9a9653d8d5fed4 (diff)
follow Standard Go Project Layout
https://github.com/golang-standards/project-layout Now you can see clearly what are private and public library code. Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'internal/pkg/zebra')
-rw-r--r--internal/pkg/zebra/afi_string.go17
-rw-r--r--internal/pkg/zebra/api_type_string.go16
-rw-r--r--internal/pkg/zebra/link_type_string.go16
-rw-r--r--internal/pkg/zebra/nexthop_flag_string.go17
-rw-r--r--internal/pkg/zebra/ptm_enable_string.go16
-rw-r--r--internal/pkg/zebra/ptm_status_string.go16
-rw-r--r--internal/pkg/zebra/route_type_string.go16
-rw-r--r--internal/pkg/zebra/safi_string.go17
-rw-r--r--internal/pkg/zebra/zapi.go1917
-rw-r--r--internal/pkg/zebra/zapi_bsd.go58
-rw-r--r--internal/pkg/zebra/zapi_darwin.go59
-rw-r--r--internal/pkg/zebra/zapi_linux.go83
-rw-r--r--internal/pkg/zebra/zapi_test.go531
-rw-r--r--internal/pkg/zebra/zapi_windows.go38
14 files changed, 2817 insertions, 0 deletions
diff --git a/internal/pkg/zebra/afi_string.go b/internal/pkg/zebra/afi_string.go
new file mode 100644
index 00000000..6c07a09d
--- /dev/null
+++ b/internal/pkg/zebra/afi_string.go
@@ -0,0 +1,17 @@
+// Code generated by "stringer -type=AFI"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _AFI_name = "AFI_IPAFI_IP6AFI_ETHERAFI_MAX"
+
+var _AFI_index = [...]uint8{0, 6, 13, 22, 29}
+
+func (i AFI) String() string {
+ i -= 1
+ if i >= AFI(len(_AFI_index)-1) {
+ return fmt.Sprintf("AFI(%d)", i+1)
+ }
+ return _AFI_name[_AFI_index[i]:_AFI_index[i+1]]
+}
diff --git a/internal/pkg/zebra/api_type_string.go b/internal/pkg/zebra/api_type_string.go
new file mode 100644
index 00000000..e97059b1
--- /dev/null
+++ b/internal/pkg/zebra/api_type_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=API_TYPE"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _API_TYPE_name = "FRR_INTERFACE_ADDINTERFACE_ADDINTERFACE_DELETEINTERFACE_ADDRESS_ADDINTERFACE_ADDRESS_DELETEINTERFACE_UPINTERFACE_DOWNIPV4_ROUTE_ADDIPV4_ROUTE_DELETEIPV6_ROUTE_ADDIPV6_ROUTE_DELETEREDISTRIBUTE_ADDREDISTRIBUTE_DELETEREDISTRIBUTE_DEFAULT_ADDREDISTRIBUTE_DEFAULT_DELETEIPV4_NEXTHOP_LOOKUPIPV6_NEXTHOP_LOOKUPIPV4_IMPORT_LOOKUPIPV6_IMPORT_LOOKUPINTERFACE_RENAMEROUTER_ID_ADDROUTER_ID_DELETEROUTER_ID_UPDATEHELLOIPV4_NEXTHOP_LOOKUP_MRIBVRF_UNREGISTERINTERFACE_LINK_PARAMSNEXTHOP_REGISTERNEXTHOP_UNREGISTERNEXTHOP_UPDATEMESSAGE_MAXFRR_BFD_DEST_REPLAYFRR_REDISTRIBUTE_IPV4_ADDFRR_REDISTRIBUTE_IPV4_DELFRR_REDISTRIBUTE_IPV6_ADDFRR_REDISTRIBUTE_IPV6_DELFRR_VRF_UNREGISTERFRR_VRF_ADDFRR_VRF_DELETEFRR_INTERFACE_VRF_UPDATEFRR_BFD_CLIENT_REGISTERFRR_INTERFACE_ENABLE_RADVFRR_INTERFACE_DISABLE_RADVFRR_IPV4_NEXTHOP_LOOKUP_MRIBFRR_INTERFACE_LINK_PARAMSFRR_MPLS_LABELS_ADDFRR_MPLS_LABELS_DELETEFRR_IPV4_NEXTHOP_ADDFRR_IPV4_NEXTHOP_DELETEFRR_IPV6_NEXTHOP_ADDFRR_IPV6_NEXTHOP_DELETEFRR_IPMR_ROUTE_STATSFRR_LABEL_MANAGER_CONNECTFRR_GET_LABEL_CHUNKFRR_RELEASE_LABEL_CHUNKFRR_PW_ADDFRR_PW_DELETEFRR_PW_SETFRR_PW_UNSETFRR_PW_STATUS_UPDATE"
+
+var _API_TYPE_index = [...]uint16{0, 17, 30, 46, 67, 91, 103, 117, 131, 148, 162, 179, 195, 214, 238, 265, 284, 303, 321, 339, 355, 368, 384, 400, 405, 429, 443, 464, 480, 498, 512, 523, 542, 567, 592, 617, 642, 660, 671, 685, 709, 732, 757, 783, 811, 836, 855, 877, 897, 920, 940, 963, 983, 1008, 1027, 1050, 1060, 1073, 1083, 1095, 1115}
+
+func (i API_TYPE) String() string {
+ if i >= API_TYPE(len(_API_TYPE_index)-1) {
+ return fmt.Sprintf("API_TYPE(%d)", i)
+ }
+ return _API_TYPE_name[_API_TYPE_index[i]:_API_TYPE_index[i+1]]
+}
diff --git a/internal/pkg/zebra/link_type_string.go b/internal/pkg/zebra/link_type_string.go
new file mode 100644
index 00000000..9db8544b
--- /dev/null
+++ b/internal/pkg/zebra/link_type_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=LINK_TYPE"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _LINK_TYPE_name = "LINK_TYPE_UNKNOWNLINK_TYPE_ETHERLINK_TYPE_EETHERLINK_TYPE_AX25LINK_TYPE_PRONETLINK_TYPE_IEEE802LINK_TYPE_ARCNETLINK_TYPE_APPLETLKLINK_TYPE_DLCILINK_TYPE_ATMLINK_TYPE_METRICOMLINK_TYPE_IEEE1394LINK_TYPE_EUI64LINK_TYPE_INFINIBANDLINK_TYPE_SLIPLINK_TYPE_CSLIPLINK_TYPE_SLIP6LINK_TYPE_CSLIP6LINK_TYPE_RSRVDLINK_TYPE_ADAPTLINK_TYPE_ROSELINK_TYPE_X25LINK_TYPE_PPPLINK_TYPE_CHDLCLINK_TYPE_LAPBLINK_TYPE_RAWHDLCLINK_TYPE_IPIPLINK_TYPE_IPIP6LINK_TYPE_FRADLINK_TYPE_SKIPLINK_TYPE_LOOPBACKLINK_TYPE_LOCALTLKLINK_TYPE_FDDILINK_TYPE_SITLINK_TYPE_IPDDPLINK_TYPE_IPGRELINK_TYPE_IP6GRELINK_TYPE_PIMREGLINK_TYPE_HIPPILINK_TYPE_ECONETLINK_TYPE_IRDALINK_TYPE_FCPPLINK_TYPE_FCALLINK_TYPE_FCPLLINK_TYPE_FCFABRICLINK_TYPE_IEEE802_TRLINK_TYPE_IEEE80211LINK_TYPE_IEEE80211_RADIOTAPLINK_TYPE_IEEE802154LINK_TYPE_IEEE802154_PHY"
+
+var _LINK_TYPE_index = [...]uint16{0, 17, 32, 48, 62, 78, 95, 111, 129, 143, 156, 174, 192, 207, 227, 241, 256, 271, 287, 302, 317, 331, 344, 357, 372, 386, 403, 417, 432, 446, 460, 478, 496, 510, 523, 538, 553, 569, 585, 600, 616, 630, 644, 658, 672, 690, 710, 729, 757, 777, 801}
+
+func (i LINK_TYPE) String() string {
+ if i >= LINK_TYPE(len(_LINK_TYPE_index)-1) {
+ return fmt.Sprintf("LINK_TYPE(%d)", i)
+ }
+ return _LINK_TYPE_name[_LINK_TYPE_index[i]:_LINK_TYPE_index[i+1]]
+}
diff --git a/internal/pkg/zebra/nexthop_flag_string.go b/internal/pkg/zebra/nexthop_flag_string.go
new file mode 100644
index 00000000..38f08b8a
--- /dev/null
+++ b/internal/pkg/zebra/nexthop_flag_string.go
@@ -0,0 +1,17 @@
+// Code generated by "stringer -type=NEXTHOP_FLAG"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _NEXTHOP_FLAG_name = "NEXTHOP_IFINDEXNEXTHOP_IFNAMENEXTHOP_IPV4NEXTHOP_IPV4_IFINDEXNEXTHOP_IPV4_IFNAMENEXTHOP_IPV6NEXTHOP_IPV6_IFINDEXNEXTHOP_IPV6_IFNAMENEXTHOP_BLACKHOLE"
+
+var _NEXTHOP_FLAG_index = [...]uint8{0, 15, 29, 41, 61, 80, 92, 112, 131, 148}
+
+func (i NEXTHOP_FLAG) String() string {
+ i -= 1
+ if i >= NEXTHOP_FLAG(len(_NEXTHOP_FLAG_index)-1) {
+ return fmt.Sprintf("NEXTHOP_FLAG(%d)", i+1)
+ }
+ return _NEXTHOP_FLAG_name[_NEXTHOP_FLAG_index[i]:_NEXTHOP_FLAG_index[i+1]]
+}
diff --git a/internal/pkg/zebra/ptm_enable_string.go b/internal/pkg/zebra/ptm_enable_string.go
new file mode 100644
index 00000000..d750542e
--- /dev/null
+++ b/internal/pkg/zebra/ptm_enable_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=PTM_ENABLE"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _PTM_ENABLE_name = "PTM_ENABLE_OFFPTM_ENABLE_ONPTM_ENABLE_UNSPEC"
+
+var _PTM_ENABLE_index = [...]uint8{0, 14, 27, 44}
+
+func (i PTM_ENABLE) String() string {
+ if i >= PTM_ENABLE(len(_PTM_ENABLE_index)-1) {
+ return fmt.Sprintf("PTM_ENABLE(%d)", i)
+ }
+ return _PTM_ENABLE_name[_PTM_ENABLE_index[i]:_PTM_ENABLE_index[i+1]]
+}
diff --git a/internal/pkg/zebra/ptm_status_string.go b/internal/pkg/zebra/ptm_status_string.go
new file mode 100644
index 00000000..464233b7
--- /dev/null
+++ b/internal/pkg/zebra/ptm_status_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=PTM_STATUS"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _PTM_STATUS_name = "PTM_STATUS_DOWNPTM_STATUS_UPPTM_STATUS_UNKNOWN"
+
+var _PTM_STATUS_index = [...]uint8{0, 15, 28, 46}
+
+func (i PTM_STATUS) String() string {
+ if i >= PTM_STATUS(len(_PTM_STATUS_index)-1) {
+ return fmt.Sprintf("PTM_STATUS(%d)", i)
+ }
+ return _PTM_STATUS_name[_PTM_STATUS_index[i]:_PTM_STATUS_index[i+1]]
+}
diff --git a/internal/pkg/zebra/route_type_string.go b/internal/pkg/zebra/route_type_string.go
new file mode 100644
index 00000000..e2ad6c97
--- /dev/null
+++ b/internal/pkg/zebra/route_type_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=ROUTE_TYPE"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _ROUTE_TYPE_name = "ROUTE_SYSTEMROUTE_KERNELROUTE_CONNECTROUTE_STATICROUTE_RIPROUTE_RIPNGROUTE_OSPFROUTE_OSPF6ROUTE_ISISROUTE_BGPROUTE_PIMROUTE_HSLSROUTE_OLSRROUTE_BABELROUTE_MAXFRR_ROUTE_VNCFRR_ROUTE_VNC_DIRECTFRR_ROUTE_VNC_DIRECT_RHFRR_ROUTE_BGP_DIRECTFRR_ROUTE_BGP_DIRECT_EXTFRR_ROUTE_ALLFRR_ROUTE_MAX"
+
+var _ROUTE_TYPE_index = [...]uint16{0, 12, 24, 37, 49, 58, 69, 79, 90, 100, 109, 118, 128, 138, 149, 158, 171, 191, 214, 234, 258, 271, 284}
+
+func (i ROUTE_TYPE) String() string {
+ if i >= ROUTE_TYPE(len(_ROUTE_TYPE_index)-1) {
+ return fmt.Sprintf("ROUTE_TYPE(%d)", i)
+ }
+ return _ROUTE_TYPE_name[_ROUTE_TYPE_index[i]:_ROUTE_TYPE_index[i+1]]
+}
diff --git a/internal/pkg/zebra/safi_string.go b/internal/pkg/zebra/safi_string.go
new file mode 100644
index 00000000..ab491cb6
--- /dev/null
+++ b/internal/pkg/zebra/safi_string.go
@@ -0,0 +1,17 @@
+// Code generated by "stringer -type=SAFI"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _SAFI_name = "SAFI_UNICASTSAFI_MULTICASTSAFI_RESERVED_3SAFI_MPLS_VPNSAFI_MAX"
+
+var _SAFI_index = [...]uint8{0, 12, 26, 41, 54, 62}
+
+func (i SAFI) String() string {
+ i -= 1
+ if i >= SAFI(len(_SAFI_index)-1) {
+ return fmt.Sprintf("SAFI(%d)", i+1)
+ }
+ return _SAFI_name[_SAFI_index[i]:_SAFI_index[i+1]]
+}
diff --git a/internal/pkg/zebra/zapi.go b/internal/pkg/zebra/zapi.go
new file mode 100644
index 00000000..eb443d05
--- /dev/null
+++ b/internal/pkg/zebra/zapi.go
@@ -0,0 +1,1917 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+ "syscall"
+
+ log "github.com/sirupsen/logrus"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+)
+
+const (
+ HEADER_MARKER = 255
+ FRR_HEADER_MARKER = 254
+ INTERFACE_NAMSIZ = 20
+)
+
+// Internal Interface Status.
+type INTERFACE_STATUS uint8
+
+const (
+ INTERFACE_ACTIVE INTERFACE_STATUS = 0x01
+ INTERFACE_SUB INTERFACE_STATUS = 0x02
+ INTERFACE_LINKDETECTION INTERFACE_STATUS = 0x04
+ INTERFACE_VRF_LOOPBACK INTERFACE_STATUS = 0x08
+)
+
+// Interface Link Layer Types.
+//go:generate stringer -type=LINK_TYPE
+type LINK_TYPE uint32
+
+const (
+ LINK_TYPE_UNKNOWN LINK_TYPE = iota
+ LINK_TYPE_ETHER
+ LINK_TYPE_EETHER
+ LINK_TYPE_AX25
+ LINK_TYPE_PRONET
+ LINK_TYPE_IEEE802
+ LINK_TYPE_ARCNET
+ LINK_TYPE_APPLETLK
+ LINK_TYPE_DLCI
+ LINK_TYPE_ATM
+ LINK_TYPE_METRICOM
+ LINK_TYPE_IEEE1394
+ LINK_TYPE_EUI64
+ LINK_TYPE_INFINIBAND
+ LINK_TYPE_SLIP
+ LINK_TYPE_CSLIP
+ LINK_TYPE_SLIP6
+ LINK_TYPE_CSLIP6
+ LINK_TYPE_RSRVD
+ LINK_TYPE_ADAPT
+ LINK_TYPE_ROSE
+ LINK_TYPE_X25
+ LINK_TYPE_PPP
+ LINK_TYPE_CHDLC
+ LINK_TYPE_LAPB
+ LINK_TYPE_RAWHDLC
+ LINK_TYPE_IPIP
+ LINK_TYPE_IPIP6
+ LINK_TYPE_FRAD
+ LINK_TYPE_SKIP
+ LINK_TYPE_LOOPBACK
+ LINK_TYPE_LOCALTLK
+ LINK_TYPE_FDDI
+ LINK_TYPE_SIT
+ LINK_TYPE_IPDDP
+ LINK_TYPE_IPGRE
+ LINK_TYPE_IP6GRE
+ LINK_TYPE_PIMREG
+ LINK_TYPE_HIPPI
+ LINK_TYPE_ECONET
+ LINK_TYPE_IRDA
+ LINK_TYPE_FCPP
+ LINK_TYPE_FCAL
+ LINK_TYPE_FCPL
+ LINK_TYPE_FCFABRIC
+ LINK_TYPE_IEEE802_TR
+ LINK_TYPE_IEEE80211
+ LINK_TYPE_IEEE80211_RADIOTAP
+ LINK_TYPE_IEEE802154
+ LINK_TYPE_IEEE802154_PHY
+)
+
+const VRF_DEFAULT = 0
+
+func HeaderSize(version uint8) uint16 {
+ switch version {
+ case 3, 4:
+ return 8
+ default:
+ return 6
+ }
+}
+
+func (t INTERFACE_STATUS) String() string {
+ ss := make([]string, 0, 3)
+ if t&INTERFACE_ACTIVE > 0 {
+ ss = append(ss, "ACTIVE")
+ }
+ if t&INTERFACE_SUB > 0 {
+ ss = append(ss, "SUB")
+ }
+ if t&INTERFACE_LINKDETECTION > 0 {
+ ss = append(ss, "LINKDETECTION")
+ }
+ if t&INTERFACE_VRF_LOOPBACK > 0 {
+ ss = append(ss, "VRF_LOOPBACK")
+ }
+ return strings.Join(ss, "|")
+}
+
+// Interface Connected Address Flags
+type INTERFACE_ADDRESS_FLAG uint8
+
+const (
+ INTERFACE_ADDRESS_SECONDARY INTERFACE_ADDRESS_FLAG = 0x01
+ INTERFACE_ADDRESS_PEER INTERFACE_ADDRESS_FLAG = 0x02
+ INTERFACE_ADDRESS_UNNUMBERED INTERFACE_ADDRESS_FLAG = 0x04
+)
+
+func (t INTERFACE_ADDRESS_FLAG) String() string {
+ ss := make([]string, 0, 3)
+ if t&INTERFACE_ADDRESS_SECONDARY > 0 {
+ ss = append(ss, "SECONDARY")
+ }
+ if t&INTERFACE_ADDRESS_PEER > 0 {
+ ss = append(ss, "PEER")
+ }
+ if t&INTERFACE_ADDRESS_UNNUMBERED > 0 {
+ ss = append(ss, "UNNUMBERED")
+ }
+ return strings.Join(ss, "|")
+}
+
+// Address Family Identifier.
+//go:generate stringer -type=AFI
+type AFI uint8
+
+const (
+ AFI_IP AFI = 1
+ AFI_IP6 AFI = 2
+ AFI_ETHER AFI = 3
+ AFI_MAX AFI = 4
+)
+
+// Subsequent Address Family Identifier.
+//go:generate stringer -type=SAFI
+type SAFI uint8
+
+const (
+ _ SAFI = iota
+ SAFI_UNICAST
+ SAFI_MULTICAST
+ SAFI_RESERVED_3
+ SAFI_MPLS_VPN
+ SAFI_MAX
+)
+
+// API Types.
+//go:generate stringer -type=API_TYPE
+type API_TYPE uint16
+
+// For Quagga.
+const (
+ _ API_TYPE = iota
+ INTERFACE_ADD
+ INTERFACE_DELETE
+ INTERFACE_ADDRESS_ADD
+ INTERFACE_ADDRESS_DELETE
+ INTERFACE_UP
+ INTERFACE_DOWN
+ IPV4_ROUTE_ADD
+ IPV4_ROUTE_DELETE
+ IPV6_ROUTE_ADD
+ IPV6_ROUTE_DELETE
+ REDISTRIBUTE_ADD
+ REDISTRIBUTE_DELETE
+ REDISTRIBUTE_DEFAULT_ADD
+ REDISTRIBUTE_DEFAULT_DELETE
+ IPV4_NEXTHOP_LOOKUP
+ IPV6_NEXTHOP_LOOKUP
+ IPV4_IMPORT_LOOKUP
+ IPV6_IMPORT_LOOKUP
+ INTERFACE_RENAME
+ ROUTER_ID_ADD
+ ROUTER_ID_DELETE
+ ROUTER_ID_UPDATE
+ HELLO
+ IPV4_NEXTHOP_LOOKUP_MRIB
+ VRF_UNREGISTER
+ INTERFACE_LINK_PARAMS
+ NEXTHOP_REGISTER
+ NEXTHOP_UNREGISTER
+ NEXTHOP_UPDATE
+ MESSAGE_MAX
+)
+
+// For FRRouting.
+const (
+ FRR_INTERFACE_ADD API_TYPE = iota
+ FRR_INTERFACE_DELETE
+ FRR_INTERFACE_ADDRESS_ADD
+ FRR_INTERFACE_ADDRESS_DELETE
+ FRR_INTERFACE_UP
+ FRR_INTERFACE_DOWN
+ FRR_IPV4_ROUTE_ADD
+ FRR_IPV4_ROUTE_DELETE
+ FRR_IPV6_ROUTE_ADD
+ FRR_IPV6_ROUTE_DELETE
+ FRR_REDISTRIBUTE_ADD
+ FRR_REDISTRIBUTE_DELETE
+ FRR_REDISTRIBUTE_DEFAULT_ADD
+ FRR_REDISTRIBUTE_DEFAULT_DELETE
+ FRR_ROUTER_ID_ADD
+ FRR_ROUTER_ID_DELETE
+ FRR_ROUTER_ID_UPDATE
+ FRR_HELLO
+ FRR_NEXTHOP_REGISTER
+ FRR_NEXTHOP_UNREGISTER
+ FRR_NEXTHOP_UPDATE
+ FRR_INTERFACE_NBR_ADDRESS_ADD
+ FRR_INTERFACE_NBR_ADDRESS_DELETE
+ FRR_INTERFACE_BFD_DEST_UPDATE
+ FRR_IMPORT_ROUTE_REGISTER
+ FRR_IMPORT_ROUTE_UNREGISTER
+ FRR_IMPORT_CHECK_UPDATE
+ FRR_IPV4_ROUTE_IPV6_NEXTHOP_ADD
+ FRR_BFD_DEST_REGISTER
+ FRR_BFD_DEST_DEREGISTER
+ FRR_BFD_DEST_UPDATE
+ FRR_BFD_DEST_REPLAY
+ FRR_REDISTRIBUTE_IPV4_ADD
+ FRR_REDISTRIBUTE_IPV4_DEL
+ FRR_REDISTRIBUTE_IPV6_ADD
+ FRR_REDISTRIBUTE_IPV6_DEL
+ FRR_VRF_UNREGISTER
+ FRR_VRF_ADD
+ FRR_VRF_DELETE
+ FRR_INTERFACE_VRF_UPDATE
+ FRR_BFD_CLIENT_REGISTER
+ FRR_INTERFACE_ENABLE_RADV
+ FRR_INTERFACE_DISABLE_RADV
+ FRR_IPV4_NEXTHOP_LOOKUP_MRIB
+ FRR_INTERFACE_LINK_PARAMS
+ FRR_MPLS_LABELS_ADD
+ FRR_MPLS_LABELS_DELETE
+ FRR_IPV4_NEXTHOP_ADD
+ FRR_IPV4_NEXTHOP_DELETE
+ FRR_IPV6_NEXTHOP_ADD
+ FRR_IPV6_NEXTHOP_DELETE
+ FRR_IPMR_ROUTE_STATS
+ FRR_LABEL_MANAGER_CONNECT
+ FRR_GET_LABEL_CHUNK
+ FRR_RELEASE_LABEL_CHUNK
+ FRR_PW_ADD
+ FRR_PW_DELETE
+ FRR_PW_SET
+ FRR_PW_UNSET
+ FRR_PW_STATUS_UPDATE
+)
+
+// Route Types.
+//go:generate stringer -type=ROUTE_TYPE
+type ROUTE_TYPE uint8
+
+// For Quagga.
+const (
+ ROUTE_SYSTEM ROUTE_TYPE = iota
+ ROUTE_KERNEL
+ ROUTE_CONNECT
+ ROUTE_STATIC
+ ROUTE_RIP
+ ROUTE_RIPNG
+ ROUTE_OSPF
+ ROUTE_OSPF6
+ ROUTE_ISIS
+ ROUTE_BGP
+ ROUTE_PIM
+ ROUTE_HSLS
+ ROUTE_OLSR
+ ROUTE_BABEL
+ ROUTE_MAX
+)
+
+// For FRRouting.
+const (
+ FRR_ROUTE_SYSTEM ROUTE_TYPE = iota
+ FRR_ROUTE_KERNEL
+ FRR_ROUTE_CONNECT
+ FRR_ROUTE_STATIC
+ FRR_ROUTE_RIP
+ FRR_ROUTE_RIPNG
+ FRR_ROUTE_OSPF
+ FRR_ROUTE_OSPF6
+ FRR_ROUTE_ISIS
+ FRR_ROUTE_BGP
+ FRR_ROUTE_PIM
+ FRR_ROUTE_HSLS
+ FRR_ROUTE_OLSR
+ FRR_ROUTE_TABLE
+ FRR_ROUTE_LDP
+ FRR_ROUTE_VNC
+ FRR_ROUTE_VNC_DIRECT
+ FRR_ROUTE_VNC_DIRECT_RH
+ FRR_ROUTE_BGP_DIRECT
+ FRR_ROUTE_BGP_DIRECT_EXT
+ FRR_ROUTE_ALL
+ FRR_ROUTE_MAX
+)
+
+var routeTypeValueMap = map[string]ROUTE_TYPE{
+ "system": ROUTE_SYSTEM,
+ "kernel": ROUTE_KERNEL,
+ "connect": ROUTE_CONNECT, // hack for backyard compatibility
+ "directly-connected": ROUTE_CONNECT,
+ "static": ROUTE_STATIC,
+ "rip": ROUTE_RIP,
+ "ripng": ROUTE_RIPNG,
+ "ospf": ROUTE_OSPF,
+ "ospf3": ROUTE_OSPF6,
+ "isis": ROUTE_ISIS,
+ "bgp": ROUTE_BGP,
+ "pim": ROUTE_PIM,
+ "hsls": ROUTE_HSLS,
+ "olsr": ROUTE_OLSR,
+ "babel": ROUTE_BABEL,
+ "table": FRR_ROUTE_TABLE,
+ "ldp": FRR_ROUTE_LDP,
+ "vnc": FRR_ROUTE_VNC,
+ "vnc-direct": FRR_ROUTE_VNC_DIRECT,
+ "vnc-direct-rh": FRR_ROUTE_VNC_DIRECT_RH,
+ "bgp-direct": FRR_ROUTE_BGP_DIRECT,
+ "bgp-direct-ext": FRR_ROUTE_BGP_DIRECT_EXT,
+ "all": FRR_ROUTE_ALL,
+}
+
+func RouteTypeFromString(typ string) (ROUTE_TYPE, error) {
+ t, ok := routeTypeValueMap[typ]
+ if ok {
+ return t, nil
+ }
+ return t, fmt.Errorf("unknown route type: %s", typ)
+}
+
+// API Message Flags.
+type MESSAGE_FLAG uint8
+
+// For Quagga.
+const (
+ MESSAGE_NEXTHOP MESSAGE_FLAG = 0x01
+ MESSAGE_IFINDEX MESSAGE_FLAG = 0x02
+ MESSAGE_DISTANCE MESSAGE_FLAG = 0x04
+ MESSAGE_METRIC MESSAGE_FLAG = 0x08
+ MESSAGE_MTU MESSAGE_FLAG = 0x10
+ MESSAGE_TAG MESSAGE_FLAG = 0x20
+)
+
+func (t MESSAGE_FLAG) String() string {
+ var ss []string
+ if t&MESSAGE_NEXTHOP > 0 {
+ ss = append(ss, "NEXTHOP")
+ }
+ if t&MESSAGE_IFINDEX > 0 {
+ ss = append(ss, "IFINDEX")
+ }
+ if t&MESSAGE_DISTANCE > 0 {
+ ss = append(ss, "DISTANCE")
+ }
+ if t&MESSAGE_METRIC > 0 {
+ ss = append(ss, "METRIC")
+ }
+ if t&MESSAGE_MTU > 0 {
+ ss = append(ss, "MTU")
+ }
+ if t&MESSAGE_TAG > 0 {
+ ss = append(ss, "TAG")
+ }
+ return strings.Join(ss, "|")
+}
+
+// For FRRouting.
+const (
+ FRR_MESSAGE_NEXTHOP MESSAGE_FLAG = 0x01
+ FRR_MESSAGE_IFINDEX MESSAGE_FLAG = 0x02
+ FRR_MESSAGE_DISTANCE MESSAGE_FLAG = 0x04
+ FRR_MESSAGE_METRIC MESSAGE_FLAG = 0x08
+ FRR_MESSAGE_TAG MESSAGE_FLAG = 0x10
+ FRR_MESSAGE_MTU MESSAGE_FLAG = 0x20
+ FRR_MESSAGE_SRCPFX MESSAGE_FLAG = 0x40
+)
+
+// Message Flags
+type FLAG uint64
+
+const (
+ FLAG_INTERNAL FLAG = 0x01
+ FLAG_SELFROUTE FLAG = 0x02
+ FLAG_BLACKHOLE FLAG = 0x04
+ FLAG_IBGP FLAG = 0x08
+ FLAG_SELECTED FLAG = 0x10
+ FLAG_CHANGED FLAG = 0x20
+ FLAG_STATIC FLAG = 0x40
+ FLAG_REJECT FLAG = 0x80
+ FLAG_SCOPE_LINK FLAG = 0x100
+ FLAG_FIB_OVERRIDE FLAG = 0x200
+)
+
+func (t FLAG) String() string {
+ var ss []string
+ if t&FLAG_INTERNAL > 0 {
+ ss = append(ss, "FLAG_INTERNAL")
+ }
+ if t&FLAG_SELFROUTE > 0 {
+ ss = append(ss, "FLAG_SELFROUTE")
+ }
+ if t&FLAG_BLACKHOLE > 0 {
+ ss = append(ss, "FLAG_BLACKHOLE")
+ }
+ if t&FLAG_IBGP > 0 {
+ ss = append(ss, "FLAG_IBGP")
+ }
+ if t&FLAG_SELECTED > 0 {
+ ss = append(ss, "FLAG_SELECTED")
+ }
+ if t&FLAG_CHANGED > 0 {
+ ss = append(ss, "FLAG_CHANGED")
+ }
+ if t&FLAG_STATIC > 0 {
+ ss = append(ss, "FLAG_STATIC")
+ }
+ if t&FLAG_REJECT > 0 {
+ ss = append(ss, "FLAG_REJECT")
+ }
+ if t&FLAG_SCOPE_LINK > 0 {
+ ss = append(ss, "FLAG_SCOPE_LINK")
+ }
+ if t&FLAG_FIB_OVERRIDE > 0 {
+ ss = append(ss, "FLAG_FIB_OVERRIDE")
+ }
+ return strings.Join(ss, "|")
+}
+
+// Nexthop Flags.
+//go:generate stringer -type=NEXTHOP_FLAG
+type NEXTHOP_FLAG uint8
+
+// For Quagga.
+const (
+ _ NEXTHOP_FLAG = iota
+ NEXTHOP_IFINDEX
+ NEXTHOP_IFNAME
+ NEXTHOP_IPV4
+ NEXTHOP_IPV4_IFINDEX
+ NEXTHOP_IPV4_IFNAME
+ NEXTHOP_IPV6
+ NEXTHOP_IPV6_IFINDEX
+ NEXTHOP_IPV6_IFNAME
+ NEXTHOP_BLACKHOLE
+)
+
+// For FRRouting.
+const (
+ _ NEXTHOP_FLAG = iota
+ FRR_NEXTHOP_IFINDEX
+ FRR_NEXTHOP_IPV4
+ FRR_NEXTHOP_IPV4_IFINDEX
+ FRR_NEXTHOP_IPV6
+ FRR_NEXTHOP_IPV6_IFINDEX
+ FRR_NEXTHOP_BLACKHOLE
+)
+
+// Interface PTM Enable Configuration.
+//go:generate stringer -type=PTM_ENABLE
+type PTM_ENABLE uint8
+
+const (
+ PTM_ENABLE_OFF PTM_ENABLE = 0
+ PTM_ENABLE_ON PTM_ENABLE = 1
+ PTM_ENABLE_UNSPEC PTM_ENABLE = 2
+)
+
+// PTM Status.
+//go:generate stringer -type=PTM_STATUS
+type PTM_STATUS uint8
+
+const (
+ PTM_STATUS_DOWN PTM_STATUS = 0
+ PTM_STATUS_UP PTM_STATUS = 1
+ PTM_STATUS_UNKNOWN PTM_STATUS = 2
+)
+
+type Client struct {
+ outgoing chan *Message
+ incoming chan *Message
+ redistDefault ROUTE_TYPE
+ conn net.Conn
+ Version uint8
+}
+
+func NewClient(network, address string, typ ROUTE_TYPE, version uint8) (*Client, error) {
+ conn, err := net.Dial(network, address)
+ if err != nil {
+ return nil, err
+ }
+ outgoing := make(chan *Message)
+ incoming := make(chan *Message, 64)
+ if version < 2 {
+ version = 2
+ } else if version > 4 {
+ version = 4
+ }
+
+ c := &Client{
+ outgoing: outgoing,
+ incoming: incoming,
+ redistDefault: typ,
+ conn: conn,
+ Version: version,
+ }
+
+ go func() {
+ for {
+ m, more := <-outgoing
+ if more {
+ b, err := m.Serialize()
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ }).Warnf("failed to serialize: %v", m)
+ continue
+ }
+
+ _, err = conn.Write(b)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ }).Errorf("failed to write: %s", err)
+ close(outgoing)
+ }
+ } else {
+ log.Debug("finish outgoing loop")
+ return
+ }
+ }
+ }()
+
+ // Send HELLO/ROUTER_ID_ADD messages to negotiate the Zebra message version.
+ c.SendHello()
+ c.SendRouterIDAdd()
+
+ receiveSingleMsg := func() (*Message, error) {
+ headerBuf, err := readAll(conn, int(HeaderSize(version)))
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Error": err,
+ }).Error("failed to read header")
+ return nil, err
+ }
+
+ hd := &Header{}
+ err = hd.DecodeFromBytes(headerBuf)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Data": headerBuf,
+ "Error": err,
+ }).Error("failed to decode header")
+ return nil, err
+ }
+
+ bodyBuf, err := readAll(conn, int(hd.Len-HeaderSize(version)))
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Header": hd,
+ "Error": err,
+ }).Error("failed to read body")
+ return nil, err
+ }
+
+ m, err := ParseMessage(hd, bodyBuf)
+ if err != nil {
+ // Just outputting warnings (not error message) and ignore this
+ // error considering the case that body parser is not implemented
+ // yet.
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Header": hd,
+ "Data": bodyBuf,
+ "Error": err,
+ }).Warn("failed to decode body")
+ return nil, nil
+ }
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Message": m,
+ }).Debug("read message from zebra")
+
+ return m, nil
+ }
+
+ // Try to receive the first message from Zebra.
+ if m, err := receiveSingleMsg(); err != nil {
+ c.Close()
+ // Return error explicitly in order to retry connection.
+ return nil, err
+ } else if m != nil {
+ incoming <- m
+ }
+
+ // Start receive loop only when the first message successfully received.
+ go func() {
+ defer close(incoming)
+ for {
+ if m, err := receiveSingleMsg(); err != nil {
+ return
+ } else if m != nil {
+ incoming <- m
+ }
+ }
+ }()
+
+ return c, nil
+}
+
+func readAll(conn net.Conn, length int) ([]byte, error) {
+ buf := make([]byte, length)
+ _, err := io.ReadFull(conn, buf)
+ return buf, err
+}
+
+func (c *Client) Receive() chan *Message {
+ return c.incoming
+}
+
+func (c *Client) Send(m *Message) {
+ defer func() {
+ if err := recover(); err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ }).Debugf("recovered: %s", err)
+ }
+ }()
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Header": m.Header,
+ "Body": m.Body,
+ }).Debug("send command to zebra")
+ c.outgoing <- m
+}
+
+func (c *Client) SendCommand(command API_TYPE, vrfId uint16, body Body) error {
+ var marker uint8 = HEADER_MARKER
+ if c.Version >= 4 {
+ marker = FRR_HEADER_MARKER
+ }
+ m := &Message{
+ Header: Header{
+ Len: HeaderSize(c.Version),
+ Marker: marker,
+ Version: c.Version,
+ VrfId: vrfId,
+ Command: command,
+ },
+ Body: body,
+ }
+ c.Send(m)
+ return nil
+}
+
+func (c *Client) SendHello() error {
+ if c.redistDefault > 0 {
+ command := HELLO
+ body := &HelloBody{
+ RedistDefault: c.redistDefault,
+ Instance: 0,
+ }
+ if c.Version >= 4 {
+ command = FRR_HELLO
+ }
+ return c.SendCommand(command, VRF_DEFAULT, body)
+ }
+ return nil
+}
+
+func (c *Client) SendRouterIDAdd() error {
+ command := ROUTER_ID_ADD
+ if c.Version >= 4 {
+ command = FRR_ROUTER_ID_ADD
+ }
+ return c.SendCommand(command, VRF_DEFAULT, nil)
+}
+
+func (c *Client) SendInterfaceAdd() error {
+ command := INTERFACE_ADD
+ if c.Version >= 4 {
+ command = FRR_INTERFACE_ADD
+ }
+ return c.SendCommand(command, VRF_DEFAULT, nil)
+}
+
+func (c *Client) SendRedistribute(t ROUTE_TYPE, vrfId uint16) error {
+ command := REDISTRIBUTE_ADD
+ if c.redistDefault != t {
+ bodies := make([]*RedistributeBody, 0)
+ if c.Version <= 3 {
+ bodies = append(bodies, &RedistributeBody{
+ Redist: t,
+ })
+ } else { // version >= 4
+ command = FRR_REDISTRIBUTE_ADD
+ for _, afi := range []AFI{AFI_IP, AFI_IP6} {
+ bodies = append(bodies, &RedistributeBody{
+ Afi: afi,
+ Redist: t,
+ Instance: 0,
+ })
+ }
+ }
+
+ for _, body := range bodies {
+ return c.SendCommand(command, vrfId, body)
+ }
+ }
+
+ return nil
+}
+
+func (c *Client) SendRedistributeDelete(t ROUTE_TYPE) error {
+ if t < ROUTE_MAX {
+ command := REDISTRIBUTE_DELETE
+ if c.Version >= 4 {
+ command = FRR_REDISTRIBUTE_DELETE
+ }
+ body := &RedistributeBody{
+ Redist: t,
+ }
+ return c.SendCommand(command, VRF_DEFAULT, body)
+ } else {
+ return fmt.Errorf("unknown route type: %d", t)
+ }
+}
+
+func (c *Client) SendIPRoute(vrfId uint16, body *IPRouteBody, isWithdraw bool) error {
+ command := IPV4_ROUTE_ADD
+ if c.Version <= 3 {
+ if body.Prefix.To4() != nil {
+ if isWithdraw {
+ command = IPV4_ROUTE_DELETE
+ }
+ } else {
+ if isWithdraw {
+ command = IPV6_ROUTE_DELETE
+ } else {
+ command = IPV6_ROUTE_ADD
+ }
+ }
+ } else { // version >= 4
+ if body.Prefix.To4() != nil {
+ if isWithdraw {
+ command = FRR_IPV4_ROUTE_DELETE
+ } else {
+ command = FRR_IPV4_ROUTE_ADD
+ }
+ } else {
+ if isWithdraw {
+ command = FRR_IPV6_ROUTE_DELETE
+ } else {
+ command = FRR_IPV6_ROUTE_ADD
+ }
+ }
+ }
+ return c.SendCommand(command, vrfId, body)
+}
+
+func (c *Client) SendNexthopRegister(vrfId uint16, body *NexthopRegisterBody, isWithdraw bool) error {
+ // Note: NEXTHOP_REGISTER and NEXTHOP_UNREGISTER messages are not
+ // supported in Zebra protocol version<3.
+ if c.Version < 3 {
+ return fmt.Errorf("NEXTHOP_REGISTER/NEXTHOP_UNREGISTER are not supported in version: %d", c.Version)
+ }
+ command := NEXTHOP_REGISTER
+ if c.Version == 3 {
+ if isWithdraw {
+ command = NEXTHOP_UNREGISTER
+ }
+ } else { // version >= 4
+ if isWithdraw {
+ command = FRR_NEXTHOP_UNREGISTER
+ } else {
+ command = FRR_NEXTHOP_REGISTER
+ }
+ }
+ return c.SendCommand(command, vrfId, body)
+}
+
+func (c *Client) Close() error {
+ close(c.outgoing)
+ return c.conn.Close()
+}
+
+type Header struct {
+ Len uint16
+ Marker uint8
+ Version uint8
+ VrfId uint16
+ Command API_TYPE
+}
+
+func (h *Header) Serialize() ([]byte, error) {
+ buf := make([]byte, HeaderSize(h.Version))
+ binary.BigEndian.PutUint16(buf[0:2], h.Len)
+ buf[2] = h.Marker
+ buf[3] = h.Version
+ switch h.Version {
+ case 2:
+ binary.BigEndian.PutUint16(buf[4:6], uint16(h.Command))
+ case 3, 4:
+ binary.BigEndian.PutUint16(buf[4:6], uint16(h.VrfId))
+ binary.BigEndian.PutUint16(buf[6:8], uint16(h.Command))
+ default:
+ return nil, fmt.Errorf("Unsupported ZAPI version: %d", h.Version)
+ }
+ return buf, nil
+}
+
+func (h *Header) DecodeFromBytes(data []byte) error {
+ if uint16(len(data)) < 4 {
+ return fmt.Errorf("Not all ZAPI message header")
+ }
+ h.Len = binary.BigEndian.Uint16(data[0:2])
+ h.Marker = data[2]
+ h.Version = data[3]
+ if uint16(len(data)) < HeaderSize(h.Version) {
+ return fmt.Errorf("Not all ZAPI message header")
+ }
+ switch h.Version {
+ case 2:
+ h.Command = API_TYPE(binary.BigEndian.Uint16(data[4:6]))
+ case 3, 4:
+ h.VrfId = binary.BigEndian.Uint16(data[4:6])
+ h.Command = API_TYPE(binary.BigEndian.Uint16(data[6:8]))
+ default:
+ return fmt.Errorf("Unsupported ZAPI version: %d", h.Version)
+ }
+ return nil
+}
+
+type Body interface {
+ DecodeFromBytes([]byte, uint8) error
+ Serialize(uint8) ([]byte, error)
+ String() string
+}
+
+type UnknownBody struct {
+ Data []byte
+}
+
+func (b *UnknownBody) DecodeFromBytes(data []byte, version uint8) error {
+ b.Data = data
+ return nil
+}
+
+func (b *UnknownBody) Serialize(version uint8) ([]byte, error) {
+ return b.Data, nil
+}
+
+func (b *UnknownBody) String() string {
+ return fmt.Sprintf("data: %v", b.Data)
+}
+
+type HelloBody struct {
+ RedistDefault ROUTE_TYPE
+ Instance uint16
+}
+
+func (b *HelloBody) DecodeFromBytes(data []byte, version uint8) error {
+ b.RedistDefault = ROUTE_TYPE(data[0])
+ if version >= 4 {
+ b.Instance = binary.BigEndian.Uint16(data[1:3])
+ }
+ return nil
+}
+
+func (b *HelloBody) Serialize(version uint8) ([]byte, error) {
+ if version <= 3 {
+ return []byte{uint8(b.RedistDefault)}, nil
+ } else { // version >= 4
+ buf := make([]byte, 3)
+ buf[0] = uint8(b.RedistDefault)
+ binary.BigEndian.PutUint16(buf[1:3], b.Instance)
+ return buf, nil
+
+ }
+}
+
+func (b *HelloBody) String() string {
+ return fmt.Sprintf(
+ "route_type: %s, instance :%d",
+ b.RedistDefault.String(), b.Instance)
+}
+
+type RedistributeBody struct {
+ Afi AFI
+ Redist ROUTE_TYPE
+ Instance uint16
+}
+
+func (b *RedistributeBody) DecodeFromBytes(data []byte, version uint8) error {
+ if version <= 3 {
+ b.Redist = ROUTE_TYPE(data[0])
+ } else { // version >= 4
+ b.Afi = AFI(data[0])
+ b.Redist = ROUTE_TYPE(data[1])
+ b.Instance = binary.BigEndian.Uint16(data[2:4])
+ }
+ return nil
+}
+
+func (b *RedistributeBody) Serialize(version uint8) ([]byte, error) {
+ if version <= 3 {
+ return []byte{uint8(b.Redist)}, nil
+ } else { // version >= 4
+ buf := make([]byte, 4)
+ buf[0] = uint8(b.Afi)
+ buf[1] = uint8(b.Redist)
+ binary.BigEndian.PutUint16(buf[2:4], b.Instance)
+ return buf, nil
+ }
+}
+
+func (b *RedistributeBody) String() string {
+ return fmt.Sprintf(
+ "afi: %s, route_type: %s, instance :%d",
+ b.Afi.String(), b.Redist.String(), b.Instance)
+}
+
+type InterfaceUpdateBody struct {
+ Name string
+ Index uint32
+ Status INTERFACE_STATUS
+ Flags uint64
+ PTMEnable PTM_ENABLE
+ PTMStatus PTM_STATUS
+ Metric uint32
+ Speed uint32
+ MTU uint32
+ MTU6 uint32
+ Bandwidth uint32
+ Linktype LINK_TYPE
+ HardwareAddr net.HardwareAddr
+}
+
+func (b *InterfaceUpdateBody) DecodeFromBytes(data []byte, version uint8) error {
+ if len(data) < INTERFACE_NAMSIZ+29 {
+ return fmt.Errorf("lack of bytes. need %d but %d", INTERFACE_NAMSIZ+29, len(data))
+ }
+
+ b.Name = strings.Trim(string(data[:INTERFACE_NAMSIZ]), "\u0000")
+ data = data[INTERFACE_NAMSIZ:]
+ b.Index = binary.BigEndian.Uint32(data[0:4])
+ b.Status = INTERFACE_STATUS(data[4])
+ b.Flags = binary.BigEndian.Uint64(data[5:13])
+ if version >= 4 {
+ b.PTMEnable = PTM_ENABLE(data[13])
+ b.PTMStatus = PTM_STATUS(data[14])
+ b.Metric = binary.BigEndian.Uint32(data[15:19])
+ b.Speed = binary.BigEndian.Uint32(data[19:23])
+ data = data[23:]
+ } else {
+ b.Metric = binary.BigEndian.Uint32(data[13:17])
+ data = data[17:]
+ }
+ b.MTU = binary.BigEndian.Uint32(data[0:4])
+ b.MTU6 = binary.BigEndian.Uint32(data[4:8])
+ b.Bandwidth = binary.BigEndian.Uint32(data[8:12])
+ if version >= 3 {
+ b.Linktype = LINK_TYPE(binary.BigEndian.Uint32(data[12:16]))
+ data = data[16:]
+ } else {
+ data = data[12:]
+ }
+ l := binary.BigEndian.Uint32(data[:4])
+ if l > 0 {
+ if len(data) < 4+int(l) {
+ return fmt.Errorf("lack of bytes. need %d but %d", 4+l, len(data))
+ }
+ b.HardwareAddr = data[4 : 4+l]
+ }
+ return nil
+}
+
+func (b *InterfaceUpdateBody) Serialize(version uint8) ([]byte, error) {
+ return []byte{}, nil
+}
+
+func (b *InterfaceUpdateBody) String() string {
+ s := fmt.Sprintf(
+ "name: %s, idx: %d, status: %s, flags: %s, ptm_enable: %s, ptm_status: %s, metric: %d, speed: %d, mtu: %d, mtu6: %d, bandwidth: %d, linktype: %s",
+ b.Name, b.Index, b.Status.String(), intfflag2string(b.Flags), b.PTMEnable.String(), b.PTMStatus.String(), b.Metric, b.Speed, b.MTU, b.MTU6, b.Bandwidth, b.Linktype.String())
+ if len(b.HardwareAddr) > 0 {
+ return s + fmt.Sprintf(", mac: %s", b.HardwareAddr.String())
+ }
+ return s
+}
+
+type InterfaceAddressUpdateBody struct {
+ Index uint32
+ Flags INTERFACE_ADDRESS_FLAG
+ Prefix net.IP
+ Length uint8
+ Destination net.IP
+}
+
+func (b *InterfaceAddressUpdateBody) DecodeFromBytes(data []byte, version uint8) error {
+ b.Index = binary.BigEndian.Uint32(data[:4])
+ b.Flags = INTERFACE_ADDRESS_FLAG(data[4])
+ family := data[5]
+ var addrlen int8
+ switch family {
+ case syscall.AF_INET:
+ addrlen = net.IPv4len
+ case syscall.AF_INET6:
+ addrlen = net.IPv6len
+ default:
+ return fmt.Errorf("unknown address family: %d", family)
+ }
+ b.Prefix = data[6 : 6+addrlen]
+ b.Length = data[6+addrlen]
+ b.Destination = data[7+addrlen : 7+addrlen*2]
+ return nil
+}
+
+func (b *InterfaceAddressUpdateBody) Serialize(version uint8) ([]byte, error) {
+ return []byte{}, nil
+}
+
+func (b *InterfaceAddressUpdateBody) String() string {
+ return fmt.Sprintf(
+ "idx: %d, flags: %s, addr: %s/%d",
+ b.Index, b.Flags.String(), b.Prefix.String(), b.Length)
+}
+
+type RouterIDUpdateBody struct {
+ Length uint8
+ Prefix net.IP
+}
+
+func (b *RouterIDUpdateBody) DecodeFromBytes(data []byte, version uint8) error {
+ family := data[0]
+ var addrlen int8
+ switch family {
+ case syscall.AF_INET:
+ addrlen = net.IPv4len
+ case syscall.AF_INET6:
+ addrlen = net.IPv6len
+ default:
+ return fmt.Errorf("unknown address family: %d", family)
+ }
+ b.Prefix = data[1 : 1+addrlen]
+ b.Length = data[1+addrlen]
+ return nil
+}
+
+func (b *RouterIDUpdateBody) Serialize(version uint8) ([]byte, error) {
+ return []byte{}, nil
+}
+
+func (b *RouterIDUpdateBody) String() string {
+ return fmt.Sprintf("id: %s/%d", b.Prefix.String(), b.Length)
+}
+
+type IPRouteBody struct {
+ Type ROUTE_TYPE
+ Instance uint16
+ Flags FLAG
+ Message MESSAGE_FLAG
+ SAFI SAFI
+ Prefix net.IP
+ PrefixLength uint8
+ SrcPrefix net.IP
+ SrcPrefixLength uint8
+ Nexthops []net.IP
+ Ifindexs []uint32
+ Distance uint8
+ Metric uint32
+ Mtu uint32
+ Tag uint32
+ Api API_TYPE
+}
+
+func (b *IPRouteBody) RouteFamily() bgp.RouteFamily {
+ switch b.Api {
+ case IPV4_ROUTE_ADD, IPV4_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV4_ADD, FRR_REDISTRIBUTE_IPV4_DEL:
+ return bgp.RF_IPv4_UC
+ case IPV6_ROUTE_ADD, IPV6_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV6_ADD, FRR_REDISTRIBUTE_IPV6_DEL:
+ return bgp.RF_IPv6_UC
+ default:
+ return bgp.RF_OPAQUE
+ }
+}
+
+func (b *IPRouteBody) IsWithdraw() bool {
+ switch b.Api {
+ case IPV4_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV4_DEL, IPV6_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV6_DEL:
+ return true
+ default:
+ return false
+ }
+}
+
+func (b *IPRouteBody) Serialize(version uint8) ([]byte, error) {
+
+ var buf []byte
+ nhfIPv4 := uint8(NEXTHOP_IPV4)
+ nhfIPv6 := uint8(NEXTHOP_IPV6)
+ nhfIndx := uint8(NEXTHOP_IFINDEX)
+ nhfBlkH := uint8(NEXTHOP_BLACKHOLE)
+ if version <= 3 {
+ buf = make([]byte, 5)
+ buf[0] = uint8(b.Type)
+ buf[1] = uint8(b.Flags)
+ buf[2] = uint8(b.Message)
+ binary.BigEndian.PutUint16(buf[3:5], uint16(b.SAFI))
+ } else { // version >= 4
+ buf = make([]byte, 10)
+ buf[0] = uint8(b.Type)
+ binary.BigEndian.PutUint16(buf[1:3], uint16(b.Instance))
+ binary.BigEndian.PutUint32(buf[3:7], uint32(b.Flags))
+ buf[7] = uint8(b.Message)
+ binary.BigEndian.PutUint16(buf[8:10], uint16(b.SAFI))
+ nhfIPv4 = uint8(FRR_NEXTHOP_IPV4)
+ nhfIPv6 = uint8(FRR_NEXTHOP_IPV6)
+ nhfIndx = uint8(FRR_NEXTHOP_IFINDEX)
+ nhfBlkH = uint8(FRR_NEXTHOP_BLACKHOLE)
+ }
+ byteLen := (int(b.PrefixLength) + 7) / 8
+ buf = append(buf, b.PrefixLength)
+ buf = append(buf, b.Prefix[:byteLen]...)
+ if b.Message&FRR_MESSAGE_SRCPFX > 0 {
+ byteLen = (int(b.SrcPrefixLength) + 7) / 8
+ buf = append(buf, b.SrcPrefixLength)
+ buf = append(buf, b.SrcPrefix[:byteLen]...)
+ }
+
+ if b.Message&MESSAGE_NEXTHOP > 0 {
+ if b.Flags&FLAG_BLACKHOLE > 0 {
+ buf = append(buf, []byte{1, nhfBlkH}...)
+ } else {
+ buf = append(buf, uint8(len(b.Nexthops)+len(b.Ifindexs)))
+ }
+
+ for _, v := range b.Nexthops {
+ if v.To4() != nil {
+ buf = append(buf, nhfIPv4)
+ buf = append(buf, v.To4()...)
+ } else {
+ buf = append(buf, nhfIPv6)
+ buf = append(buf, v.To16()...)
+ }
+ }
+
+ for _, v := range b.Ifindexs {
+ buf = append(buf, nhfIndx)
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, v)
+ buf = append(buf, bbuf...)
+ }
+ }
+
+ if b.Message&MESSAGE_DISTANCE > 0 {
+ buf = append(buf, b.Distance)
+ }
+ if b.Message&MESSAGE_METRIC > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Metric)
+ buf = append(buf, bbuf...)
+ }
+ if version <= 3 {
+ if b.Message&MESSAGE_MTU > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Mtu)
+ buf = append(buf, bbuf...)
+ }
+ if b.Message&MESSAGE_TAG > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Tag)
+ buf = append(buf, bbuf...)
+ }
+ } else { // version >= 4
+ if b.Message&FRR_MESSAGE_TAG > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Tag)
+ buf = append(buf, bbuf...)
+ }
+ if b.Message&FRR_MESSAGE_MTU > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Mtu)
+ buf = append(buf, bbuf...)
+ }
+ }
+ return buf, nil
+}
+
+func (b *IPRouteBody) DecodeFromBytes(data []byte, version uint8) error {
+ isV4 := true
+ if version <= 3 {
+ isV4 = b.Api == IPV4_ROUTE_ADD || b.Api == IPV4_ROUTE_DELETE
+ } else {
+ isV4 = b.Api == FRR_REDISTRIBUTE_IPV4_ADD || b.Api == FRR_REDISTRIBUTE_IPV4_DEL
+ }
+ var addrLen uint8 = net.IPv4len
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+
+ b.Type = ROUTE_TYPE(data[0])
+ if version <= 3 {
+ b.Flags = FLAG(data[1])
+ data = data[2:]
+ } else { // version >= 4
+ b.Instance = binary.BigEndian.Uint16(data[1:3])
+ b.Flags = FLAG(binary.BigEndian.Uint32(data[3:7]))
+ data = data[7:]
+ }
+
+ b.Message = MESSAGE_FLAG(data[0])
+ b.SAFI = SAFI(SAFI_UNICAST)
+
+ b.PrefixLength = data[1]
+ if b.PrefixLength > addrLen*8 {
+ return fmt.Errorf("prefix length is greater than %d", addrLen*8)
+ }
+ pos := 2
+ buf := make([]byte, addrLen)
+ byteLen := int((b.PrefixLength + 7) / 8)
+ copy(buf, data[pos:pos+byteLen])
+ if isV4 {
+ b.Prefix = net.IP(buf).To4()
+ } else {
+ b.Prefix = net.IP(buf).To16()
+ }
+ pos += byteLen
+
+ if b.Message&FRR_MESSAGE_SRCPFX > 0 {
+ b.SrcPrefixLength = data[pos]
+ pos += 1
+ buf = make([]byte, addrLen)
+ byteLen = int((b.SrcPrefixLength + 7) / 8)
+ copy(buf, data[pos:pos+byteLen])
+ if isV4 {
+ b.SrcPrefix = net.IP(buf).To4()
+ } else {
+ b.SrcPrefix = net.IP(buf).To16()
+ }
+ pos += byteLen
+ }
+
+ rest := 0
+ var numNexthop int
+ if b.Message&MESSAGE_NEXTHOP > 0 {
+ numNexthop = int(data[pos])
+ // rest = numNexthop(1) + (nexthop(4 or 16) + placeholder(1) + ifindex(4)) * numNexthop
+ rest += 1 + numNexthop*(int(addrLen)+5)
+ }
+ if b.Message&MESSAGE_DISTANCE > 0 {
+ // distance(1)
+ rest += 1
+ }
+ if b.Message&MESSAGE_METRIC > 0 {
+ // metric(4)
+ rest += 4
+ }
+ if version <= 3 {
+ if b.Message&MESSAGE_MTU > 0 {
+ // mtu(4)
+ rest += 4
+ }
+ if b.Message&MESSAGE_TAG > 0 {
+ // tag(4)
+ rest += 4
+ }
+ } else { // version >= 4
+ if b.Message&FRR_MESSAGE_TAG > 0 {
+ // tag(4)
+ rest += 4
+ }
+ if b.Message&FRR_MESSAGE_MTU > 0 {
+ // mtu(4)
+ rest += 4
+ }
+ }
+
+ if len(data[pos:]) != rest {
+ return fmt.Errorf("message length invalid")
+ }
+
+ b.Nexthops = []net.IP{}
+ b.Ifindexs = []uint32{}
+
+ if b.Message&MESSAGE_NEXTHOP > 0 {
+ pos += 1
+ for i := 0; i < numNexthop; i++ {
+ addr := data[pos : pos+int(addrLen)]
+ var nexthop net.IP
+ if isV4 {
+ nexthop = net.IP(addr).To4()
+ } else {
+ nexthop = net.IP(addr).To16()
+ }
+ b.Nexthops = append(b.Nexthops, nexthop)
+
+ // skip nexthop and 1byte place holder
+ pos += int(addrLen + 1)
+ ifidx := binary.BigEndian.Uint32(data[pos : pos+4])
+ b.Ifindexs = append(b.Ifindexs, ifidx)
+ pos += 4
+ }
+ }
+
+ if b.Message&MESSAGE_DISTANCE > 0 {
+ b.Distance = data[pos]
+ pos += 1
+ }
+ if b.Message&MESSAGE_METRIC > 0 {
+ b.Metric = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ if version <= 3 {
+ if b.Message&MESSAGE_MTU > 0 {
+ b.Mtu = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ if b.Message&MESSAGE_TAG > 0 {
+ b.Tag = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ } else {
+ if b.Message&FRR_MESSAGE_TAG > 0 {
+ b.Tag = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ if b.Message&FRR_MESSAGE_MTU > 0 {
+ b.Mtu = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ }
+
+ return nil
+}
+
+func (b *IPRouteBody) String() string {
+ s := fmt.Sprintf(
+ "type: %s, instance: %d, flags: %s, message: %d, safi: %s, prefix: %s/%d, src_prefix: %s/%d",
+ b.Type.String(), b.Instance, b.Flags.String(), b.Message, b.SAFI.String(), b.Prefix.String(), b.PrefixLength, b.SrcPrefix.String(), b.SrcPrefixLength)
+ for i, nh := range b.Nexthops {
+ s += fmt.Sprintf(", nexthops[%d]: %s", i, nh.String())
+ }
+ for i, idx := range b.Ifindexs {
+ s += fmt.Sprintf(", ifindex[%d]: %d", i, idx)
+ }
+ return s + fmt.Sprintf(
+ ", distance: %d, metric: %d, mtu: %d, tag: %d",
+ b.Distance, b.Metric, b.Mtu, b.Tag)
+}
+
+type NexthopLookupBody struct {
+ Api API_TYPE
+ Addr net.IP
+ Distance uint8
+ Metric uint32
+ Nexthops []*Nexthop
+}
+
+type Nexthop struct {
+ Ifname string
+ Ifindex uint32
+ Type NEXTHOP_FLAG
+ Addr net.IP
+}
+
+func (n *Nexthop) String() string {
+ s := fmt.Sprintf(
+ "type: %s, addr: %s, ifindex: %d, ifname: %s",
+ n.Type.String(), n.Addr.String(), n.Ifindex, n.Ifname)
+ return s
+}
+
+func decodeNexthopsFromBytes(nexthops *[]*Nexthop, data []byte, isV4 bool, version uint8) (int, error) {
+ addrLen := net.IPv4len
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+ nhIfindex := NEXTHOP_IFINDEX
+ nhIfname := NEXTHOP_IFNAME
+ nhIPv4 := NEXTHOP_IPV4
+ nhIPv4Ifindex := NEXTHOP_IPV4_IFINDEX
+ nhIPv4Ifname := NEXTHOP_IPV4_IFNAME
+ nhIPv6 := NEXTHOP_IPV6
+ nhIPv6Ifindex := NEXTHOP_IPV6_IFINDEX
+ nhIPv6Ifname := NEXTHOP_IPV6_IFNAME
+ if version >= 4 {
+ nhIfindex = FRR_NEXTHOP_IFINDEX
+ nhIfname = NEXTHOP_FLAG(0)
+ nhIPv4 = FRR_NEXTHOP_IPV4
+ nhIPv4Ifindex = FRR_NEXTHOP_IPV4_IFINDEX
+ nhIPv4Ifname = NEXTHOP_FLAG(0)
+ nhIPv6 = FRR_NEXTHOP_IPV6
+ nhIPv6Ifindex = FRR_NEXTHOP_IPV6_IFINDEX
+ nhIPv6Ifname = NEXTHOP_FLAG(0)
+ }
+
+ numNexthop := int(data[0])
+ offset := 1
+
+ for i := 0; i < numNexthop; i++ {
+ nh := &Nexthop{}
+ nh.Type = NEXTHOP_FLAG(data[offset])
+ offset += 1
+
+ switch nh.Type {
+ case nhIfindex, nhIfname:
+ nh.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4])
+ offset += 4
+
+ case nhIPv4, nhIPv6:
+ if isV4 {
+ nh.Addr = net.IP(data[offset : offset+addrLen]).To4()
+ } else {
+ nh.Addr = net.IP(data[offset : offset+addrLen]).To16()
+ }
+ offset += addrLen
+ if version >= 4 {
+ // On FRRouting version 3.0 or later, NEXTHOP_IPV4 and
+ // NEXTHOP_IPV6 have the same structure with
+ // NEXTHOP_TYPE_IPV4_IFINDEX and NEXTHOP_TYPE_IPV6_IFINDEX.
+ nh.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4])
+ offset += 4
+ }
+
+ case nhIPv4Ifindex, nhIPv4Ifname, nhIPv6Ifindex, nhIPv6Ifname:
+ if isV4 {
+ nh.Addr = net.IP(data[offset : offset+addrLen]).To4()
+ } else {
+ nh.Addr = net.IP(data[offset : offset+addrLen]).To16()
+ }
+ offset += addrLen
+ nh.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4])
+ offset += 4
+ }
+ *nexthops = append(*nexthops, nh)
+ }
+
+ return offset, nil
+}
+
+func (b *NexthopLookupBody) Serialize(version uint8) ([]byte, error) {
+ isV4 := false
+ if version <= 3 {
+ isV4 = b.Api == IPV4_NEXTHOP_LOOKUP
+ } else { // version >= 4
+ isV4 = b.Api == FRR_IPV4_NEXTHOP_LOOKUP_MRIB
+ }
+
+ buf := make([]byte, 0)
+
+ if isV4 {
+ buf = append(buf, b.Addr.To4()...)
+ } else {
+ buf = append(buf, b.Addr.To16()...)
+ }
+ return buf, nil
+}
+
+func (b *NexthopLookupBody) DecodeFromBytes(data []byte, version uint8) error {
+ isV4 := false
+ if version <= 3 {
+ isV4 = b.Api == IPV4_NEXTHOP_LOOKUP
+ } else { // version >= 4
+ isV4 = b.Api == FRR_IPV4_NEXTHOP_LOOKUP_MRIB
+ }
+ addrLen := net.IPv4len
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+
+ if len(data) < addrLen {
+ return fmt.Errorf("message length invalid")
+ }
+
+ buf := make([]byte, addrLen)
+ copy(buf, data[0:addrLen])
+ pos := addrLen
+
+ if isV4 {
+ b.Addr = net.IP(buf).To4()
+ } else {
+ b.Addr = net.IP(buf).To16()
+ }
+
+ if version >= 4 {
+ b.Distance = data[pos]
+ pos++
+ }
+
+ if len(data[pos:]) > int(1+addrLen) {
+ b.Metric = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ b.Nexthops = []*Nexthop{}
+ if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[pos:], isV4, version); err != nil {
+ return err
+ } else {
+ pos += nexthopsByteLen
+ }
+ }
+
+ return nil
+}
+
+func (b *NexthopLookupBody) String() string {
+ s := fmt.Sprintf(
+ "addr: %s, distance:%d, metric: %d",
+ b.Addr.String(), b.Distance, b.Metric)
+ if len(b.Nexthops) > 0 {
+ for _, nh := range b.Nexthops {
+ s = s + fmt.Sprintf(", nexthop:{%s}", nh.String())
+ }
+ }
+ return s
+}
+
+type ImportLookupBody struct {
+ Api API_TYPE
+ PrefixLength uint8
+ Prefix net.IP
+ Addr net.IP
+ Metric uint32
+ Nexthops []*Nexthop
+}
+
+func (b *ImportLookupBody) Serialize(version uint8) ([]byte, error) {
+ buf := make([]byte, 1)
+ buf[0] = b.PrefixLength
+ buf = append(buf, b.Addr.To4()...)
+ return buf, nil
+}
+
+func (b *ImportLookupBody) DecodeFromBytes(data []byte, version uint8) error {
+ isV4 := b.Api == IPV4_IMPORT_LOOKUP
+ addrLen := net.IPv4len
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+
+ if len(data) < addrLen {
+ return fmt.Errorf("message length invalid")
+ }
+
+ buf := make([]byte, addrLen)
+ copy(buf, data[0:addrLen])
+ pos := addrLen
+
+ b.Addr = net.IP(buf).To4()
+
+ if len(data[pos:]) > int(1+addrLen) {
+ b.Metric = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ b.Nexthops = []*Nexthop{}
+ if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[pos:], isV4, version); err != nil {
+ return err
+ } else {
+ pos += nexthopsByteLen
+ }
+ }
+
+ return nil
+}
+
+func (b *ImportLookupBody) String() string {
+ s := fmt.Sprintf(
+ "prefix: %s/%d, addr: %s, metric: %d",
+ b.Prefix.String(), b.PrefixLength, b.Addr.String(), b.Metric)
+ if len(b.Nexthops) > 0 {
+ for _, nh := range b.Nexthops {
+ s = s + fmt.Sprintf(", nexthop:{%s}", nh.String())
+ }
+ }
+ return s
+}
+
+type RegisteredNexthop struct {
+ Connected uint8
+ Family uint16
+ // Note: Ignores PrefixLength (uint8),
+ // because this field should be always:
+ // - 32 if Address Family is AF_INET
+ // - 128 if Address Family is AF_INET6
+ Prefix net.IP
+}
+
+func (n *RegisteredNexthop) Len() int {
+ // Connected (1 byte) + Address Family (2 bytes) + Prefix Length (1 byte) + Prefix (variable)
+ if n.Family == uint16(syscall.AF_INET) {
+ return 4 + net.IPv4len
+ } else {
+ return 4 + net.IPv6len
+ }
+}
+
+func (n *RegisteredNexthop) Serialize() ([]byte, error) {
+ // Connected (1 byte)
+ buf := make([]byte, 4)
+ buf[0] = byte(n.Connected)
+
+ // Address Family (2 bytes)
+ binary.BigEndian.PutUint16(buf[1:3], n.Family)
+
+ // Prefix Length (1 byte) + Prefix (variable)
+ switch n.Family {
+ case uint16(syscall.AF_INET):
+ buf[3] = byte(net.IPv4len * 8)
+ buf = append(buf, n.Prefix.To4()...)
+ case uint16(syscall.AF_INET6):
+ buf[3] = byte(net.IPv6len * 8)
+ buf = append(buf, n.Prefix.To16()...)
+ default:
+ return nil, fmt.Errorf("invalid address family: %d", n.Family)
+ }
+
+ return buf, nil
+}
+
+func (n *RegisteredNexthop) DecodeFromBytes(data []byte) error {
+ // Connected (1 byte)
+ n.Connected = uint8(data[0])
+ offset := 1
+
+ // Address Family (2 bytes)
+ n.Family = binary.BigEndian.Uint16(data[offset : offset+2])
+ isV4 := n.Family == uint16(syscall.AF_INET)
+ addrLen := int(net.IPv4len)
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+ // Note: Ignores Prefix Length (1 byte)
+ offset += 3
+
+ // Prefix (variable)
+ if isV4 {
+ n.Prefix = net.IP(data[offset : offset+addrLen]).To4()
+ } else {
+ n.Prefix = net.IP(data[offset : offset+addrLen]).To16()
+ }
+
+ return nil
+}
+
+func (n *RegisteredNexthop) String() string {
+ return fmt.Sprintf(
+ "connected: %d, family: %d, prefix: %s",
+ n.Connected, n.Family, n.Prefix.String())
+}
+
+type NexthopRegisterBody struct {
+ Api API_TYPE
+ Nexthops []*RegisteredNexthop
+}
+
+func (b *NexthopRegisterBody) Serialize(version uint8) ([]byte, error) {
+ buf := make([]byte, 0)
+
+ // List of Registered Nexthops
+ for _, nh := range b.Nexthops {
+ nhBuf, err := nh.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, nhBuf...)
+ }
+
+ return buf, nil
+}
+
+func (b *NexthopRegisterBody) DecodeFromBytes(data []byte, version uint8) error {
+ offset := 0
+
+ // List of Registered Nexthops
+ b.Nexthops = []*RegisteredNexthop{}
+ for len(data[offset:]) > 0 {
+ nh := new(RegisteredNexthop)
+ err := nh.DecodeFromBytes(data[offset:])
+ if err != nil {
+ return err
+ }
+ b.Nexthops = append(b.Nexthops, nh)
+
+ offset += nh.Len()
+ if len(data) < offset {
+ break
+ }
+ }
+
+ return nil
+}
+
+func (b *NexthopRegisterBody) String() string {
+ s := make([]string, 0)
+ for _, nh := range b.Nexthops {
+ s = append(s, fmt.Sprintf("nexthop:{%s}", nh.String()))
+ }
+ return strings.Join(s, ", ")
+}
+
+type NexthopUpdateBody struct {
+ Api API_TYPE
+ Family uint16
+ // Note: Ignores PrefixLength (uint8),
+ // because this field should be always:
+ // - 32 if Address Family is AF_INET
+ // - 128 if Address Family is AF_INET6
+ Prefix net.IP
+ Distance uint8
+ Metric uint32
+ Nexthops []*Nexthop
+}
+
+func (b *NexthopUpdateBody) Serialize(version uint8) ([]byte, error) {
+ // Address Family (2 bytes)
+ buf := make([]byte, 3)
+ binary.BigEndian.PutUint16(buf, b.Family)
+
+ // Prefix Length (1 byte) + Prefix (variable)
+ switch b.Family {
+ case uint16(syscall.AF_INET):
+ buf[2] = byte(net.IPv4len * 8)
+ buf = append(buf, b.Prefix.To4()...)
+ case uint16(syscall.AF_INET6):
+ buf[2] = byte(net.IPv6len * 8)
+ buf = append(buf, b.Prefix.To16()...)
+ default:
+ return nil, fmt.Errorf("invalid address family: %d", b.Family)
+ }
+
+ return buf, nil
+}
+
+func (b *NexthopUpdateBody) DecodeFromBytes(data []byte, version uint8) error {
+ // Address Family (2 bytes)
+ b.Family = binary.BigEndian.Uint16(data[0:2])
+ isV4 := b.Family == uint16(syscall.AF_INET)
+ addrLen := int(net.IPv4len)
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+ // Note: Ignores Prefix Length (1 byte)
+ offset := 3
+
+ // Prefix (variable)
+ if isV4 {
+ b.Prefix = net.IP(data[offset : offset+addrLen]).To4()
+ } else {
+ b.Prefix = net.IP(data[offset : offset+addrLen]).To16()
+ }
+ offset += addrLen
+
+ // Distance (1 byte) (if version>=4)
+ // Metric (4 bytes)
+ // Number of Nexthops (1 byte)
+ if version >= 4 {
+ if len(data[offset:]) < 6 {
+ return fmt.Errorf("invalid message length: missing distance(1 byte), metric(4 bytes) or nexthops(1 byte): %d<6", len(data[offset:]))
+ }
+ b.Distance = data[offset]
+ offset += 1
+ } else if len(data[offset:]) < 5 {
+ return fmt.Errorf("invalid message length: missing metric(4 bytes) or nexthops(1 byte): %d<5", len(data[offset:]))
+ }
+ b.Metric = binary.BigEndian.Uint32(data[offset : offset+4])
+ offset += 4
+
+ // List of Nexthops
+ b.Nexthops = []*Nexthop{}
+ if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[offset:], isV4, version); err != nil {
+ return err
+ } else {
+ offset += nexthopsByteLen
+ }
+
+ return nil
+}
+
+func (b *NexthopUpdateBody) String() string {
+ s := fmt.Sprintf(
+ "family: %d, prefix: %s, distance: %d, metric: %d",
+ b.Family, b.Prefix.String(), b.Distance, b.Metric)
+ for _, nh := range b.Nexthops {
+ s = s + fmt.Sprintf(", nexthop:{%s}", nh.String())
+ }
+ return s
+}
+
+type Message struct {
+ Header Header
+ Body Body
+}
+
+func (m *Message) Serialize() ([]byte, error) {
+ var body []byte
+ if m.Body != nil {
+ var err error
+ body, err = m.Body.Serialize(m.Header.Version)
+ if err != nil {
+ return nil, err
+ }
+ }
+ m.Header.Len = uint16(len(body)) + HeaderSize(m.Header.Version)
+ hdr, err := m.Header.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ return append(hdr, body...), nil
+}
+
+func (m *Message) parseMessage(data []byte) error {
+ switch m.Header.Command {
+ case INTERFACE_ADD, INTERFACE_DELETE, INTERFACE_UP, INTERFACE_DOWN:
+ m.Body = &InterfaceUpdateBody{}
+ case INTERFACE_ADDRESS_ADD, INTERFACE_ADDRESS_DELETE:
+ m.Body = &InterfaceAddressUpdateBody{}
+ case ROUTER_ID_UPDATE:
+ m.Body = &RouterIDUpdateBody{}
+ case IPV4_ROUTE_ADD, IPV6_ROUTE_ADD, IPV4_ROUTE_DELETE, IPV6_ROUTE_DELETE:
+ m.Body = &IPRouteBody{Api: m.Header.Command}
+ case IPV4_NEXTHOP_LOOKUP, IPV6_NEXTHOP_LOOKUP:
+ m.Body = &NexthopLookupBody{Api: m.Header.Command}
+ case IPV4_IMPORT_LOOKUP:
+ m.Body = &ImportLookupBody{Api: m.Header.Command}
+ case NEXTHOP_UPDATE:
+ m.Body = &NexthopUpdateBody{Api: m.Header.Command}
+ default:
+ m.Body = &UnknownBody{}
+ }
+ return m.Body.DecodeFromBytes(data, m.Header.Version)
+}
+
+func (m *Message) parseFrrMessage(data []byte) error {
+ switch m.Header.Command {
+ case FRR_INTERFACE_ADD, FRR_INTERFACE_DELETE, FRR_INTERFACE_UP, FRR_INTERFACE_DOWN:
+ m.Body = &InterfaceUpdateBody{}
+ case FRR_INTERFACE_ADDRESS_ADD, FRR_INTERFACE_ADDRESS_DELETE:
+ m.Body = &InterfaceAddressUpdateBody{}
+ case FRR_ROUTER_ID_UPDATE:
+ m.Body = &RouterIDUpdateBody{}
+ case FRR_NEXTHOP_UPDATE:
+ m.Body = &NexthopUpdateBody{}
+ case FRR_INTERFACE_NBR_ADDRESS_ADD, FRR_INTERFACE_NBR_ADDRESS_DELETE:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_INTERFACE_BFD_DEST_UPDATE:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_IMPORT_CHECK_UPDATE:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_BFD_DEST_REPLAY:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_REDISTRIBUTE_IPV4_ADD, FRR_REDISTRIBUTE_IPV4_DEL, FRR_REDISTRIBUTE_IPV6_ADD, FRR_REDISTRIBUTE_IPV6_DEL:
+ m.Body = &IPRouteBody{Api: m.Header.Command}
+ case FRR_INTERFACE_VRF_UPDATE:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_INTERFACE_LINK_PARAMS:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_PW_STATUS_UPDATE:
+ // TODO
+ m.Body = &UnknownBody{}
+ default:
+ m.Body = &UnknownBody{}
+ }
+ return m.Body.DecodeFromBytes(data, m.Header.Version)
+}
+
+func ParseMessage(hdr *Header, data []byte) (m *Message, err error) {
+ m = &Message{Header: *hdr}
+ if m.Header.Version == 4 {
+ err = m.parseFrrMessage(data)
+ } else {
+ err = m.parseMessage(data)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return m, nil
+}
diff --git a/internal/pkg/zebra/zapi_bsd.go b/internal/pkg/zebra/zapi_bsd.go
new file mode 100644
index 00000000..8960e796
--- /dev/null
+++ b/internal/pkg/zebra/zapi_bsd.go
@@ -0,0 +1,58 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build freebsd netbsd openbsd
+
+package zebra
+
+import (
+ "strings"
+ "syscall"
+)
+
+func intfflag2string(flag uint64) string {
+ ss := make([]string, 0, 10)
+ if flag&syscall.IFF_UP > 0 {
+ ss = append(ss, "UP")
+ }
+ if flag&syscall.IFF_BROADCAST > 0 {
+ ss = append(ss, "BROADCAST")
+ }
+ if flag&syscall.IFF_DEBUG > 0 {
+ ss = append(ss, "DEBUG")
+ }
+ if flag&syscall.IFF_LOOPBACK > 0 {
+ ss = append(ss, "LOOPBACK")
+ }
+ if flag&syscall.IFF_POINTOPOINT > 0 {
+ ss = append(ss, "POINTOPOINT")
+ }
+ if flag&syscall.IFF_RUNNING > 0 {
+ ss = append(ss, "RUNNING")
+ }
+ if flag&syscall.IFF_NOARP > 0 {
+ ss = append(ss, "NOARP")
+ }
+ if flag&syscall.IFF_PROMISC > 0 {
+ ss = append(ss, "PROMISC")
+ }
+ if flag&syscall.IFF_ALLMULTI > 0 {
+ ss = append(ss, "ALLMULTI")
+ }
+ if flag&syscall.IFF_MULTICAST > 0 {
+ ss = append(ss, "MULTICAST")
+ }
+ return strings.Join(ss, " | ")
+}
diff --git a/internal/pkg/zebra/zapi_darwin.go b/internal/pkg/zebra/zapi_darwin.go
new file mode 100644
index 00000000..a2536913
--- /dev/null
+++ b/internal/pkg/zebra/zapi_darwin.go
@@ -0,0 +1,59 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "strings"
+ "syscall"
+)
+
+func intfflag2string(flag uint64) string {
+ ss := make([]string, 0, 10)
+ if flag&syscall.IFF_UP > 0 {
+ ss = append(ss, "UP")
+ }
+ if flag&syscall.IFF_BROADCAST > 0 {
+ ss = append(ss, "BROADCAST")
+ }
+ if flag&syscall.IFF_DEBUG > 0 {
+ ss = append(ss, "DEBUG")
+ }
+ if flag&syscall.IFF_LOOPBACK > 0 {
+ ss = append(ss, "LOOPBACK")
+ }
+ if flag&syscall.IFF_POINTOPOINT > 0 {
+ ss = append(ss, "POINTOPOINT")
+ }
+ if flag&syscall.IFF_NOTRAILERS > 0 {
+ ss = append(ss, "NOTRAILERS")
+ }
+ if flag&syscall.IFF_RUNNING > 0 {
+ ss = append(ss, "RUNNING")
+ }
+ if flag&syscall.IFF_NOARP > 0 {
+ ss = append(ss, "NOARP")
+ }
+ if flag&syscall.IFF_PROMISC > 0 {
+ ss = append(ss, "PROMISC")
+ }
+ if flag&syscall.IFF_ALLMULTI > 0 {
+ ss = append(ss, "ALLMULTI")
+ }
+ if flag&syscall.IFF_MULTICAST > 0 {
+ ss = append(ss, "MULTICAST")
+ }
+ return strings.Join(ss, " | ")
+}
diff --git a/internal/pkg/zebra/zapi_linux.go b/internal/pkg/zebra/zapi_linux.go
new file mode 100644
index 00000000..66fccb74
--- /dev/null
+++ b/internal/pkg/zebra/zapi_linux.go
@@ -0,0 +1,83 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "strings"
+ "syscall"
+)
+
+func intfflag2string(flag uint64) string {
+ ss := make([]string, 0, 10)
+ if flag&syscall.IFF_UP > 0 {
+ ss = append(ss, "UP")
+ }
+ if flag&syscall.IFF_BROADCAST > 0 {
+ ss = append(ss, "BROADCAST")
+ }
+ if flag&syscall.IFF_DEBUG > 0 {
+ ss = append(ss, "DEBUG")
+ }
+ if flag&syscall.IFF_LOOPBACK > 0 {
+ ss = append(ss, "LOOPBACK")
+ }
+ if flag&syscall.IFF_POINTOPOINT > 0 {
+ ss = append(ss, "POINTOPOINT")
+ }
+ if flag&syscall.IFF_NOTRAILERS > 0 {
+ ss = append(ss, "NOTRAILERS")
+ }
+ if flag&syscall.IFF_RUNNING > 0 {
+ ss = append(ss, "RUNNING")
+ }
+ if flag&syscall.IFF_NOARP > 0 {
+ ss = append(ss, "NOARP")
+ }
+ if flag&syscall.IFF_PROMISC > 0 {
+ ss = append(ss, "PROMISC")
+ }
+ if flag&syscall.IFF_ALLMULTI > 0 {
+ ss = append(ss, "ALLMULTI")
+ }
+ if flag&syscall.IFF_MASTER > 0 {
+ ss = append(ss, "MASTER")
+ }
+ if flag&syscall.IFF_SLAVE > 0 {
+ ss = append(ss, "SLAVE")
+ }
+ if flag&syscall.IFF_MULTICAST > 0 {
+ ss = append(ss, "MULTICAST")
+ }
+ if flag&syscall.IFF_PORTSEL > 0 {
+ ss = append(ss, "PORTSEL")
+ }
+ if flag&syscall.IFF_AUTOMEDIA > 0 {
+ ss = append(ss, "AUTOMEDIA")
+ }
+ if flag&syscall.IFF_DYNAMIC > 0 {
+ ss = append(ss, "DYNAMIC")
+ }
+ // if flag&syscall.IFF_LOWER_UP > 0 {
+ // ss = append(ss, "LOWER_UP")
+ // }
+ // if flag&syscall.IFF_DORMANT > 0 {
+ // ss = append(ss, "DORMANT")
+ // }
+ // if flag&syscall.IFF_ECHO > 0 {
+ // ss = append(ss, "ECHO")
+ // }
+ return strings.Join(ss, " | ")
+}
diff --git a/internal/pkg/zebra/zapi_test.go b/internal/pkg/zebra/zapi_test.go
new file mode 100644
index 00000000..9fda5416
--- /dev/null
+++ b/internal/pkg/zebra/zapi_test.go
@@ -0,0 +1,531 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "encoding/binary"
+ "net"
+ "syscall"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_Header(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ buf := make([]byte, 6)
+ binary.BigEndian.PutUint16(buf[0:], 10)
+ buf[2] = HEADER_MARKER
+ buf[3] = 2
+ binary.BigEndian.PutUint16(buf[4:], uint16(IPV4_ROUTE_ADD))
+ h := &Header{}
+ err := h.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+
+ //Serialize
+ buf, err = h.Serialize()
+ assert.Equal(nil, err)
+ h2 := &Header{}
+ err = h2.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal(h, h2)
+
+ // header_size mismatch
+ buf = make([]byte, HeaderSize(2)-1)
+ binary.BigEndian.PutUint16(buf[0:], 10)
+ buf[2] = 0xff
+ buf[3] = 0x02
+ h3 := &Header{}
+ err = h3.DecodeFromBytes(buf)
+ assert.NotEqual(nil, err)
+}
+
+func Test_InterfaceUpdateBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ buf := make([]byte, INTERFACE_NAMSIZ+49)
+ pos := INTERFACE_NAMSIZ
+ binary.BigEndian.PutUint32(buf[pos:], 1)
+ pos += 4
+ buf[pos] = byte(INTERFACE_ACTIVE)
+ pos += 1
+ binary.BigEndian.PutUint64(buf[pos:], 1)
+ pos += 8 // flags
+ binary.BigEndian.PutUint32(buf[pos:], 1)
+ pos += 4 // metric
+ binary.BigEndian.PutUint32(buf[pos:], 1500)
+ pos += 4 // MTU
+ binary.BigEndian.PutUint32(buf[pos:], 1500)
+ pos += 4 // MTU6
+ binary.BigEndian.PutUint32(buf[pos:], 200)
+ pos += 4 // bandwidth
+ binary.BigEndian.PutUint32(buf[pos:], 6)
+ pos += 4 // hwaddr_len
+ mac, _ := net.ParseMAC("01:23:45:67:89:ab")
+ copy(buf[pos:pos+6], []byte(mac))
+ pos += 4
+ b := &InterfaceUpdateBody{}
+ err := b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("01:23:45:67:89:ab", b.HardwareAddr.String())
+
+ buf = make([]byte, INTERFACE_NAMSIZ+28)
+ b = &InterfaceUpdateBody{}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_InterfaceAddressUpdateBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ buf := make([]byte, 15)
+ pos := 0
+ binary.BigEndian.PutUint32(buf[pos:], 0) // index
+ pos += 4
+ buf[pos] = 0x01 // flags
+ pos += 1
+ buf[pos] = 0x2 // family
+ pos += 1
+ ip := net.ParseIP("192.168.100.1").To4() // prefix
+ copy(buf[pos:pos+4], []byte(ip))
+ pos += 4
+ buf[pos] = byte(24) // prefix len
+ pos += 1
+ dst := net.ParseIP("192.168.100.255").To4() // destination
+ copy(buf[pos:pos+4], []byte(dst))
+
+ b := &InterfaceAddressUpdateBody{}
+ err := b.DecodeFromBytes(buf, 2)
+ require.NoError(t, err)
+
+ assert.Equal(uint32(0), b.Index)
+ assert.Equal(INTERFACE_ADDRESS_FLAG(1), b.Flags)
+ assert.Equal("192.168.100.1", b.Prefix.String())
+ assert.Equal(uint8(24), b.Length)
+ assert.Equal("192.168.100.255", b.Destination.String())
+
+ // af invalid
+ buf[5] = 0x4
+ pos += 1
+ b = &InterfaceAddressUpdateBody{}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_RouterIDUpdateBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ buf := make([]byte, 6)
+ pos := 0
+ buf[pos] = 0x2
+ pos += 1
+ ip := net.ParseIP("192.168.100.1").To4()
+ copy(buf[pos:pos+4], []byte(ip))
+ pos += 4
+ buf[pos] = byte(32)
+
+ b := &RouterIDUpdateBody{}
+ err := b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("192.168.100.1", b.Prefix.String())
+ assert.Equal(uint8(32), b.Length)
+
+ // af invalid
+ buf[0] = 0x4
+ pos += 1
+ b = &RouterIDUpdateBody{}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_IPRouteBody_IPv4(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes IPV4_ROUTE
+ buf := make([]byte, 26)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_NEXTHOP | MESSAGE_DISTANCE | MESSAGE_METRIC | MESSAGE_MTU)
+ buf[3] = 24
+ ip := net.ParseIP("192.168.100.0").To4()
+ copy(buf[4:7], []byte(ip))
+
+ buf[7] = 1
+ nexthop := net.ParseIP("0.0.0.0").To4()
+ copy(buf[8:12], []byte(nexthop))
+
+ buf[12] = 1
+ binary.BigEndian.PutUint32(buf[13:], 1)
+ buf[17] = 0 // distance
+ binary.BigEndian.PutUint32(buf[18:], 1)
+ binary.BigEndian.PutUint32(buf[22:], 1)
+ r := &IPRouteBody{Api: IPV4_ROUTE_ADD}
+ err := r.DecodeFromBytes(buf, 2)
+
+ assert.Equal(nil, err)
+ assert.Equal("192.168.100.0", r.Prefix.String())
+ assert.Equal(uint8(0x18), r.PrefixLength)
+ assert.Equal(MESSAGE_NEXTHOP|MESSAGE_DISTANCE|MESSAGE_METRIC|MESSAGE_MTU, r.Message)
+ assert.Equal("0.0.0.0", r.Nexthops[0].String())
+ assert.Equal(uint32(1), r.Ifindexs[0])
+ assert.Equal(uint8(0), r.Distance)
+ assert.Equal(uint32(1), r.Metric)
+ assert.Equal(uint32(1), r.Mtu)
+
+ //Serialize
+ buf, err = r.Serialize(2)
+ assert.Equal(nil, err)
+ assert.Equal([]byte{0x2, 0x10, 0x1d}, buf[0:3])
+ assert.Equal([]byte{0x0, 0x1}, buf[3:5])
+ assert.Equal(byte(24), buf[5])
+ ip = net.ParseIP("192.168.100.0").To4()
+ assert.Equal([]byte(ip)[0:3], buf[6:9])
+ assert.Equal(byte(NEXTHOP_IPV4), buf[10])
+ assert.Equal(byte(NEXTHOP_IFINDEX), buf[15])
+ assert.Equal(byte(0x0), buf[20])
+
+ bi := make([]byte, 4)
+ binary.BigEndian.PutUint32(bi, 1)
+ assert.Equal(bi, buf[21:25])
+ assert.Equal(bi, buf[25:])
+
+ // length invalid
+ buf = make([]byte, 18)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_NEXTHOP | MESSAGE_DISTANCE | MESSAGE_METRIC)
+ buf[3] = 24
+ ip = net.ParseIP("192.168.100.0").To4()
+ copy(buf[4:7], []byte(ip))
+ buf[7] = 1
+ nexthop = net.ParseIP("0.0.0.0").To4()
+ copy(buf[8:12], []byte(nexthop))
+ buf[12] = 1
+ binary.BigEndian.PutUint32(buf[13:], 1)
+
+ r = &IPRouteBody{Api: IPV4_ROUTE_ADD}
+ err = r.DecodeFromBytes(buf, 2)
+ assert.Equal("message length invalid", err.Error())
+
+ // no nexthop
+ buf = make([]byte, 12)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_DISTANCE | MESSAGE_METRIC)
+ buf[3] = 24
+ ip = net.ParseIP("192.168.100.0").To4()
+ copy(buf[4:7], []byte(ip))
+ buf[7] = 1
+ binary.BigEndian.PutUint32(buf[8:], 0)
+ r = &IPRouteBody{Api: IPV4_ROUTE_ADD}
+ err = r.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+
+}
+
+func Test_IPRouteBody_IPv6(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes IPV6_ROUTE
+ buf := make([]byte, 43)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_NEXTHOP | MESSAGE_DISTANCE | MESSAGE_METRIC | MESSAGE_MTU)
+ buf[3] = 64
+ ip := net.ParseIP("2001:db8:0:f101::").To16()
+ copy(buf[4:12], []byte(ip))
+
+ buf[12] = 1
+ nexthop := net.ParseIP("::").To16()
+ copy(buf[13:29], []byte(nexthop))
+ // ifindex
+ buf[29] = 1
+ binary.BigEndian.PutUint32(buf[30:], 1)
+
+ buf[34] = 0 // distance
+ binary.BigEndian.PutUint32(buf[35:], 1)
+ binary.BigEndian.PutUint32(buf[39:], 1)
+ r := &IPRouteBody{Api: IPV6_ROUTE_ADD}
+ err := r.DecodeFromBytes(buf, 2)
+
+ assert.Equal(nil, err)
+ assert.Equal("2001:db8:0:f101::", r.Prefix.String())
+ assert.Equal(uint8(64), r.PrefixLength)
+ assert.Equal(MESSAGE_NEXTHOP|MESSAGE_DISTANCE|MESSAGE_METRIC|MESSAGE_MTU, r.Message)
+ assert.Equal("::", r.Nexthops[0].String())
+ assert.Equal(uint32(1), r.Ifindexs[0])
+ assert.Equal(uint8(0), r.Distance)
+ assert.Equal(uint32(1), r.Metric)
+ assert.Equal(uint32(1), r.Mtu)
+
+ //Serialize
+ buf, err = r.Serialize(2)
+ assert.Equal(nil, err)
+ assert.Equal([]byte{0x2, 0x10, 0x1d}, buf[0:3])
+ assert.Equal([]byte{0x0, 0x1}, buf[3:5])
+ assert.Equal(byte(64), buf[5])
+ ip = net.ParseIP("2001:db8:0:f101::").To16()
+ assert.Equal([]byte(ip)[0:8], buf[6:14])
+ assert.Equal(byte(2), buf[14])
+ assert.Equal(byte(NEXTHOP_IPV6), buf[15])
+ ip = net.ParseIP("::").To16()
+ assert.Equal([]byte(ip), buf[16:32])
+ assert.Equal(byte(NEXTHOP_IFINDEX), buf[32])
+ bi := make([]byte, 4)
+ binary.BigEndian.PutUint32(bi, 1)
+ assert.Equal(bi, buf[33:37])
+
+ //distance
+ assert.Equal(byte(0), buf[37])
+ bi = make([]byte, 4)
+ binary.BigEndian.PutUint32(bi, 1)
+ assert.Equal(bi, buf[38:42])
+ assert.Equal(bi, buf[42:])
+
+ // length invalid
+ buf = make([]byte, 50)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_NEXTHOP | MESSAGE_DISTANCE | MESSAGE_METRIC)
+ buf[3] = 24
+ ip = net.ParseIP("2001:db8:0:f101::").To4()
+ copy(buf[4:12], []byte(ip))
+ buf[13] = 1
+ nexthop = net.ParseIP("::").To16()
+ copy(buf[14:30], []byte(nexthop))
+ buf[31] = 1
+ binary.BigEndian.PutUint32(buf[32:], 1)
+
+ r = &IPRouteBody{Api: IPV6_ROUTE_ADD}
+ err = r.DecodeFromBytes(buf, 2)
+ assert.Equal("message length invalid", err.Error())
+
+ // no nexthop
+ buf = make([]byte, 11)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_DISTANCE | MESSAGE_METRIC)
+ buf[3] = 16
+ ip = net.ParseIP("2501::").To16()
+ copy(buf[4:6], []byte(ip))
+ buf[6] = 1
+ binary.BigEndian.PutUint32(buf[7:], 0)
+ r = &IPRouteBody{Api: IPV6_ROUTE_ADD}
+ err = r.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+}
+
+func Test_NexthopLookupBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //ipv4
+ //DecodeFromBytes
+ pos := 0
+ buf := make([]byte, 18)
+ ip := net.ParseIP("192.168.50.0").To4()
+ copy(buf[0:4], []byte(ip))
+ pos += 4
+ binary.BigEndian.PutUint32(buf[pos:], 10)
+ pos += 4
+ buf[pos] = byte(1)
+ pos += 1
+ buf[pos] = byte(4)
+ pos += 1
+ ip = net.ParseIP("172.16.1.101").To4()
+ copy(buf[pos:pos+4], []byte(ip))
+ pos += 4
+ binary.BigEndian.PutUint32(buf[pos:], 3)
+
+ b := &NexthopLookupBody{Api: IPV4_NEXTHOP_LOOKUP}
+ err := b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("192.168.50.0", b.Addr.String())
+ assert.Equal(uint32(10), b.Metric)
+ assert.Equal(uint32(3), b.Nexthops[0].Ifindex)
+ assert.Equal(NEXTHOP_FLAG(4), b.Nexthops[0].Type)
+ assert.Equal("172.16.1.101", b.Nexthops[0].Addr.String())
+
+ //Serialize
+ buf, err = b.Serialize(2)
+ ip = net.ParseIP("192.168.50.0").To4()
+ assert.Equal(nil, err)
+ assert.Equal([]byte(ip)[0:4], buf[0:4])
+
+ // length invalid
+ buf = make([]byte, 3)
+ b = &NexthopLookupBody{Api: IPV4_NEXTHOP_LOOKUP}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+
+ //ipv6
+ //DecodeFromBytes
+ pos = 0
+ buf = make([]byte, 46)
+ ip = net.ParseIP("2001:db8:0:f101::").To16()
+ copy(buf[0:16], []byte(ip))
+ pos += 16
+ binary.BigEndian.PutUint32(buf[pos:], 10)
+ pos += 4
+ buf[pos] = byte(1)
+ pos += 1
+ buf[pos] = byte(4)
+ pos += 1
+ ip = net.ParseIP("2001:db8:0:1111::1").To16()
+ copy(buf[pos:pos+16], []byte(ip))
+ pos += 16
+ binary.BigEndian.PutUint32(buf[pos:], 3)
+
+ b = &NexthopLookupBody{Api: IPV6_NEXTHOP_LOOKUP}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("2001:db8:0:f101::", b.Addr.String())
+ assert.Equal(uint32(10), b.Metric)
+ assert.Equal(uint32(3), b.Nexthops[0].Ifindex)
+ assert.Equal(NEXTHOP_FLAG(4), b.Nexthops[0].Type)
+ assert.Equal("2001:db8:0:1111::1", b.Nexthops[0].Addr.String())
+
+ //Serialize
+ buf, err = b.Serialize(2)
+ ip = net.ParseIP("2001:db8:0:f101::").To16()
+ assert.Equal(nil, err)
+ assert.Equal([]byte(ip)[0:16], buf[0:16])
+
+ // length invalid
+ buf = make([]byte, 15)
+ b = &NexthopLookupBody{Api: IPV6_NEXTHOP_LOOKUP}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_ImportLookupBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ pos := 0
+ buf := make([]byte, 18)
+ ip := net.ParseIP("192.168.50.0").To4()
+ copy(buf[0:4], []byte(ip))
+ pos += 4
+ binary.BigEndian.PutUint32(buf[pos:], 10)
+ pos += 4
+ buf[pos] = byte(1)
+ pos += 1
+ buf[pos] = byte(4)
+ pos += 1
+ ip = net.ParseIP("172.16.1.101").To4()
+ copy(buf[pos:pos+4], []byte(ip))
+ pos += 4
+ binary.BigEndian.PutUint32(buf[pos:], 3)
+
+ b := &ImportLookupBody{Api: IPV4_IMPORT_LOOKUP}
+ err := b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("192.168.50.0", b.Addr.String())
+ assert.Equal(uint32(10), b.Metric)
+ assert.Equal(uint32(3), b.Nexthops[0].Ifindex)
+ assert.Equal(NEXTHOP_FLAG(4), b.Nexthops[0].Type)
+ assert.Equal("172.16.1.101", b.Nexthops[0].Addr.String())
+
+ //Serialize
+ b.PrefixLength = uint8(24)
+ buf, err = b.Serialize(2)
+ ip = net.ParseIP("192.168.50.0").To4()
+ assert.Equal(nil, err)
+ assert.Equal(uint8(24), buf[0])
+ assert.Equal([]byte(ip)[0:4], buf[1:5])
+
+ // length invalid
+ buf = make([]byte, 3)
+ b = &ImportLookupBody{Api: IPV4_IMPORT_LOOKUP}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_NexthopRegisterBody(t *testing.T) {
+ assert := assert.New(t)
+
+ // Input binary
+ bufIn := []byte{
+ 0x01, 0x00, 0x02, 0x20, // connected(1 byte)=1, afi(2 bytes)=AF_INET, prefix_len(1 byte)=32
+ 0xc0, 0xa8, 0x01, 0x01, // prefix(4 bytes)="192.168.1.1"
+ 0x00, 0x00, 0x0a, 0x80, // connected(1 byte)=0, afi(2 bytes)=AF_INET6, prefix_len(1 byte)=128
+ 0x20, 0x01, 0x0d, 0xb8, // prefix(16 bytes)="2001:db8:1:1::1"
+ 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ }
+ binary.BigEndian.PutUint16(bufIn[1:], syscall.AF_INET)
+ binary.BigEndian.PutUint16(bufIn[9:], syscall.AF_INET6)
+
+ // Test DecodeFromBytes()
+ b := &NexthopRegisterBody{Api: NEXTHOP_REGISTER}
+ err := b.DecodeFromBytes(bufIn, 3)
+ assert.Nil(err)
+
+ // Test decoded values
+ assert.Equal(uint8(1), b.Nexthops[0].Connected)
+ assert.Equal(uint16(syscall.AF_INET), b.Nexthops[0].Family)
+ assert.Equal(net.ParseIP("192.168.1.1").To4(), b.Nexthops[0].Prefix)
+ assert.Equal(uint8(0), b.Nexthops[1].Connected)
+ assert.Equal(uint16(syscall.AF_INET6), b.Nexthops[1].Family)
+ assert.Equal(net.ParseIP("2001:db8:1:1::1").To16(), b.Nexthops[1].Prefix)
+
+ // Test Serialize()
+ bufOut, err := b.Serialize(3)
+ assert.Nil(err)
+
+ // Test serialised value
+ assert.Equal(bufIn, bufOut)
+}
+
+func Test_NexthopUpdateBody(t *testing.T) {
+ assert := assert.New(t)
+
+ // Input binary
+ bufIn := []byte{
+ 0x00, 0x02, 0x20, // afi(2 bytes)=AF_INET, prefix_len(1 byte)=32
+ 0xc0, 0xa8, 0x01, 0x01, // prefix(4 bytes)="192.168.1.1"
+ 0x00, 0x00, 0x00, 0x01, // metric(4 bytes)=1
+ 0x01, // nexthops(1 byte)=1
+ 0x04, // nexthop_type(1 byte)=NEXTHOP_IPV4_IFINDEX
+ 0xc0, 0xa8, 0x01, 0x01, // nexthop_ip(4 bytes)="192.168.0.1"
+ 0x00, 0x00, 0x00, 0x02, // nexthop_ifindex(4 byte)=2
+ }
+
+ // Test DecodeFromBytes()
+ b := &NexthopUpdateBody{Api: NEXTHOP_UPDATE}
+ err := b.DecodeFromBytes(bufIn, 2)
+ assert.Nil(err)
+
+ // Test decoded values
+ assert.Equal(uint16(syscall.AF_INET), b.Family)
+ assert.Equal(net.ParseIP("192.168.1.1").To4(), b.Prefix)
+ assert.Equal(uint32(1), b.Metric)
+ nexthop := &Nexthop{
+ Type: NEXTHOP_FLAG(NEXTHOP_IPV4_IFINDEX),
+ Addr: net.ParseIP("192.168.1.1").To4(),
+ Ifindex: uint32(2),
+ }
+ assert.Equal(1, len(b.Nexthops))
+ assert.Equal(nexthop, b.Nexthops[0])
+}
diff --git a/internal/pkg/zebra/zapi_windows.go b/internal/pkg/zebra/zapi_windows.go
new file mode 100644
index 00000000..d55525db
--- /dev/null
+++ b/internal/pkg/zebra/zapi_windows.go
@@ -0,0 +1,38 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "strings"
+ "syscall"
+)
+
+func intfflag2string(flag uint64) string {
+ ss := make([]string, 0, 10)
+ if flag&syscall.IFF_UP > 0 {
+ ss = append(ss, "UP")
+ }
+ if flag&syscall.IFF_BROADCAST > 0 {
+ ss = append(ss, "BROADCAST")
+ }
+ if flag&syscall.IFF_LOOPBACK > 0 {
+ ss = append(ss, "LOOPBACK")
+ }
+ if flag&syscall.IFF_MULTICAST > 0 {
+ ss = append(ss, "MULTICAST")
+ }
+ return strings.Join(ss, " | ")
+}