diff options
Diffstat (limited to 'internal/pkg/zebra')
-rw-r--r-- | internal/pkg/zebra/afi_string.go | 17 | ||||
-rw-r--r-- | internal/pkg/zebra/api_type_string.go | 16 | ||||
-rw-r--r-- | internal/pkg/zebra/link_type_string.go | 16 | ||||
-rw-r--r-- | internal/pkg/zebra/nexthop_flag_string.go | 17 | ||||
-rw-r--r-- | internal/pkg/zebra/ptm_enable_string.go | 16 | ||||
-rw-r--r-- | internal/pkg/zebra/ptm_status_string.go | 16 | ||||
-rw-r--r-- | internal/pkg/zebra/route_type_string.go | 16 | ||||
-rw-r--r-- | internal/pkg/zebra/safi_string.go | 17 | ||||
-rw-r--r-- | internal/pkg/zebra/zapi.go | 1917 | ||||
-rw-r--r-- | internal/pkg/zebra/zapi_bsd.go | 58 | ||||
-rw-r--r-- | internal/pkg/zebra/zapi_darwin.go | 59 | ||||
-rw-r--r-- | internal/pkg/zebra/zapi_linux.go | 83 | ||||
-rw-r--r-- | internal/pkg/zebra/zapi_test.go | 531 | ||||
-rw-r--r-- | internal/pkg/zebra/zapi_windows.go | 38 |
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, " | ") +} |