// 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" "errors" "fmt" "io" "math" "net" "strings" "syscall" log "github.com/sirupsen/logrus" "github.com/osrg/gobgp/pkg/packet/bgp" ) const ( HEADER_MARKER uint8 = 255 FRR_HEADER_MARKER uint8 = 254 INTERFACE_NAMSIZ = 20 ) const ( MinZapiVer uint8 = 2 MaxZapiVer uint8 = 6 ) // 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 const MAXPATH_NUM = 64 const MPLS_MAX_LABEL = 16 func HeaderSize(version uint8) uint16 { switch version { case 3, 4: return 8 case 5, 6: return 10 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 // SAFI definition in Zebra of FRRouting 4.x, 5.x, 6.x const ( _ SAFI = iota FRR_ZAPI5_SAFI_UNICAST FRR_ZAPI5_SAFI_MULTICAST FRR_ZAPI5_SAFI_MPLS_VPN FRR_ZAPI5_SAFI_ENCAP FRR_ZAPI5_SAFI_EVPN FRR_ZAPI5_SAFI_LABELED_UNICAST FRR_ZAPI5_SAFI_FLOWSPEC FRR_ZAPI5_SAFI_MAX ) // SAFI definition in Zebra of Quagga and FRRouting 3.x const ( _ SAFI = iota SAFI_UNICAST SAFI_MULTICAST FRR_SAFI_MPLS_VPN // SAFI_RESERVED_3 in quagga SAFI_MPLS_VPN // SAFI_RESERVED_4 in FRRouting 3.x FRR_SAFI_ENCAP FRR_SAFI_EVPN SAFI_ENCAP // SAFI_MAX in FRRouting 3.x SAFI_MAX ) // API Types. //go:generate stringer -type=API_TYPE type API_TYPE uint16 // For FRRouting version 7. (ZAPI version 6) const ( FRR_ZAPI6_INTERFACE_ADD API_TYPE = iota FRR_ZAPI6_INTERFACE_DELETE FRR_ZAPI6_INTERFACE_ADDRESS_ADD FRR_ZAPI6_INTERFACE_ADDRESS_DELETE FRR_ZAPI6_INTERFACE_UP FRR_ZAPI6_INTERFACE_DOWN FRR_ZAPI6_INTERFACE_SET_MASTER FRR_ZAPI6_ROUTE_ADD FRR_ZAPI6_ROUTE_DELETE FRR_ZAPI6_ROUTE_NOTIFY_OWNER FRR_ZAPI6_REDISTRIBUTE_ADD FRR_ZAPI6_REDISTRIBUTE_DELETE FRR_ZAPI6_REDISTRIBUTE_DEFAULT_ADD FRR_ZAPI6_REDISTRIBUTE_DEFAULT_DELETE FRR_ZAPI6_ROUTER_ID_ADD FRR_ZAPI6_ROUTER_ID_DELETE FRR_ZAPI6_ROUTER_ID_UPDATE FRR_ZAPI6_HELLO FRR_ZAPI6_CAPABILITIES FRR_ZAPI6_NEXTHOP_REGISTER FRR_ZAPI6_NEXTHOP_UNREGISTER FRR_ZAPI6_NEXTHOP_UPDATE FRR_ZAPI6_INTERFACE_NBR_ADDRESS_ADD FRR_ZAPI6_INTERFACE_NBR_ADDRESS_DELETE FRR_ZAPI6_INTERFACE_BFD_DEST_UPDATE FRR_ZAPI6_IMPORT_ROUTE_REGISTER FRR_ZAPI6_IMPORT_ROUTE_UNREGISTER FRR_ZAPI6_IMPORT_CHECK_UPDATE FRR_ZAPI6_BFD_DEST_REGISTER FRR_ZAPI6_BFD_DEST_DEREGISTER FRR_ZAPI6_BFD_DEST_UPDATE FRR_ZAPI6_BFD_DEST_REPLAY FRR_ZAPI6_REDISTRIBUTE_ROUTE_ADD FRR_ZAPI6_REDISTRIBUTE_ROUTE_DEL FRR_ZAPI6_VRF_UNREGISTER FRR_ZAPI6_VRF_ADD FRR_ZAPI6_VRF_DELETE FRR_ZAPI6_VRF_LABEL FRR_ZAPI6_INTERFACE_VRF_UPDATE FRR_ZAPI6_BFD_CLIENT_REGISTER FRR_ZAPI6_BFD_CLIENT_DEREGISTER FRR_ZAPI6_INTERFACE_ENABLE_RADV FRR_ZAPI6_INTERFACE_DISABLE_RADV FRR_ZAPI6_IPV4_NEXTHOP_LOOKUP_MRIB FRR_ZAPI6_INTERFACE_LINK_PARAMS FRR_ZAPI6_MPLS_LABELS_ADD FRR_ZAPI6_MPLS_LABELS_DELETE FRR_ZAPI6_IPMR_ROUTE_STATS FRR_ZAPI6_LABEL_MANAGER_CONNECT FRR_ZAPI6_LABEL_MANAGER_CONNECT_ASYNC FRR_ZAPI6_GET_LABEL_CHUNK FRR_ZAPI6_RELEASE_LABEL_CHUNK FRR_ZAPI6_FEC_REGISTER FRR_ZAPI6_FEC_UNREGISTER FRR_ZAPI6_FEC_UPDATE FRR_ZAPI6_ADVERTISE_DEFAULT_GW FRR_ZAPI6_ADVERTISE_SUBNET FRR_ZAPI6_ADVERTISE_ALL_VNI FRR_ZAPI6_LOCAL_ES_ADD FRR_ZAPI6_LOCAL_ES_DEL FRR_ZAPI6_VNI_ADD FRR_ZAPI6_VNI_DEL FRR_ZAPI6_L3VNI_ADD FRR_ZAPI6_L3VNI_DEL FRR_ZAPI6_REMOTE_VTEP_ADD FRR_ZAPI6_REMOTE_VTEP_DEL FRR_ZAPI6_MACIP_ADD FRR_ZAPI6_MACIP_DEL FRR_ZAPI6_IP_PREFIX_ROUTE_ADD FRR_ZAPI6_IP_PREFIX_ROUTE_DEL FRR_ZAPI6_REMOTE_MACIP_ADD FRR_ZAPI6_REMOTE_MACIP_DEL FRR_ZAPI6_DUPLICATE_ADDR_DETECTION FRR_ZAPI6_PW_ADD FRR_ZAPI6_PW_DELETE FRR_ZAPI6_PW_SET FRR_ZAPI6_PW_UNSET FRR_ZAPI6_PW_STATUS_UPDATE FRR_ZAPI6_RULE_ADD FRR_ZAPI6_RULE_DELETE FRR_ZAPI6_RULE_NOTIFY_OWNER FRR_ZAPI6_TABLE_MANAGER_CONNECT FRR_ZAPI6_GET_TABLE_CHUNK FRR_ZAPI6_RELEASE_TABLE_CHUNK FRR_ZAPI6_IPSET_CREATE FRR_ZAPI6_IPSET_DESTROY FRR_ZAPI6_IPSET_ENTRY_ADD FRR_ZAPI6_IPSET_ENTRY_DELETE FRR_ZAPI6_IPSET_NOTIFY_OWNER FRR_ZAPI6_IPSET_ENTRY_NOTIFY_OWNER FRR_ZAPI6_IPTABLE_ADD FRR_ZAPI6_IPTABLE_DELETE FRR_ZAPI6_IPTABLE_NOTIFY_OWNER FRR_ZAPI6_VXLAN_FLOOD_CONTROL ) // For FRRouting 5. (ZAPI version 5) const ( FRR_ZAPI5_INTERFACE_ADD API_TYPE = iota FRR_ZAPI5_INTERFACE_DELETE FRR_ZAPI5_INTERFACE_ADDRESS_ADD FRR_ZAPI5_INTERFACE_ADDRESS_DELETE FRR_ZAPI5_INTERFACE_UP FRR_ZAPI5_INTERFACE_DOWN FRR_ZAPI5_INTERFACE_SET_MASTER FRR_ZAPI5_ROUTE_ADD FRR_ZAPI5_ROUTE_DELETE FRR_ZAPI5_ROUTE_NOTIFY_OWNER FRR_ZAPI5_IPV4_ROUTE_ADD FRR_ZAPI5_IPV4_ROUTE_DELETE FRR_ZAPI5_IPV6_ROUTE_ADD FRR_ZAPI5_IPV6_ROUTE_DELETE FRR_ZAPI5_REDISTRIBUTE_ADD FRR_ZAPI5_REDISTRIBUTE_DELETE FRR_ZAPI5_REDISTRIBUTE_DEFAULT_ADD FRR_ZAPI5_REDISTRIBUTE_DEFAULT_DELETE FRR_ZAPI5_ROUTER_ID_ADD FRR_ZAPI5_ROUTER_ID_DELETE FRR_ZAPI5_ROUTER_ID_UPDATE FRR_ZAPI5_HELLO FRR_ZAPI5_CAPABILITIES FRR_ZAPI5_NEXTHOP_REGISTER FRR_ZAPI5_NEXTHOP_UNREGISTER FRR_ZAPI5_NEXTHOP_UPDATE FRR_ZAPI5_INTERFACE_NBR_ADDRESS_ADD FRR_ZAPI5_INTERFACE_NBR_ADDRESS_DELETE FRR_ZAPI5_INTERFACE_BFD_DEST_UPDATE FRR_ZAPI5_IMPORT_ROUTE_REGISTER FRR_ZAPI5_IMPORT_ROUTE_UNREGISTER FRR_ZAPI5_IMPORT_CHECK_UPDATE FRR_ZAPI5_IPV4_ROUTE_IPV6_NEXTHOP_ADD FRR_ZAPI5_BFD_DEST_REGISTER FRR_ZAPI5_BFD_DEST_DEREGISTER FRR_ZAPI5_BFD_DEST_UPDATE FRR_ZAPI5_BFD_DEST_REPLAY FRR_ZAPI5_REDISTRIBUTE_ROUTE_ADD FRR_ZAPI5_REDISTRIBUTE_ROUTE_DEL FRR_ZAPI5_VRF_UNREGISTER FRR_ZAPI5_VRF_ADD FRR_ZAPI5_VRF_DELETE FRR_ZAPI5_VRF_LABEL FRR_ZAPI5_INTERFACE_VRF_UPDATE FRR_ZAPI5_BFD_CLIENT_REGISTER FRR_ZAPI5_INTERFACE_ENABLE_RADV FRR_ZAPI5_INTERFACE_DISABLE_RADV FRR_ZAPI5_IPV4_NEXTHOP_LOOKUP_MRIB FRR_ZAPI5_INTERFACE_LINK_PARAMS FRR_ZAPI5_MPLS_LABELS_ADD FRR_ZAPI5_MPLS_LABELS_DELETE FRR_ZAPI5_IPMR_ROUTE_STATS FRR_ZAPI5_LABEL_MANAGER_CONNECT FRR_ZAPI5_LABEL_MANAGER_CONNECT_ASYNC FRR_ZAPI5_GET_LABEL_CHUNK FRR_ZAPI5_RELEASE_LABEL_CHUNK FRR_ZAPI5_FEC_REGISTER FRR_ZAPI5_FEC_UNREGISTER FRR_ZAPI5_FEC_UPDATE FRR_ZAPI5_ADVERTISE_DEFAULT_GW FRR_ZAPI5_ADVERTISE_SUBNET FRR_ZAPI5_ADVERTISE_ALL_VNI FRR_ZAPI5_VNI_ADD FRR_ZAPI5_VNI_DEL FRR_ZAPI5_L3VNI_ADD FRR_ZAPI5_L3VNI_DEL FRR_ZAPI5_REMOTE_VTEP_ADD FRR_ZAPI5_REMOTE_VTEP_DEL FRR_ZAPI5_MACIP_ADD FRR_ZAPI5_MACIP_DEL FRR_ZAPI5_IP_PREFIX_ROUTE_ADD FRR_ZAPI5_IP_PREFIX_ROUTE_DEL FRR_ZAPI5_REMOTE_MACIP_ADD FRR_ZAPI5_REMOTE_MACIP_DEL FRR_ZAPI5_PW_ADD FRR_ZAPI5_PW_DELETE FRR_ZAPI5_PW_SET FRR_ZAPI5_PW_UNSET FRR_ZAPI5_PW_STATUS_UPDATE FRR_ZAPI5_RULE_ADD FRR_ZAPI5_RULE_DELETE FRR_ZAPI5_RULE_NOTIFY_OWNER FRR_ZAPI5_TABLE_MANAGER_CONNECT FRR_ZAPI5_GET_TABLE_CHUNK FRR_ZAPI5_RELEASE_TABLE_CHUNK FRR_ZAPI5_IPSET_CREATE FRR_ZAPI5_IPSET_DESTROY FRR_ZAPI5_IPSET_ENTRY_ADD FRR_ZAPI5_IPSET_ENTRY_DELETE FRR_ZAPI5_IPSET_NOTIFY_OWNER FRR_ZAPI5_IPSET_ENTRY_NOTIFY_OWNER FRR_ZAPI5_IPTABLE_ADD FRR_ZAPI5_IPTABLE_DELETE FRR_ZAPI5_IPTABLE_NOTIFY_OWNER ) // For FRRouting version 3. (ZAPI version 4) 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 ) // 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 ) // Route Types. //go:generate stringer -type=ROUTE_TYPE type ROUTE_TYPE uint8 // For FRRouting version 7 (ZAPI version 6). const ( FRR_ZAPI6_ROUTE_SYSTEM ROUTE_TYPE = iota FRR_ZAPI6_ROUTE_KERNEL FRR_ZAPI6_ROUTE_CONNECT FRR_ZAPI6_ROUTE_STATIC FRR_ZAPI6_ROUTE_RIP FRR_ZAPI6_ROUTE_RIPNG FRR_ZAPI6_ROUTE_OSPF FRR_ZAPI6_ROUTE_OSPF6 FRR_ZAPI6_ROUTE_ISIS FRR_ZAPI6_ROUTE_BGP FRR_ZAPI6_ROUTE_PIM FRR_ZAPI6_ROUTE_EIGRP FRR_ZAPI6_ROUTE_NHRP FRR_ZAPI6_ROUTE_HSLS FRR_ZAPI6_ROUTE_OLSR FRR_ZAPI6_ROUTE_TABLE FRR_ZAPI6_ROUTE_LDP FRR_ZAPI6_ROUTE_VNC FRR_ZAPI6_ROUTE_VNC_DIRECT FRR_ZAPI6_ROUTE_VNC_DIRECT_RH FRR_ZAPI6_ROUTE_BGP_DIRECT FRR_ZAPI6_ROUTE_BGP_DIRECT_EXT FRR_ZAPI6_ROUTE_BABEL FRR_ZAPI6_ROUTE_SHARP FRR_ZAPI6_ROUTE_PBR FRR_ZAPI6_ROUTE_BFD FRR_ZAPI6_ROUTE_OPENFABRIC // FRRRouting version 7 adds. FRR_ZAPI6_ROUTE_ALL FRR_ZAPI6_ROUTE_MAX ) // For FRRouting version 5 (ZAPI version 5). const ( FRR_ZAPI5_ROUTE_SYSTEM ROUTE_TYPE = iota FRR_ZAPI5_ROUTE_KERNEL FRR_ZAPI5_ROUTE_CONNECT FRR_ZAPI5_ROUTE_STATIC FRR_ZAPI5_ROUTE_RIP FRR_ZAPI5_ROUTE_RIPNG FRR_ZAPI5_ROUTE_OSPF FRR_ZAPI5_ROUTE_OSPF6 FRR_ZAPI5_ROUTE_ISIS FRR_ZAPI5_ROUTE_BGP FRR_ZAPI5_ROUTE_PIM FRR_ZAPI5_ROUTE_EIGRP FRR_ZAPI5_ROUTE_NHRP FRR_ZAPI5_ROUTE_HSLS FRR_ZAPI5_ROUTE_OLSR FRR_ZAPI5_ROUTE_TABLE FRR_ZAPI5_ROUTE_LDP FRR_ZAPI5_ROUTE_VNC FRR_ZAPI5_ROUTE_VNC_DIRECT FRR_ZAPI5_ROUTE_VNC_DIRECT_RH FRR_ZAPI5_ROUTE_BGP_DIRECT FRR_ZAPI5_ROUTE_BGP_DIRECT_EXT FRR_ZAPI5_ROUTE_BABEL FRR_ZAPI5_ROUTE_SHARP FRR_ZAPI5_ROUTE_PBR // FRRRouting version 5 adds. FRR_ZAPI5_ROUTE_ALL FRR_ZAPI5_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 ) // 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_NHRP // quagga 1.2.4 ROUTE_MAX ) var routeTypeValueMapFrrZapi6 = map[string]ROUTE_TYPE{ "system": FRR_ZAPI6_ROUTE_SYSTEM, "kernel": FRR_ZAPI6_ROUTE_KERNEL, "connect": FRR_ZAPI6_ROUTE_CONNECT, // hack for backward compatibility "directly-connected": FRR_ZAPI6_ROUTE_CONNECT, "static": FRR_ZAPI6_ROUTE_STATIC, "rip": FRR_ZAPI6_ROUTE_RIP, "ripng": FRR_ZAPI6_ROUTE_RIPNG, "ospf": FRR_ZAPI6_ROUTE_OSPF, "ospf3": FRR_ZAPI6_ROUTE_OSPF6, "isis": FRR_ZAPI6_ROUTE_ISIS, "bgp": FRR_ZAPI6_ROUTE_BGP, "pim": FRR_ZAPI6_ROUTE_PIM, "eigrp": FRR_ZAPI6_ROUTE_EIGRP, "nhrp": FRR_ZAPI6_ROUTE_EIGRP, "hsls": FRR_ZAPI6_ROUTE_HSLS, "olsr": FRR_ZAPI6_ROUTE_OLSR, "table": FRR_ZAPI6_ROUTE_TABLE, "ldp": FRR_ZAPI6_ROUTE_LDP, "vnc": FRR_ZAPI6_ROUTE_VNC, "vnc-direct": FRR_ZAPI6_ROUTE_VNC_DIRECT, "vnc-direct-rh": FRR_ZAPI6_ROUTE_VNC_DIRECT_RH, "bgp-direct": FRR_ZAPI6_ROUTE_BGP_DIRECT, "bgp-direct-ext": FRR_ZAPI6_ROUTE_BGP_DIRECT_EXT, "babel": FRR_ZAPI6_ROUTE_BABEL, "sharp": FRR_ZAPI6_ROUTE_SHARP, "pbr": FRR_ZAPI6_ROUTE_PBR, "bfd": FRR_ZAPI6_ROUTE_BFD, "all": FRR_ZAPI6_ROUTE_ALL, } var routeTypeValueMapFrrZapi5 = map[string]ROUTE_TYPE{ "system": FRR_ZAPI5_ROUTE_SYSTEM, "kernel": FRR_ZAPI5_ROUTE_KERNEL, "connect": FRR_ZAPI5_ROUTE_CONNECT, // hack for backward compatibility "directly-connected": FRR_ZAPI5_ROUTE_CONNECT, "static": FRR_ZAPI5_ROUTE_STATIC, "rip": FRR_ZAPI5_ROUTE_RIP, "ripng": FRR_ZAPI5_ROUTE_RIPNG, "ospf": FRR_ZAPI5_ROUTE_OSPF, "ospf3": FRR_ZAPI5_ROUTE_OSPF6, "isis": FRR_ZAPI5_ROUTE_ISIS, "bgp": FRR_ZAPI5_ROUTE_BGP, "pim": FRR_ZAPI5_ROUTE_PIM, "eigrp": FRR_ZAPI5_ROUTE_EIGRP, "nhrp": FRR_ZAPI5_ROUTE_EIGRP, "hsls": FRR_ZAPI5_ROUTE_HSLS, "olsr": FRR_ZAPI5_ROUTE_OLSR, "table": FRR_ZAPI5_ROUTE_TABLE, "ldp": FRR_ZAPI5_ROUTE_LDP, "vnc": FRR_ZAPI5_ROUTE_VNC, "vnc-direct": FRR_ZAPI5_ROUTE_VNC_DIRECT, "vnc-direct-rh": FRR_ZAPI5_ROUTE_VNC_DIRECT_RH, "bgp-direct": FRR_ZAPI5_ROUTE_BGP_DIRECT, "bgp-direct-ext": FRR_ZAPI5_ROUTE_BGP_DIRECT_EXT, "babel": FRR_ZAPI5_ROUTE_BABEL, "sharp": FRR_ZAPI5_ROUTE_SHARP, "pbr": FRR_ZAPI5_ROUTE_PBR, "all": FRR_ZAPI5_ROUTE_ALL, } var routeTypeValueMapFrr = map[string]ROUTE_TYPE{ "system": FRR_ROUTE_SYSTEM, "kernel": FRR_ROUTE_KERNEL, "connect": FRR_ROUTE_CONNECT, // hack for backward compatibility "directly-connected": FRR_ROUTE_CONNECT, "static": FRR_ROUTE_STATIC, "rip": FRR_ROUTE_RIP, "ripng": FRR_ROUTE_RIPNG, "ospf": FRR_ROUTE_OSPF, "ospf3": FRR_ROUTE_OSPF6, "isis": FRR_ROUTE_ISIS, "bgp": FRR_ROUTE_BGP, "pim": FRR_ROUTE_PIM, "hsls": FRR_ROUTE_HSLS, "olsr": FRR_ROUTE_OLSR, "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, } var routeTypeValueMap = map[string]ROUTE_TYPE{ "system": ROUTE_SYSTEM, "kernel": ROUTE_KERNEL, "connect": ROUTE_CONNECT, // hack for backward 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, "nhrp": ROUTE_NHRP, } func RouteTypeFromString(typ string, version uint8, softwareName string) (ROUTE_TYPE, error) { delegateRouteTypeValueMap := routeTypeValueMap if version == 4 { delegateRouteTypeValueMap = routeTypeValueMapFrr } else if version == 5 { delegateRouteTypeValueMap = routeTypeValueMapFrrZapi5 } else if version >= 6 { delegateRouteTypeValueMap = routeTypeValueMapFrrZapi6 } t, ok := delegateRouteTypeValueMap[typ] if (version == 5 && softwareName == "frr4" && t == FRR_ZAPI5_ROUTE_PBR) || (version == 6 && softwareName == "frr6" && t == FRR_ZAPI6_ROUTE_OPENFABRIC) { ok = false } if ok { return t, nil } return t, fmt.Errorf("unknown route type: %s in version: %d (%s)", typ, version, softwareName) } func addressFamilyFromApi(Api API_TYPE, version uint8) uint8 { //ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB is not implemented. if version <= 3 { switch Api { case IPV4_ROUTE_ADD, IPV4_ROUTE_DELETE, IPV4_NEXTHOP_LOOKUP, IPV4_IMPORT_LOOKUP: return syscall.AF_INET case IPV6_ROUTE_ADD, IPV6_ROUTE_DELETE, IPV6_NEXTHOP_LOOKUP, IPV6_IMPORT_LOOKUP: return syscall.AF_INET6 } } else if version == 4 { switch Api { case FRR_REDISTRIBUTE_IPV4_ADD, FRR_REDISTRIBUTE_IPV4_DEL, FRR_IPV4_ROUTE_ADD, FRR_IPV4_ROUTE_DELETE, FRR_IPV4_NEXTHOP_LOOKUP_MRIB: return syscall.AF_INET case FRR_REDISTRIBUTE_IPV6_ADD, FRR_REDISTRIBUTE_IPV6_DEL, FRR_IPV6_ROUTE_ADD, FRR_IPV6_ROUTE_DELETE: return syscall.AF_INET6 } } else if version == 5 { switch Api { case FRR_ZAPI5_IPV4_ROUTE_ADD, FRR_ZAPI5_IPV4_ROUTE_DELETE: return syscall.AF_INET case FRR_ZAPI5_IPV6_ROUTE_ADD, FRR_ZAPI5_IPV6_ROUTE_DELETE: return syscall.AF_INET6 } } return syscall.AF_UNSPEC } func addressByteLength(family uint8) (int, error) { switch family { case syscall.AF_INET: return net.IPv4len, nil case syscall.AF_INET6: return net.IPv6len, nil } return 0, fmt.Errorf("unknown address family: %d", family) } func ipFromFamily(family uint8, buf []byte) net.IP { switch family { case syscall.AF_INET: return net.IP(buf).To4() case syscall.AF_INET6: return net.IP(buf).To16() } return nil } // API Message Flags. type MESSAGE_FLAG uint8 // For FRRouting version 4, 5 and 6 (ZAPI version 5 and 6). const ( FRR_ZAPI5_MESSAGE_NEXTHOP MESSAGE_FLAG = 0x01 FRR_ZAPI5_MESSAGE_DISTANCE MESSAGE_FLAG = 0x02 FRR_ZAPI5_MESSAGE_METRIC MESSAGE_FLAG = 0x04 FRR_ZAPI5_MESSAGE_TAG MESSAGE_FLAG = 0x08 FRR_ZAPI5_MESSAGE_MTU MESSAGE_FLAG = 0x10 FRR_ZAPI5_MESSAGE_SRCPFX MESSAGE_FLAG = 0x20 FRR_ZAPI5_MESSAGE_LABEL MESSAGE_FLAG = 0x40 FRR_ZAPI5_MESSAGE_TABLEID MESSAGE_FLAG = 0x80 ) // 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 ) // 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(version uint8) string { var ss []string if (version <= 3 && t&MESSAGE_NEXTHOP > 0) || (version == 4 && t&FRR_MESSAGE_NEXTHOP > 0) || (version >= 5 && t&FRR_ZAPI5_MESSAGE_NEXTHOP > 0) { ss = append(ss, "NEXTHOP") } if (version <= 3 && t&MESSAGE_IFINDEX > 0) || (version == 4 && t&FRR_MESSAGE_IFINDEX > 0) { ss = append(ss, "IFINDEX") } if (version <= 3 && t&MESSAGE_DISTANCE > 0) || (version == 4 && t&FRR_MESSAGE_DISTANCE > 0) || (version >= 5 && t&FRR_ZAPI5_MESSAGE_DISTANCE > 0) { ss = append(ss, "DISTANCE") } if (version <= 3 && t&MESSAGE_METRIC > 0) || (version == 4 && t&FRR_MESSAGE_METRIC > 0) || (version >= 5 && t&FRR_ZAPI5_MESSAGE_METRIC > 0) { ss = append(ss, "METRIC") } if (version <= 3 && t&MESSAGE_MTU > 0) || (version == 4 && t&FRR_MESSAGE_MTU > 0) || (version >= 5 && t&FRR_ZAPI5_MESSAGE_MTU > 0) { ss = append(ss, "MTU") } if (version <= 3 && t&MESSAGE_TAG > 0) || (version == 4 && t&FRR_MESSAGE_TAG > 0) || (version >= 5 && t&FRR_ZAPI5_MESSAGE_TAG > 0) { ss = append(ss, "TAG") } if (version == 4 && t&FRR_MESSAGE_SRCPFX > 0) || (version >= 5 && t&FRR_ZAPI5_MESSAGE_SRCPFX > 0) { ss = append(ss, "SRCPFX") } if version >= 5 && t&FRR_ZAPI5_MESSAGE_LABEL > 0 { ss = append(ss, "LABEL") } return strings.Join(ss, "|") } // Message Flags type FLAG uint64 // For FRRouting version 7 (zebra API version 6) const ( FRR_ZAPI6_FLAG_ALLOW_RECURSION FLAG = 0x01 FRR_ZAPI6_FLAG_SELFROUTE FLAG = 0x02 FRR_ZAPI6_FLAG_IBGP FLAG = 0x04 FRR_ZAPI6_FLAG_SELECTED FLAG = 0x08 FRR_ZAPI6_FLAG_FIB_OVERRIDE FLAG = 0x10 FRR_ZAPI6_FLAG_EVPN_ROUTE FLAG = 0x20 FRR_ZAPI6_FLAG_RR_USE_DISTANCE FLAG = 0x40 FRR_ZAPI6_FLAG_ONLINk FLAG = 0x40 ) // For Quagga (ZAPI v2, v3), FRR v3 (ZAPI v4), FRR v4, v5 (ZAPI v5), FRR v6 (ZAPI v6) const ( // Rename ZEBRA_FLAG_INTERNAL -> ZEBRA_FLAG_ALLOW_RECURSION // https://github.com/FRRouting/frr/commit/4e8b02f4df5d6bcfde6390955b8feda2a17dc9bd FLAG_ALLOW_RECURSION FLAG = 0x01 FLAG_SELFROUTE FLAG = 0x02 FLAG_BLACKHOLE FLAG = 0x04 // quagga, frr3 FLAG_IBGP FLAG = 0x08 FLAG_SELECTED FLAG = 0x10 FLAG_CHANGED FLAG = 0x20 FLAG_STATIC FLAG = 0x40 FLAG_REJECT FLAG = 0x80 // quagga, frr3 FLAG_SCOPE_LINK FLAG = 0x100 FLAG_FIB_OVERRIDE FLAG = 0x200 FLAG_EVPN_ROUTE FLAG = 0x400 FLAG_RR_USE_DISTANCE FLAG = 0x800 ) func (t FLAG) String(version uint8, softwareName string) string { if version == 6 && softwareName != "frr6" { return t.stringFrrZapi6() } return t.string() } func (t FLAG) string() string { var ss []string if t&FLAG_ALLOW_RECURSION > 0 { ss = append(ss, "FLAG_ALLOW_RECURSION") } 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") } if t&FLAG_EVPN_ROUTE > 0 { ss = append(ss, "FLAG_EVPN_ROUTE") } if t&FLAG_RR_USE_DISTANCE > 0 { ss = append(ss, "FLAG_RR_USE_DISTANCE") } return strings.Join(ss, "|") } func (t FLAG) stringFrrZapi6() string { var ss []string if t&FRR_ZAPI6_FLAG_ALLOW_RECURSION > 0 { ss = append(ss, "FLAG_ALLOW_RECURSION") } if t&FRR_ZAPI6_FLAG_SELFROUTE > 0 { ss = append(ss, "FLAG_SELFROUTE") } if t&FRR_ZAPI6_FLAG_IBGP > 0 { ss = append(ss, "FLAG_IBGP") } if t&FRR_ZAPI6_FLAG_SELECTED > 0 { ss = append(ss, "FLAG_SELECTED") } if t&FRR_ZAPI6_FLAG_FIB_OVERRIDE > 0 { ss = append(ss, "FLAG_FIB_OVERRIDE") } if t&FRR_ZAPI6_FLAG_EVPN_ROUTE > 0 { ss = append(ss, "FLAG_EVPN_ROUTE") } if t&FRR_ZAPI6_FLAG_RR_USE_DISTANCE > 0 { ss = append(ss, "FLAG_RR_USE_DISTANCE") } if t&FRR_ZAPI6_FLAG_ONLINk > 0 { ss = append(ss, "FLAG_ONLINK") } return strings.Join(ss, "|") } // Nexthop Types. //go:generate stringer -type=NEXTHOP_TYPE type NEXTHOP_TYPE uint8 // For FRRouting. const ( _ NEXTHOP_TYPE = iota FRR_NEXTHOP_TYPE_IFINDEX FRR_NEXTHOP_TYPE_IPV4 FRR_NEXTHOP_TYPE_IPV4_IFINDEX FRR_NEXTHOP_TYPE_IPV6 FRR_NEXTHOP_TYPE_IPV6_IFINDEX FRR_NEXTHOP_TYPE_BLACKHOLE ) // For Quagga. const ( _ NEXTHOP_TYPE = iota NEXTHOP_TYPE_IFINDEX NEXTHOP_TYPE_IFNAME NEXTHOP_TYPE_IPV4 NEXTHOP_TYPE_IPV4_IFINDEX NEXTHOP_TYPE_IPV4_IFNAME NEXTHOP_TYPE_IPV6 NEXTHOP_TYPE_IPV6_IFINDEX NEXTHOP_TYPE_IPV6_IFNAME NEXTHOP_TYPE_BLACKHOLE ) // Nexthop Flags. //go:generate stringer -type=NEXTHOP_FLAG type NEXTHOP_FLAG uint8 const ( NEXTHOP_FLAG_ACTIVE NEXTHOP_FLAG = 0x01 // This nexthop is alive. NEXTHOP_FLAG_FIB NEXTHOP_FLAG = 0x02 // FIB nexthop. NEXTHOP_FLAG_RECURSIVE NEXTHOP_FLAG = 0x04 // Recursive nexthop. NEXTHOP_FLAG_ONLINK NEXTHOP_FLAG = 0x08 // Nexthop should be installed onlink. NEXTHOP_FLAG_MATCHED NEXTHOP_FLAG = 0x10 // Already matched vs a nexthop NEXTHOP_FLAG_FILTERED NEXTHOP_FLAG = 0x20 // rmap filtered (version >= 4) NEXTHOP_FLAG_DUPLICATE NEXTHOP_FLAG = 0x40 // nexthop duplicates (version >= 5) NEXTHOP_FLAG_EVPN_RVTEP NEXTHOP_FLAG = 0x80 // EVPN remote vtep nexthop (version >= 5) ) // 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 SoftwareName string } func NewClient(network, address string, typ ROUTE_TYPE, version uint8, software string) (*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 < MinZapiVer { version = MinZapiVer } else if version > MaxZapiVer { version = MaxZapiVer } isAllowableSoftware := true if ((version == 2 || version == 3) && software != "" && software != "quagga") || (version == 4 && software != "" && software != "frr3") || (version == 5 && software != "" && software != "frr4" && software != "frr5" && software != "cumulus") || (version == 6 && software != "" && software != "frr6" && software != "frr7" && software != "frr7.1") { isAllowableSoftware = false } if !isAllowableSoftware { log.WithFields(log.Fields{ "Topic": "Zebra", }).Warnf("softwareName %s cannot be used with version %d.", software, version) software = "" } c := &Client{ outgoing: outgoing, incoming: incoming, redistDefault: typ, conn: conn, Version: version, SoftwareName: software, } go func() { for { m, more := <-outgoing if more { b, err := m.Serialize(software) 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) closeChannel(outgoing) return } } else { log.Debug("finish outgoing loop") return } } }() // Send HELLO/ROUTER_ID_ADD messages to negotiate the Zebra message version. c.SendHello() c.SendRouterIDAdd() // ZAPIv5 has ZEBRA_LABEL_MANAGER_CONNECT_ASYNC, however frr4 (ZAPIv5) doesn't have it. if version > 4 && software != "frr4" { c.SendLabelManagerConnectAsync() } 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 c.Version != hd.Version { log.WithFields(log.Fields{ "Topic": "Zebra", }).Warnf("ZAPI version mismatch. configured version: %d, version of received message:%d", c.Version, hd.Version) return nil, errors.New("ZAPI version mismatch") } 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, software) 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 closeChannel(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 uint32, body Body) error { marker := HEADER_MARKER if c.Version >= 4 { marker = FRR_HEADER_MARKER } if c.Version == 6 && c.SoftwareName == "frr6" { if frr6Command, err := frr6Zapi6Command(command, c.SoftwareName, false); err == nil { command = frr6Command } else { return err } } else if c.Version == 5 && c.SoftwareName == "frr4" { if frr4Command, err := frr4Zapi5Command(command, c.SoftwareName, false); err == nil { command = frr4Command } else { return err } } else if c.Version == 5 && c.SoftwareName == "cumulus" { if cumulusCommand, err := cumulusZapi5Command(command, c.SoftwareName, false); err == nil { command = cumulusCommand } else { return err } } 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 if c.Version == 4 { command = FRR_HELLO } else if c.Version == 5 { command = FRR_ZAPI5_HELLO } else if c.Version >= 6 { command = FRR_ZAPI6_HELLO } body := &HelloBody{ RedistDefault: c.redistDefault, Instance: 0, } 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 } else if c.Version == 5 { command = FRR_ZAPI5_ROUTER_ID_ADD } else if c.Version >= 6 { command = FRR_ZAPI6_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 } else if c.Version >= 5 { command = FRR_ZAPI5_INTERFACE_ADD } return c.SendCommand(command, VRF_DEFAULT, nil) } func (c *Client) SendRedistribute(t ROUTE_TYPE, vrfId uint32) 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 if c.Version == 5 { command = FRR_ZAPI5_REDISTRIBUTE_ADD } else if c.Version >= 6 { command = FRR_ZAPI6_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 } else if c.Version == 5 { command = FRR_ZAPI5_REDISTRIBUTE_DELETE } else if c.Version >= 6 { command = FRR_ZAPI6_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 uint32, body *IPRouteBody, isWithdraw bool) error { routeFamily := body.RouteFamily(c.Version) if vrfId == VRF_DEFAULT && (routeFamily == bgp.RF_IPv4_VPN || routeFamily == bgp.RF_IPv6_VPN) { return fmt.Errorf("RF_IPv4_VPN or RF_IPv6_VPN are not suitable for VPN_DEFAULT(default forwarding table)") } command := IPV4_ROUTE_ADD if c.Version <= 3 { if body.Prefix.Prefix.To4() != nil { if isWithdraw { command = IPV4_ROUTE_DELETE } } else { if isWithdraw { command = IPV6_ROUTE_DELETE } else { command = IPV6_ROUTE_ADD } } } else if c.Version == 4 { // version >= 4 if body.Prefix.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 } } } else { // version >= 5 (version 6 uses the same value as version 5) if isWithdraw { command = FRR_ZAPI5_ROUTE_DELETE } else { command = FRR_ZAPI5_ROUTE_ADD } } return c.SendCommand(command, vrfId, body) } func (c *Client) SendNexthopRegister(vrfId uint32, 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 if c.Version == 4 { // version == 4 if isWithdraw { command = FRR_NEXTHOP_UNREGISTER } else { command = FRR_NEXTHOP_REGISTER } } else if c.Version == 5 { // version == 5 if isWithdraw { command = FRR_ZAPI5_NEXTHOP_UNREGISTER } else { command = FRR_ZAPI5_NEXTHOP_REGISTER } } else { // version >= 6 if isWithdraw { command = FRR_ZAPI6_NEXTHOP_UNREGISTER } else { command = FRR_ZAPI6_NEXTHOP_REGISTER } } return c.SendCommand(command, vrfId, body) } // Reference: zread_label_manager_connect function in zebra/zserv.c of FRR3.x (ZAPI) // Reference: zread_label_manager_connect function in zebra/zapi_msg.c of FRR5.x and 6.x (ZAPI5 and 6) func (c *Client) SendLabelManagerConnect() error { if c.Version < 4 { return fmt.Errorf("LABEL_MANAGER_CONNECT is not supported in zebra API version: %d", c.Version) } command := FRR_LABEL_MANAGER_CONNECT proto := FRR_ROUTE_BGP if c.Version == 5 { command = FRR_ZAPI5_LABEL_MANAGER_CONNECT proto = FRR_ZAPI5_ROUTE_BGP } else if c.Version == 6 { command = FRR_ZAPI6_LABEL_MANAGER_CONNECT proto = FRR_ZAPI6_ROUTE_BGP } return c.SendCommand( command, 0, &LabelManagerConnectBody{ RedistDefault: proto, Instance: 0, }) } // Reference: zread_label_manager_connect function in zebra/zserv.c of FRR3.x (ZAPI) // Reference: zread_label_manager_connect function in zebra/zapi_msg.c of FRR5.x and 6.x (ZAPI5 and 6) func (c *Client) SendLabelManagerConnectAsync() error { if c.Version < 5 { return fmt.Errorf("LABEL_MANAGER_CONNECT_ASYNC is not supported in zebra API version: %d", c.Version) } // ZAPIv5 has ZEBRA_LABEL_MANAGER_CONNECT_ASYNC, however frr4 (ZAPIv5) doesn't have it. if c.SoftwareName == "frr4" { return fmt.Errorf("LABEL_MANAGER_CONNECT_ASYNC is not supported in software: %s", c.SoftwareName) } command := FRR_ZAPI5_LABEL_MANAGER_CONNECT_ASYNC proto := FRR_ZAPI5_ROUTE_BGP if c.Version == 6 { command = FRR_ZAPI6_LABEL_MANAGER_CONNECT_ASYNC proto = FRR_ZAPI6_ROUTE_BGP } return c.SendCommand( command, 0, &LabelManagerConnectBody{ RedistDefault: proto, Instance: 0, }) } func (c *Client) SendGetLabelChunk(body *GetLabelChunkBody) error { if c.Version < 4 { return fmt.Errorf("GET_LABEL_CHUNK is not supported in version: %d", c.Version) } command := FRR_GET_LABEL_CHUNK body.Instance = 0 if c.Version == 5 { body.Proto = uint8(FRR_ZAPI5_ROUTE_BGP) command = FRR_ZAPI5_GET_LABEL_CHUNK } else if c.Version == 6 { body.Proto = uint8(FRR_ZAPI6_ROUTE_BGP) command = FRR_ZAPI6_GET_LABEL_CHUNK } return c.SendCommand(command, 0, body) } func (c *Client) SendVrfLabel(label uint32, vrfId uint32) error { body := &VrfLabelBody{ Label: label, Afi: AFI_IP, LabelType: LSP_BGP, } command := FRR_ZAPI5_VRF_LABEL if c.Version == 6 { command = FRR_ZAPI6_VRF_LABEL } return c.SendCommand(command, vrfId, body) } // for avoiding double close func closeChannel(ch chan *Message) bool { select { case _, ok := <-ch: if ok { close(ch) return true } default: } return false } func (c *Client) Close() error { closeChannel(c.outgoing) return c.conn.Close() } type Header struct { Len uint16 Marker uint8 Version uint8 VrfId uint32 // ZAPI v4: 16bits, v5: 32bits 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)) case 5, 6: binary.BigEndian.PutUint32(buf[4:8], uint32(h.VrfId)) binary.BigEndian.PutUint16(buf[8:10], 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 = uint32(binary.BigEndian.Uint16(data[4:6])) h.Command = API_TYPE(binary.BigEndian.Uint16(data[6:8])) case 5, 6: h.VrfId = binary.BigEndian.Uint32(data[4:8]) h.Command = API_TYPE(binary.BigEndian.Uint16(data[8:10])) default: return fmt.Errorf("unsupported ZAPI version: %d", h.Version) } return nil } type Body interface { DecodeFromBytes([]byte, uint8, string) error Serialize(uint8, string) ([]byte, error) String(uint8, string) string } type UnknownBody struct { Data []byte } func (b *UnknownBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { b.Data = data return nil } func (b *UnknownBody) Serialize(version uint8, softwareName string) ([]byte, error) { return b.Data, nil } func (b *UnknownBody) String(version uint8, softwareName string) string { return fmt.Sprintf("data: %v", b.Data) } type HelloBody struct { RedistDefault ROUTE_TYPE Instance uint16 ReceiveNotify uint8 } // Reference: zread_hello function in zebra/zserv.c of Quagga1.2.x (ZAPI3) // Reference: zread_hello function in zebra/zserv.c of FRR3.x (ZAPI4) // Reference: zread_hello function in zebra/zapi_msg.c of FRR5.x (ZAPI5) func (b *HelloBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { b.RedistDefault = ROUTE_TYPE(data[0]) if version >= 4 { b.Instance = binary.BigEndian.Uint16(data[1:3]) if version >= 5 { b.ReceiveNotify = data[3] } } return nil } // Reference: zebra_hello_send function in lib/zclient.c of Quagga1.2.x (ZAPI3) // Reference: zebra_hello_send function in lib/zclient.c of FRR3.x (ZAPI4) // Reference: zebra_hello_send function in lib/zclient.c of FRR5.x (ZAPI5) func (b *HelloBody) Serialize(version uint8, softwareName string) ([]byte, error) { if version <= 3 { return []byte{uint8(b.RedistDefault)}, nil } else { // version >= 4 var buf []byte if version == 4 { buf = make([]byte, 3) } else if version >= 5 { buf = make([]byte, 4) } buf[0] = uint8(b.RedistDefault) binary.BigEndian.PutUint16(buf[1:3], b.Instance) if version >= 5 { buf[3] = b.ReceiveNotify } return buf, nil } } func (b *HelloBody) String(version uint8, softwareName 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 } // Reference: zebra_redistribute_add function in zebra/redistribute.c of Quagga1.2.x (ZAPI3) // Reference: zebra_redistribute_add function in zebra/redistribute.c of FRR3.x (ZAPI4) // Reference: zebra_redistribute_add function in zebra/redistribute.c of FRR5.x (ZAPI5) func (b *RedistributeBody) DecodeFromBytes(data []byte, version uint8, softwareName string) 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 } // Reference: zebra_redistribute_send function in lib/zclient.c of Quagga1.2.x (ZAPI3) // Reference: zebra_redistribute_send function in lib/zclient.c of FRR3.x (ZAPI4) // Reference: zebra_redistribute_send function in lib/zclient.c of FRR5.x (ZAPI5) func (b *RedistributeBody) Serialize(version uint8, softwareName string) ([]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(version uint8, softwareName string) string { return fmt.Sprintf( "afi: %s, route_type: %s, instance :%d", b.Afi.String(), b.Redist.String(), b.Instance) } type LinkParam struct { Status uint32 TeMetric uint32 MaxBw float32 MaxRsvBw float32 UnrsvBw [8]float32 BwClassNum uint32 AdminGroup uint32 RemoteAS uint32 RemoteIP net.IP AveDelay uint32 MinDelay uint32 MaxDelay uint32 DelayVar uint32 PktLoss float32 ResidualBw float32 AvailableBw float32 UseBw float32 } 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 LinkParam LinkParam } // Reference: zebra_interface_if_set_value function in lib/zclient.c of Quagga1.2.x (ZAPI4) // Reference: zebra_interface_if_set_value function in lib/zclient.c of FRR3.x (ZAPI4) // Reference: zebra_interface_if_set_value function in lib/zclient.c of FRR5.x (ZAPI5) func (b *InterfaceUpdateBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { if len(data) < INTERFACE_NAMSIZ+33 { 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] } if version >= 3 { LinkParam := data[4+l] if LinkParam > 0 { data = data[5+l:] b.LinkParam.Status = binary.BigEndian.Uint32(data[0:4]) b.LinkParam.TeMetric = binary.BigEndian.Uint32(data[4:8]) b.LinkParam.MaxBw = math.Float32frombits(binary.BigEndian.Uint32(data[8:12])) b.LinkParam.MaxRsvBw = math.Float32frombits(binary.BigEndian.Uint32(data[12:16])) b.LinkParam.BwClassNum = binary.BigEndian.Uint32(data[16:20]) for i := uint32(0); i < b.LinkParam.BwClassNum; i++ { b.LinkParam.UnrsvBw[i] = math.Float32frombits(binary.BigEndian.Uint32(data[20+i*4 : 24+i*4])) } data = data[20+b.LinkParam.BwClassNum*4:] b.LinkParam.AdminGroup = binary.BigEndian.Uint32(data[0:4]) b.LinkParam.RemoteAS = binary.BigEndian.Uint32(data[4:8]) b.LinkParam.RemoteIP = data[8:12] b.LinkParam.AveDelay = binary.BigEndian.Uint32(data[12:16]) b.LinkParam.MinDelay = binary.BigEndian.Uint32(data[16:20]) b.LinkParam.MaxDelay = binary.BigEndian.Uint32(data[20:24]) b.LinkParam.DelayVar = binary.BigEndian.Uint32(data[24:28]) b.LinkParam.PktLoss = math.Float32frombits(binary.BigEndian.Uint32(data[28:32])) b.LinkParam.ResidualBw = math.Float32frombits(binary.BigEndian.Uint32(data[32:36])) b.LinkParam.AvailableBw = math.Float32frombits(binary.BigEndian.Uint32(data[36:40])) b.LinkParam.UseBw = math.Float32frombits(binary.BigEndian.Uint32(data[40:44])) } } return nil } func (b *InterfaceUpdateBody) Serialize(version uint8, softwareName string) ([]byte, error) { return []byte{}, nil } func (b *InterfaceUpdateBody) String(version uint8, softwareName 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 } // Reference: zebra_interface_address_read function in lib/zclient.c of Quagga1.2.x (ZAPI4) // Reference: zebra_interface_address_read function in lib/zclient.c of FRR3.x (ZAPI4) // Reference: zebra_interface_address_read function in lib/zclient.c of FRR5.x (ZAPI5) func (b *InterfaceAddressUpdateBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { b.Index = binary.BigEndian.Uint32(data[:4]) b.Flags = INTERFACE_ADDRESS_FLAG(data[4]) family := data[5] addrlen, err := addressByteLength(family) if err != nil { return err } 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, softwareName string) ([]byte, error) { return []byte{}, nil } func (b *InterfaceAddressUpdateBody) String(version uint8, softwareName 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 } // Reference: zebra_router_id_update_read function in lib/zclient.c of Quagga1.2.x (ZAPI4) // Reference: zebra_router_id_update_read function in lib/zclient.c of FRR3.x (ZAPI4) // Reference: zebra_router_id_update_read function in lib/zclient.c of FRR5.x (ZAPI5) func (b *RouterIDUpdateBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { family := data[0] addrlen, err := addressByteLength(family) if err != nil { return err } b.Prefix = data[1 : 1+addrlen] b.Length = data[1+addrlen] return nil } func (b *RouterIDUpdateBody) Serialize(version uint8, softwareName string) ([]byte, error) { return []byte{}, nil } func (b *RouterIDUpdateBody) String(version uint8, softwareName string) string { return fmt.Sprintf("id: %s/%d", b.Prefix.String(), b.Length) } /* Reference: struct zapi_nexthop in lib/zclient.h of FRR5.x (ZAPI5) */ type Nexthop struct { Type NEXTHOP_TYPE VrfId uint32 Ifindex uint32 Gate net.IP BlackholeType uint8 LabelNum uint8 MplsLabels []uint32 Onlink uint8 } func (n *Nexthop) String() string { s := make([]string, 0) s = append(s, fmt.Sprintf( "type: %s, gate: %s, ifindex: %d, vrf_id: %d, label_num: %d", n.Type.String(), n.Gate.String(), n.Ifindex, n.VrfId, n.LabelNum)) for i := uint8(0); i < n.LabelNum; i++ { s = append(s, fmt.Sprintf("label: %d", n.MplsLabels[i])) } return strings.Join(s, ", ") } type Prefix struct { Family uint8 PrefixLen uint8 Prefix net.IP } type IPRouteBody struct { Type ROUTE_TYPE Instance uint16 Flags FLAG Message MESSAGE_FLAG SAFI SAFI Prefix Prefix SrcPrefix Prefix Nexthops []Nexthop Distance uint8 Metric uint32 Mtu uint32 Tag uint32 Rmac [6]byte Api API_TYPE } func (b *IPRouteBody) RouteFamily(version uint8) bgp.RouteFamily { if b == nil { return bgp.RF_OPAQUE } family := addressFamilyFromApi(b.Api, version) if family == syscall.AF_UNSPEC { if b.Prefix.Prefix.To4() != nil { family = syscall.AF_INET } else if b.Prefix.Prefix.To16() != nil { family = syscall.AF_INET6 } } if version < 5 { switch family { case syscall.AF_INET: switch b.SAFI { case SAFI_UNICAST: return bgp.RF_IPv4_UC case SAFI_MULTICAST: return bgp.RF_IPv4_MC case SAFI_MPLS_VPN, FRR_SAFI_MPLS_VPN: return bgp.RF_IPv4_VPN case FRR_SAFI_ENCAP, SAFI_ENCAP: return bgp.RF_IPv4_ENCAP default: return bgp.RF_IPv4_UC } case syscall.AF_INET6: switch b.SAFI { case SAFI_UNICAST: return bgp.RF_IPv6_UC case SAFI_MULTICAST: return bgp.RF_IPv6_MC case SAFI_MPLS_VPN, FRR_SAFI_MPLS_VPN: return bgp.RF_IPv6_VPN case FRR_SAFI_ENCAP, SAFI_ENCAP: return bgp.RF_IPv6_ENCAP default: return bgp.RF_IPv6_UC } default: switch b.SAFI { case FRR_SAFI_EVPN: return bgp.RF_EVPN default: return bgp.RF_OPAQUE } } } else { switch family { case syscall.AF_INET: switch b.SAFI { case FRR_ZAPI5_SAFI_UNICAST: return bgp.RF_IPv4_UC case FRR_ZAPI5_SAFI_MULTICAST: return bgp.RF_IPv4_MC case FRR_ZAPI5_SAFI_MPLS_VPN: return bgp.RF_IPv4_VPN case FRR_ZAPI5_SAFI_ENCAP: return bgp.RF_IPv4_ENCAP case FRR_ZAPI5_SAFI_LABELED_UNICAST: return bgp.RF_IPv4_MPLS case FRR_ZAPI5_SAFI_FLOWSPEC: return bgp.RF_FS_IPv4_UC default: return bgp.RF_IPv4_UC } case syscall.AF_INET6: switch b.SAFI { case FRR_ZAPI5_SAFI_UNICAST: return bgp.RF_IPv6_UC case FRR_ZAPI5_SAFI_MULTICAST: return bgp.RF_IPv6_MC case FRR_ZAPI5_SAFI_MPLS_VPN: return bgp.RF_IPv6_VPN case FRR_ZAPI5_SAFI_ENCAP: return bgp.RF_IPv6_ENCAP case FRR_ZAPI5_SAFI_LABELED_UNICAST: return bgp.RF_IPv6_MPLS case FRR_ZAPI5_SAFI_FLOWSPEC: return bgp.RF_FS_IPv6_UC default: return bgp.RF_IPv6_UC } default: switch b.SAFI { case FRR_ZAPI5_SAFI_EVPN: return bgp.RF_EVPN default: return bgp.RF_OPAQUE } } } } func (b *IPRouteBody) IsWithdraw(version uint8) bool { if version <= 3 { switch b.Api { case IPV4_ROUTE_DELETE, IPV6_ROUTE_DELETE: return true } } else if version == 4 { switch b.Api { case FRR_IPV4_ROUTE_DELETE, FRR_IPV6_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV4_DEL, FRR_REDISTRIBUTE_IPV6_DEL: return true } } else if version == 5 { switch b.Api { case FRR_ZAPI5_ROUTE_DELETE, FRR_ZAPI5_IPV4_ROUTE_DELETE, FRR_ZAPI5_IPV6_ROUTE_DELETE, FRR_ZAPI5_REDISTRIBUTE_ROUTE_DEL: return true } } else if version >= 6 { switch b.Api { case FRR_ZAPI6_ROUTE_DELETE, FRR_ZAPI6_REDISTRIBUTE_ROUTE_DEL: return true } } return false } // Reference: zapi_ipv4_route function in lib/zclient.c of Quagga1.2.x (ZAPI3) // Reference: zapi_ipv4_route function in lib/zclient.c of FRR3.x (ZAPI4) // Reference: zapi_route_encode function in lib/zclient.c of FRR5.x (ZAPI5) func (b *IPRouteBody) Serialize(version uint8, softwareName string) ([]byte, error) { var buf []byte if version <= 3 { buf = make([]byte, 5) } else if version == 4 { buf = make([]byte, 10) } else { // version >= 5 buf = make([]byte, 9) } buf[0] = uint8(b.Type) if version <= 3 { buf[1] = uint8(b.Flags) buf[2] = uint8(b.Message) binary.BigEndian.PutUint16(buf[3:5], uint16(b.SAFI)) } else { // version >= 4 binary.BigEndian.PutUint16(buf[1:3], uint16(b.Instance)) binary.BigEndian.PutUint32(buf[3:7], uint32(b.Flags)) buf[7] = uint8(b.Message) if version == 4 { binary.BigEndian.PutUint16(buf[8:10], uint16(b.SAFI)) } else { // version >= 5 buf[8] = uint8(b.SAFI) if b.Flags&FLAG_EVPN_ROUTE > 0 { // size of struct ethaddr is 6 octets defined by ETH_ALEN buf = append(buf, b.Rmac[:6]...) } if b.Prefix.Family == syscall.AF_UNSPEC { if b.Prefix.Prefix.To4() != nil { b.Prefix.Family = syscall.AF_INET } else if b.Prefix.Prefix.To16() != nil { b.Prefix.Family = syscall.AF_INET6 } } buf = append(buf, b.Prefix.Family) } } byteLen := (int(b.Prefix.PrefixLen) + 7) / 8 buf = append(buf, b.Prefix.PrefixLen) buf = append(buf, b.Prefix.Prefix[:byteLen]...) if (version == 4 && b.Message&FRR_MESSAGE_SRCPFX > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_SRCPFX > 0) { byteLen = (int(b.SrcPrefix.PrefixLen) + 7) / 8 buf = append(buf, b.SrcPrefix.PrefixLen) buf = append(buf, b.SrcPrefix.Prefix[:byteLen]...) } if (version <= 3 && b.Message&MESSAGE_NEXTHOP > 0) || (version == 4 && b.Message&FRR_MESSAGE_NEXTHOP > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_NEXTHOP > 0) { if version < 5 { if b.Flags&FLAG_BLACKHOLE > 0 { buf = append(buf, []byte{1, uint8(NEXTHOP_TYPE_BLACKHOLE)}...) } else { buf = append(buf, uint8(len(b.Nexthops))) } } else { // version >= 5 bbuf := make([]byte, 2) binary.BigEndian.PutUint16(bbuf, uint16(len(b.Nexthops))) buf = append(buf, bbuf...) } for _, nexthop := range b.Nexthops { if version >= 5 { bbuf := make([]byte, 4) binary.BigEndian.PutUint32(bbuf, nexthop.VrfId) buf = append(buf, bbuf...) } if nexthop.Type == NEXTHOP_TYPE(0) { if nexthop.Gate.To4() != nil { if version <= 3 { nexthop.Type = NEXTHOP_TYPE_IPV4 } else { nexthop.Type = FRR_NEXTHOP_TYPE_IPV4 } if version >= 5 && nexthop.Ifindex > 0 { nexthop.Type = FRR_NEXTHOP_TYPE_IPV4_IFINDEX } } else if nexthop.Gate.To16() != nil { if version <= 3 { nexthop.Type = NEXTHOP_TYPE_IPV6 } else { nexthop.Type = FRR_NEXTHOP_TYPE_IPV6 } if version >= 5 && nexthop.Ifindex > 0 { nexthop.Type = FRR_NEXTHOP_TYPE_IPV6_IFINDEX } } else if nexthop.Ifindex > 0 { if version <= 3 { nexthop.Type = NEXTHOP_TYPE_IFINDEX } else { nexthop.Type = FRR_NEXTHOP_TYPE_IFINDEX } } else if version >= 5 { nexthop.Type = FRR_NEXTHOP_TYPE_BLACKHOLE } } buf = append(buf, uint8(nexthop.Type)) if version == 6 && softwareName == "frr7.1" { buf = append(buf, nexthop.Onlink) } if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV4) || (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV4) { buf = append(buf, nexthop.Gate.To4()...) } else if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV6) || (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV6) { buf = append(buf, nexthop.Gate.To16()...) } else if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IFINDEX) || (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IFINDEX) { bbuf := make([]byte, 4) binary.BigEndian.PutUint32(bbuf, nexthop.Ifindex) buf = append(buf, bbuf...) } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV4_IFINDEX { buf = append(buf, nexthop.Gate.To4()...) bbuf := make([]byte, 4) binary.BigEndian.PutUint32(bbuf, nexthop.Ifindex) buf = append(buf, bbuf...) } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV6_IFINDEX { buf = append(buf, nexthop.Gate.To16()...) bbuf := make([]byte, 4) binary.BigEndian.PutUint32(bbuf, nexthop.Ifindex) buf = append(buf, bbuf...) } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_BLACKHOLE { buf = append(buf, uint8(nexthop.BlackholeType)) } if version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_LABEL > 0 { buf = append(buf, nexthop.LabelNum) for i := uint8(0); i < nexthop.LabelNum; i++ { bbuf := make([]byte, 4) /* Frr uses stream_put for mpls label array. stream_put is unaware of byteorder coversion. Therefore LittleEndian is used instead of BigEndian. */ binary.LittleEndian.PutUint32(bbuf, nexthop.MplsLabels[i]) buf = append(buf, bbuf...) } } } if (version <= 3 && b.Message&MESSAGE_DISTANCE > 0) || (version == 4 && b.Message&FRR_MESSAGE_DISTANCE > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_DISTANCE > 0) { buf = append(buf, b.Distance) } if (version <= 3 && b.Message&MESSAGE_METRIC > 0) || (version == 4 && b.Message&FRR_MESSAGE_METRIC > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_METRIC > 0) { bbuf := make([]byte, 4) binary.BigEndian.PutUint32(bbuf, b.Metric) buf = append(buf, bbuf...) } if (version <= 3 && b.Message&MESSAGE_MTU > 0) || (version == 4 && b.Message&FRR_MESSAGE_MTU > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_MTU > 0) { bbuf := make([]byte, 4) binary.BigEndian.PutUint32(bbuf, b.Mtu) buf = append(buf, bbuf...) } if (version <= 3 && b.Message&MESSAGE_TAG > 0) || (version == 4 && b.Message&FRR_MESSAGE_TAG > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_TAG > 0) { bbuf := make([]byte, 4) binary.BigEndian.PutUint32(bbuf, b.Tag) buf = append(buf, bbuf...) } } return buf, nil } // Reference: zebra_read_ipv4 function in bgpd/bgp_zebra.c of Quagga1.2.x (ZAPI3) // Reference: zebra_read_ipv4 function in bgpd/bgp_zebra.c of FRR4.x (ZAPI4) // Reference: zapi_route_decode function in lib/zclient.c of FRR5.x (ZAPI5) func (b *IPRouteBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { if b == nil { return fmt.Errorf("IPRouteBody is nil") } b.Prefix.Family = addressFamilyFromApi(b.Api, version) /* REDSTRIBUTE_IPV4_ADD|DEL and REDSITRBUTE_IPV6_ADD|DEL have merged to REDISTRIBUTE_ROUTE_ADD|DEL in ZAPI version 5. Therefore it can not judge the protocol famiiy from API. */ 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) if version >= 5 { b.SAFI = SAFI(data[1]) data = data[2:] if b.Flags&FLAG_EVPN_ROUTE > 0 { // size of struct ethaddr is 6 octets defined by ETH_ALEN copy(b.Rmac[0:6], data[0:6]) data = data[6:] } b.Prefix.Family = data[0] } addrByteLen, err := addressByteLength(b.Prefix.Family) if err != nil { return err } addrBitLen := uint8(addrByteLen * 8) b.Prefix.PrefixLen = data[1] if b.Prefix.PrefixLen > addrBitLen { return fmt.Errorf("prefix length %d is greater than %d", b.Prefix.PrefixLen, addrBitLen) } data = data[2:] pos := 0 rest := len(data) buf := make([]byte, addrByteLen) byteLen := int((b.Prefix.PrefixLen + 7) / 8) if pos+byteLen > rest { return fmt.Errorf("message length invalid pos:%d rest:%d", pos, rest) } copy(buf, data[pos:pos+byteLen]) b.Prefix.Prefix = ipFromFamily(b.Prefix.Family, buf) pos += byteLen if (version == 4 && b.Message&FRR_MESSAGE_SRCPFX > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_SRCPFX > 0) { if pos+1 > rest { return fmt.Errorf("MESSAGE_SRCPFX message length invalid pos:%d rest:%d", pos, rest) } b.SrcPrefix.PrefixLen = data[pos] if b.SrcPrefix.PrefixLen > addrBitLen { return fmt.Errorf("prefix length is greater than %d", addrByteLen*8) } pos += 1 buf = make([]byte, addrByteLen) byteLen = int((b.SrcPrefix.PrefixLen + 7) / 8) if pos+byteLen > rest { return fmt.Errorf("MESSAGE_SRCPFX message length invalid pos:%d rest:%d", pos, rest) } copy(buf, data[pos:pos+byteLen]) b.SrcPrefix.Prefix = ipFromFamily(b.Prefix.Family, buf) pos += byteLen } b.Nexthops = []Nexthop{} if (version <= 3 && b.Message&MESSAGE_NEXTHOP > 0) || (version == 4 && b.Message&FRR_MESSAGE_NEXTHOP > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_NEXTHOP > 0) { var numNexthop uint16 if version <= 4 { if pos+1 > rest { return fmt.Errorf("MESSAGE_NEXTHOP message length invalid pos:%d rest:%d", pos, rest) } numNexthop = uint16(data[pos]) pos += 1 } else { // version >= 5 if pos+2 > rest { return fmt.Errorf("MESSAGE_NEXTHOP message length invalid pos:%d rest:%d", pos, rest) } numNexthop = binary.BigEndian.Uint16(data[pos : pos+2]) pos += 2 } for i := 0; i < int(numNexthop); i++ { var nexthop Nexthop if version <= 3 { if b.Prefix.Family == syscall.AF_INET { nexthop.Type = NEXTHOP_TYPE_IPV4 } else if b.Prefix.Family == syscall.AF_INET6 { nexthop.Type = NEXTHOP_TYPE_IPV6 } } else if version == 4 { if b.Prefix.Family == syscall.AF_INET { nexthop.Type = FRR_NEXTHOP_TYPE_IPV4 } else if b.Prefix.Family == syscall.AF_INET6 { nexthop.Type = FRR_NEXTHOP_TYPE_IPV6 } } else { // version >= 5 if pos+5 > rest { return fmt.Errorf("MESSAGE_NEXTHOP message length invalid pos:%d rest:%d", pos, rest) } nexthop.VrfId = binary.BigEndian.Uint32(data[pos : pos+4]) nexthop.Type = NEXTHOP_TYPE(data[pos+4]) pos += 5 if softwareName == "frr7.1" { nexthop.Onlink = uint8(data[pos]) pos += 1 } } if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV4) || (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV4) { if pos+4 > rest { return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IPV4 message length invalid pos:%d rest:%d", pos, rest) } addr := data[pos : pos+4] nexthop.Gate = net.IP(addr).To4() pos += 4 } else if (version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV6) || (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV6) { if pos+16 > rest { return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IPV6 message length invalid pos:%d rest:%d", pos, rest) } addr := data[pos : pos+16] nexthop.Gate = net.IP(addr).To16() pos += 16 } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IFINDEX { if pos+4 > rest { return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IFINDEX message length invalid pos:%d rest:%d", pos, rest) } nexthop.Ifindex = binary.BigEndian.Uint32(data[pos : pos+4]) pos += 4 // barkward compatibility if b.Prefix.Family == syscall.AF_INET { nexthop.Gate = net.ParseIP("0.0.0.0") } else if b.Prefix.Family == syscall.AF_INET6 { nexthop.Gate = net.ParseIP("::") } } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV4_IFINDEX { if pos+8 > rest { return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IPV4_IFINDEX message length invalid pos:%d rest:%d", pos, rest) } addr := data[pos : pos+4] nexthop.Gate = net.IP(addr).To4() nexthop.Ifindex = binary.BigEndian.Uint32(data[pos+4 : pos+8]) pos += 8 } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_IPV6_IFINDEX { if pos+20 > rest { return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_IPV6_IFINDEX message length invalid pos:%d rest:%d", pos, rest) } addr := data[pos : pos+16] nexthop.Gate = net.IP(addr).To16() nexthop.Ifindex = binary.BigEndian.Uint32(data[pos+16 : pos+20]) pos += 20 } else if version >= 5 && nexthop.Type == FRR_NEXTHOP_TYPE_BLACKHOLE { if pos+1 > rest { return fmt.Errorf("MESSAGE_NEXTHOP NEXTHOP_TYPE_BLACKHOLE message length invalid pos:%d rest:%d", pos, rest) } nexthop.BlackholeType = data[pos] pos += 1 } b.Nexthops = append(b.Nexthops, nexthop) } } if (version <= 3 && b.Message&MESSAGE_IFINDEX > 0) || (version == 4 && b.Message&FRR_MESSAGE_IFINDEX > 0) { if pos+1 > rest { return fmt.Errorf("MESSAGE_IFINDEX message length invalid pos:%d rest:%d", pos, rest) } numIfIndex := uint8(data[pos]) pos += 1 for i := 0; i < int(numIfIndex); i++ { if pos+4 > rest { return fmt.Errorf("MESSAGE_IFINDEX message length invalid pos:%d rest:%d", pos, rest) } var nexthop Nexthop nexthop.Ifindex = binary.BigEndian.Uint32(data[pos : pos+4]) if version <= 3 { nexthop.Type = NEXTHOP_TYPE_IFINDEX } else if version == 4 { nexthop.Type = FRR_NEXTHOP_TYPE_IFINDEX } b.Nexthops = append(b.Nexthops, nexthop) pos += 4 } } if (version <= 3 && b.Message&MESSAGE_DISTANCE > 0) || (version == 4 && b.Message&FRR_MESSAGE_DISTANCE > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_DISTANCE > 0) { if pos+1 > rest { return fmt.Errorf("MESSAGE_DISTANCE message length invalid pos:%d rest:%d", pos, rest) } b.Distance = data[pos] pos += 1 } if (version <= 3 && b.Message&MESSAGE_METRIC > 0) || (version == 4 && b.Message&FRR_MESSAGE_METRIC > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_METRIC > 0) { if pos+4 > rest { return fmt.Errorf("MESSAGE_METRIC message length invalid pos:%d rest:%d", pos, rest) } b.Metric = binary.BigEndian.Uint32(data[pos : pos+4]) pos += 4 } if (version <= 3 && b.Message&MESSAGE_MTU > 0) || (version == 4 && b.Message&FRR_MESSAGE_MTU > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_MTU > 0) { if pos+4 > rest { return fmt.Errorf("MESSAGE_MTU message length invalid pos:%d rest:%d", pos, rest) } b.Mtu = binary.BigEndian.Uint32(data[pos : pos+4]) pos += 4 } if (version <= 3 && b.Message&MESSAGE_TAG > 0) || (version == 4 && b.Message&FRR_MESSAGE_TAG > 0) || (version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_TAG > 0) { if pos+4 > rest { return fmt.Errorf("MESSAGE_TAG message length invalid pos:%d rest:%d", pos, rest) } b.Tag = binary.BigEndian.Uint32(data[pos : pos+4]) pos += 4 } if pos != rest { return fmt.Errorf("message length invalid pos:%d rest:%d", pos, rest) } return nil } func (b *IPRouteBody) String(version uint8, softwareName 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(version, softwareName), b.Message, b.SAFI.String(), b.Prefix.Prefix.String(), b.Prefix.PrefixLen, b.SrcPrefix.Prefix.String(), b.SrcPrefix.PrefixLen) for i, nh := range b.Nexthops { s += fmt.Sprintf(", nexthops[%d]: %s", i, nh.String()) } return s + fmt.Sprintf( ", distance: %d, metric: %d, mtu: %d, tag: %d", b.Distance, b.Metric, b.Mtu, b.Tag) } func decodeNexthopsFromBytes(nexthops *[]Nexthop, data []byte, family uint8, version uint8) (int, error) { addrByteLen, err := addressByteLength(family) if err != nil { return 0, err } numNexthop := int(data[0]) offset := 1 for i := 0; i < numNexthop; i++ { nexthop := Nexthop{} nexthop.Type = NEXTHOP_TYPE(data[offset]) offset += 1 // On Quagga, NEXTHOP_TYPE_IFNAME is same as NEXTHOP_TYPE_IFINDEX, // NEXTHOP_TYPE_IPV4_IFNAME is same as NEXTHOP_TYPE_IPV4_IFINDEX, // NEXTHOP_TYPE_IPV6_IFNAME is same as NEXTHOP_TYPE_IPV6_IFINDEX // On FRRouting version 3.0 or later, NEXTHOP_TYPE_IPV4 and NEXTHOP_TYPE_IPV6 have // the same structure with NEXTHOP_TYPE_IPV4_IFINDEX and NEXTHOP_TYPE_IPV6_IFINDEX. if (version <= 3 && (nexthop.Type == NEXTHOP_TYPE_IFINDEX || nexthop.Type == NEXTHOP_TYPE_IFNAME)) || (version >= 4 && nexthop.Type == FRR_NEXTHOP_TYPE_IFINDEX) { nexthop.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4]) offset += 4 } else if version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV4 { nexthop.Gate = net.IP(data[offset : offset+addrByteLen]).To4() offset += addrByteLen } else if version <= 3 && nexthop.Type == NEXTHOP_TYPE_IPV6 { nexthop.Gate = net.IP(data[offset : offset+addrByteLen]).To16() offset += addrByteLen } else if (version <= 3 && (nexthop.Type == NEXTHOP_TYPE_IPV4_IFINDEX || nexthop.Type == NEXTHOP_TYPE_IPV4_IFNAME)) || (version >= 4 && (nexthop.Type == FRR_NEXTHOP_TYPE_IPV4 || nexthop.Type == FRR_NEXTHOP_TYPE_IPV4_IFINDEX)) { nexthop.Gate = net.IP(data[offset : offset+addrByteLen]).To4() offset += addrByteLen nexthop.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4]) offset += 4 } else if (version <= 3 && (nexthop.Type == NEXTHOP_TYPE_IPV6_IFINDEX || nexthop.Type == NEXTHOP_TYPE_IPV6_IFNAME)) || (version >= 4 && (nexthop.Type == FRR_NEXTHOP_TYPE_IPV6 || nexthop.Type == FRR_NEXTHOP_TYPE_IPV6_IFINDEX)) { nexthop.Gate = net.IP(data[offset : offset+addrByteLen]).To16() offset += addrByteLen nexthop.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4]) offset += 4 } if version >= 5 { nexthop.LabelNum = uint8(data[offset]) offset += 1 if nexthop.LabelNum > MPLS_MAX_LABEL { nexthop.LabelNum = MPLS_MAX_LABEL } if nexthop.LabelNum > 0 { nexthop.MplsLabels = make([]uint32, nexthop.LabelNum) for n := uint8(0); n < nexthop.LabelNum; n++ { /* Frr uses stream_put for mpls label array. stream_put is unaware of byteorder coversion. Therefore LittleEndian is used instead of BigEndian. */ nexthop.MplsLabels[n] = binary.LittleEndian.Uint32(data[offset : offset+4]) offset += 4 } } } *nexthops = append(*nexthops, nexthop) } return offset, nil } type NexthopLookupBody struct { Api API_TYPE Addr net.IP Distance uint8 Metric uint32 Nexthops []Nexthop } // Quagga only. Reference: zread_ipv[4|6]_nexthop_lookup in zebra/zserv.c of Quagga1.2.x (ZAPI3) func (b *NexthopLookupBody) Serialize(version uint8, softwareName string) ([]byte, error) { family := addressFamilyFromApi(b.Api, version) buf := make([]byte, 0) if family == syscall.AF_INET { buf = append(buf, b.Addr.To4()...) } else if family == syscall.AF_INET6 { buf = append(buf, b.Addr.To16()...) } return buf, nil } // Quagga only. Reference: zsend_ipv[4|6]_nexthop_lookup in zebra/zserv.c of Quagga1.2.x (ZAPI3) func (b *NexthopLookupBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { family := addressFamilyFromApi(b.Api, version) addrByteLen, err := addressByteLength(family) if err != nil { return err } if len(data) < addrByteLen { return fmt.Errorf("message length invalid") } buf := make([]byte, addrByteLen) copy(buf, data[0:addrByteLen]) pos := addrByteLen b.Addr = ipFromFamily(family, buf) if version >= 4 { b.Distance = data[pos] pos++ } if len(data[pos:]) > int(1+addrByteLen) { b.Metric = binary.BigEndian.Uint32(data[pos : pos+4]) pos += 4 b.Nexthops = []Nexthop{} if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[pos:], family, version); err != nil { return err } else { pos += nexthopsByteLen } } return nil } func (b *NexthopLookupBody) String(version uint8, softwareName 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 } // Quagga only. Reference: zread_ipv4_import_lookup in zebra/zserv.c of Quagga1.2.x (ZAPI3) func (b *ImportLookupBody) Serialize(version uint8, softwareName string) ([]byte, error) { buf := make([]byte, 1) buf[0] = b.PrefixLength buf = append(buf, b.Addr.To4()...) return buf, nil } // Quagga only. Reference: zsend_ipv4_import_lookup in zebra/zserv.c of Quagga1.2.x (ZAPI3) func (b *ImportLookupBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { family := addressFamilyFromApi(b.Api, version) addrByteLen, err := addressByteLength(family) if err != nil { return err } if len(data) < addrByteLen { return fmt.Errorf("message length invalid") } buf := make([]byte, addrByteLen) copy(buf, data[0:addrByteLen]) pos := addrByteLen b.Addr = net.IP(buf).To4() if len(data[pos:]) > int(1+addrByteLen) { b.Metric = binary.BigEndian.Uint32(data[pos : pos+4]) pos += 4 b.Nexthops = []Nexthop{} if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[pos:], family, version); err != nil { return err } else { pos += nexthopsByteLen } } return nil } func (b *ImportLookupBody) String(version uint8, softwareName 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 } } // Reference: sendmsg_nexthop in bgpd/bgp_nht.c of Quagga1.2.x (ZAPI3) // Reference: sendmsg_zebra_rnh in bgpd/bgp_nht.c of FRR3.x (ZAPI4) // Reference: zclient_send_rnh function in lib/zclient.c of FRR5.x (ZAPI5) 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) addrByteLen, err := addressByteLength(uint8(n.Family)) if err != nil { return nil, err } buf[3] = byte(addrByteLen * 8) // Prefix (variable) switch n.Family { case uint16(syscall.AF_INET): buf = append(buf, n.Prefix.To4()...) case uint16(syscall.AF_INET6): buf = append(buf, n.Prefix.To16()...) default: return nil, fmt.Errorf("invalid address family: %d", n.Family) } return buf, nil } // Reference: zserv_nexthop_register in zebra/zserv.c of Quagga1.2.x (ZAPI3) // Reference: zserv_rnh_register in zebra/zserv.c of FRR3.x (ZAPI4) // Reference: zread_rnh_register in zebra/zapi_msg.c of FRR5.x (ZAPI5) func (n *RegisteredNexthop) DecodeFromBytes(data []byte, softwareName string) error { // Connected (1 byte) n.Connected = uint8(data[0]) // Address Family (2 bytes) n.Family = binary.BigEndian.Uint16(data[1:3]) // Note: Ignores Prefix Length (1 byte) addrByteLen := (int(data[3]) + 7) / 8 // Prefix (variable) n.Prefix = ipFromFamily(uint8(n.Family), data[4:4+addrByteLen]) return nil } func (n *RegisteredNexthop) String(version uint8, softwareName 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 } // Reference: sendmsg_nexthop in bgpd/bgp_nht.c of Quagga1.2.x (ZAPI3) // Reference: sendmsg_zebra_rnh in bgpd/bgp_nht.c of FRR3.x (ZAPI4) // Reference: zclient_send_rnh function in lib/zclient.c of FRR5.x (ZAPI5) func (b *NexthopRegisterBody) Serialize(version uint8, softwareName string) ([]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 } // Reference: zserv_nexthop_register in zebra/zserv.c of Quagga1.2.x (ZAPI3) // Reference: zserv_rnh_register in zebra/zserv.c of FRR3.x (ZAPI4) // Reference: zread_rnh_register in zebra/zapi_msg.c of FRR5.x (ZAPI5) func (b *NexthopRegisterBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { offset := 0 // List of Registered Nexthops b.Nexthops = []*RegisteredNexthop{} for len(data[offset:]) > 0 { nh := new(RegisteredNexthop) err := nh.DecodeFromBytes(data[offset:], softwareName) 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(version uint8, softwareName string) string { s := make([]string, 0) for _, nh := range b.Nexthops { s = append(s, fmt.Sprintf("nexthop:{%s}", nh.String(version, softwareName))) } return strings.Join(s, ", ") } /* NEXTHOP_UPDATE message uses same data structure as IPRoute (zapi_route) in FRR version 4, 5 (ZApi version 5) */ type NexthopUpdateBody IPRouteBody // Reference: send_client function in zebra/zebra_rnh.c of Quagga1.2.x (ZAPI3) // Reference: send_client function in zebra/zebra_rnh.c of FRR3.x (ZAPI4) // Reference: send_client function in zebra/zebra_rnh.c of FRR5.x (ZAPI5) func (b *NexthopUpdateBody) Serialize(version uint8, softwareName string) ([]byte, error) { // Address Family (2 bytes) buf := make([]byte, 3) binary.BigEndian.PutUint16(buf, uint16(b.Prefix.Family)) addrByteLen, err := addressByteLength(b.Prefix.Family) if err != nil { return nil, err } buf[2] = byte(addrByteLen * 8) // Prefix Length (1 byte) + Prefix (variable) switch b.Prefix.Family { case syscall.AF_INET: buf = append(buf, b.Prefix.Prefix.To4()...) case syscall.AF_INET6: buf = append(buf, b.Prefix.Prefix.To16()...) default: return nil, fmt.Errorf("invalid address family: %d", b.Prefix.Family) } if version >= 5 { // Type (1 byte) (if version>=5) // Instance (2 bytes) (if version>=5) buf = append(buf, byte(b.Type)) bbuf := make([]byte, 2) binary.BigEndian.PutUint16(bbuf, b.Instance) buf = append(buf, bbuf...) } if version >= 4 { // Distance (1 byte) (if version>=4) buf = append(buf, b.Distance) } // Metric (4 bytes) bbuf := make([]byte, 4) binary.BigEndian.PutUint32(bbuf, b.Metric) buf = append(buf, bbuf...) // Number of Nexthops (1 byte) buf = append(buf, uint8(0)) // Temporary code // ToDo Processing Route Entry return buf, nil } // Reference: bgp_parse_nexthop_update function in bgpd/bgp_nht.c of Quagga1.2.x (ZAPI3) // Reference: bgp_parse_nexthop_update function in bgpd/bgp_nht.c of FRR3.x (ZAPI4) // Reference: zapi_nexthop_update_decode function in lib/zclient.c of FRR5.x (ZAPI5) func (b *NexthopUpdateBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { // Address Family (2 bytes) prefixFamily := binary.BigEndian.Uint16(data[0:2]) b.Prefix.Family = uint8(prefixFamily) b.Prefix.PrefixLen = data[2] offset := 3 addrByteLen, err := addressByteLength(b.Prefix.Family) if err != nil { return err } b.Prefix.Prefix = ipFromFamily(b.Prefix.Family, data[offset:offset+addrByteLen]) offset += addrByteLen if version >= 5 { b.Type = ROUTE_TYPE(data[offset]) b.Instance = binary.BigEndian.Uint16(data[offset+1 : offset+3]) offset += 3 } // Distance (1 byte) (if version>=4) if version >= 4 { b.Distance = data[offset] offset += 1 } // Metric (4 bytes) // Number of Nexthops (1 byte) 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:], b.Prefix.Family, version); err != nil { return err } else { offset += nexthopsByteLen } return nil } func (b *NexthopUpdateBody) String(version uint8, softwareName string) string { s := fmt.Sprintf( "family: %d, prefix: %s, distance: %d, metric: %d", b.Prefix.Family, b.Prefix.Prefix.String(), b.Distance, b.Metric) for _, nh := range b.Nexthops { s = s + fmt.Sprintf(", nexthop:{%s}", nh.String()) } return s } type LabelManagerConnectBody struct { RedistDefault ROUTE_TYPE Instance uint16 // The followings are used in response from Zebra Result uint8 // 0 means success } // Reference: lm_label_manager_connect in lib/zclient.c of FRR func (b *LabelManagerConnectBody) Serialize(version uint8, softwareName string) ([]byte, error) { buf := make([]byte, 3) buf[0] = uint8(b.RedistDefault) binary.BigEndian.PutUint16(buf[1:3], b.Instance) return buf, nil } func (b *LabelManagerConnectBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { size := 1 // FRR v4 may work incorrectly. It returns result only although it uses ZAPI v5. if version >= 4 { size = 4 } if len(data) < size { return fmt.Errorf("invalid message length for LABEL_MANAGER_CONNECT response: %d<%d", len(data), size) } if version > 4 { b.RedistDefault = ROUTE_TYPE(data[0]) b.Instance = binary.BigEndian.Uint16(data[1:3]) data = data[3:] } b.Result = data[0] return nil } func (b *LabelManagerConnectBody) String(version uint8, softwareName string) string { return fmt.Sprintf( "route_type: %s, instance: %d, result: %d", b.RedistDefault.String(), b.Instance, b.Result) } // Reference: zsend_assign_label_chunk_response in zebra/zserv.c of FRR3.x // Reference: zsend_assign_label_chunk_response in zebra/zapi_msg.c of FRR5.x and 6.x type GetLabelChunkBody struct { Proto uint8 // it is appeared in FRR5.x and 6.x Instance uint16 // it is appeared in FRR5.x and 6.x Keep uint8 ChunkSize uint32 // The followings are used in response from Zebra Start uint32 End uint32 } // Reference: zread_get_label_chunk in zebra/zserv.c of FRR3.x // Reference: zread_get_label_chunk in zebra/zapi_msg.c of FRR5.x and 6.x func (b *GetLabelChunkBody) Serialize(version uint8, softwareName string) ([]byte, error) { buf := make([]byte, 8) pos := 0 if version > 4 { buf[pos] = b.Proto binary.BigEndian.PutUint16(buf[pos+1:pos+3], b.Instance) pos += 3 } buf[pos] = b.Keep binary.BigEndian.PutUint32(buf[pos+1:pos+5], b.ChunkSize) pos += 5 return buf[0:pos], nil } // Reference: zsend_assign_label_chunk_response in zebra/zserv.c of FRR3.x // Reference: zsend_assign_label_chunk_response in zebra/zapi_msg.c of FRR5.x and 6.x func (b *GetLabelChunkBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { size := 9 if version > 4 { size = 12 } if len(data) < size { return fmt.Errorf("invalid message length for GET_LABEL_CHUNK response: %d<%d", len(data), size) } if version > 4 { b.Proto = data[0] b.Instance = binary.BigEndian.Uint16(data[1:3]) data = data[3:] } b.Keep = data[0] b.Start = binary.BigEndian.Uint32(data[1:5]) b.End = binary.BigEndian.Uint32(data[5:9]) return nil } func (b *GetLabelChunkBody) String(version uint8, softwareName string) string { return fmt.Sprintf( "keep: %d, chunk_size: %d, start: %d, end: %d", b.Keep, b.ChunkSize, b.Start, b.End) } type ReleaseLabelChunkBody struct { Proto uint8 // it is appeared in FRR5.x and 6.x Instance uint16 // it is appeared in FRR5.x and 6.x Start uint32 End uint32 } func (b *ReleaseLabelChunkBody) Serialize(version uint8, softwareName string) ([]byte, error) { buf := make([]byte, 11) pos := 0 if version > 4 { buf[pos] = b.Proto binary.BigEndian.PutUint16(buf[pos+1:pos+3], b.Instance) pos += 3 } binary.BigEndian.PutUint32(buf[pos:pos+4], b.Start) binary.BigEndian.PutUint32(buf[pos+4:pos+8], b.End) pos += 8 return buf[0:pos], nil } func (b *ReleaseLabelChunkBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { // No response from Zebra return nil } func (b *ReleaseLabelChunkBody) String(version uint8, softwareName string) string { return fmt.Sprintf( "start: %d, end: %d", b.Start, b.End) } //go:generate stringer -type=LSP_TYPE type LSP_TYPE uint8 const ( LSP_NONE LSP_TYPE = iota //defined in FRR3 and over LSP_STATIC //defined in FRR3 and over LSP_LDP //defined in FRR3 and over LSP_BGP //defined in FRR4 and over LSP_SR //defined in FRR4 and over LSP_SHARP //defined in FRR5 and over ) type VrfLabelBody struct { Label uint32 Afi AFI LabelType LSP_TYPE } // Reference: zclient_send_vrf_label in lib/zclient.c of FRR 5.x and 6.x func (b *VrfLabelBody) Serialize(version uint8, softwareName string) ([]byte, error) { buf := make([]byte, 6) binary.BigEndian.PutUint32(buf[0:4], b.Label) buf[4] = uint8(b.Afi) buf[5] = uint8(b.LabelType) return buf, nil } func (b *VrfLabelBody) DecodeFromBytes(data []byte, version uint8, softwareName string) error { if len(data) < 6 { return fmt.Errorf("invalid message length for VRF_LABEL message: %d<6", len(data)) } b.Label = binary.BigEndian.Uint32(data[0:4]) b.Afi = AFI(data[4]) b.LabelType = LSP_TYPE(data[5]) return nil } func (b *VrfLabelBody) String(version uint8, softwareName string) string { return fmt.Sprintf( "label: %d, AFI: %s LSP_type: %s", b.Label, b.Afi, b.LabelType) } type Message struct { Header Header Body Body } func (m *Message) Serialize(software string) ([]byte, error) { var body []byte if m.Body != nil { var err error body, err = m.Body.Serialize(m.Header.Version, software) 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, software string) 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, software) } func (m *Message) parseFrrMessage(data []byte, software string) 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{} case FRR_LABEL_MANAGER_CONNECT: // Note: Synchronous message m.Body = &LabelManagerConnectBody{} case FRR_GET_LABEL_CHUNK: // Note: Synchronous message m.Body = &GetLabelChunkBody{} case FRR_RELEASE_LABEL_CHUNK: // Note: Synchronous message m.Body = &ReleaseLabelChunkBody{} default: m.Body = &UnknownBody{} } return m.Body.DecodeFromBytes(data, m.Header.Version, software) } func (m *Message) parseFrrZapi5Message(data []byte, software string) error { command := m.Header.Command if software == "frr4" { if c, err := frr4Zapi5Command(command, software, true); err == nil { command = c } else { return err } } else if software == "cumulus" { if c, err := cumulusZapi5Command(command, software, true); err == nil { command = c } else { return err } } switch command { case FRR_ZAPI5_INTERFACE_ADD, FRR_ZAPI5_INTERFACE_DELETE, FRR_ZAPI5_INTERFACE_UP, FRR_ZAPI5_INTERFACE_DOWN: m.Body = &InterfaceUpdateBody{} case FRR_ZAPI5_INTERFACE_ADDRESS_ADD, FRR_ZAPI5_INTERFACE_ADDRESS_DELETE: m.Body = &InterfaceAddressUpdateBody{} case FRR_ZAPI5_ROUTER_ID_UPDATE: m.Body = &RouterIDUpdateBody{} case FRR_ZAPI5_NEXTHOP_UPDATE: m.Body = &NexthopUpdateBody{} case FRR_ZAPI5_INTERFACE_NBR_ADDRESS_ADD, FRR_ZAPI5_INTERFACE_NBR_ADDRESS_DELETE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI5_INTERFACE_BFD_DEST_UPDATE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI5_IMPORT_CHECK_UPDATE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI5_BFD_DEST_REPLAY: // TODO m.Body = &UnknownBody{} case FRR_ZAPI5_REDISTRIBUTE_ROUTE_ADD, FRR_ZAPI5_REDISTRIBUTE_ROUTE_DEL: m.Body = &IPRouteBody{Api: m.Header.Command} case FRR_ZAPI5_INTERFACE_VRF_UPDATE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI5_INTERFACE_LINK_PARAMS: // TODO m.Body = &UnknownBody{} case FRR_ZAPI5_PW_STATUS_UPDATE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI5_LABEL_MANAGER_CONNECT: // Note: Synchronous message m.Body = &LabelManagerConnectBody{} case FRR_ZAPI5_GET_LABEL_CHUNK: // Note: Synchronous message m.Body = &GetLabelChunkBody{} case FRR_ZAPI5_RELEASE_LABEL_CHUNK: // Note: Synchronous message m.Body = &ReleaseLabelChunkBody{} case FRR_ZAPI5_VRF_LABEL: m.Body = &VrfLabelBody{} default: m.Body = &UnknownBody{} } return m.Body.DecodeFromBytes(data, m.Header.Version, software) } func (m *Message) parseFrrZapi6Message(data []byte, software string) error { command := m.Header.Command if software == "frr6" { if c, err := frr6Zapi6Command(command, software, true); err == nil { command = c } else { return err } } switch command { case FRR_ZAPI6_INTERFACE_ADD, FRR_ZAPI6_INTERFACE_DELETE, FRR_ZAPI6_INTERFACE_UP, FRR_ZAPI6_INTERFACE_DOWN: m.Body = &InterfaceUpdateBody{} case FRR_ZAPI6_INTERFACE_ADDRESS_ADD, FRR_ZAPI6_INTERFACE_ADDRESS_DELETE: m.Body = &InterfaceAddressUpdateBody{} case FRR_ZAPI6_ROUTER_ID_UPDATE: m.Body = &RouterIDUpdateBody{} case FRR_ZAPI6_NEXTHOP_UPDATE: m.Body = &NexthopUpdateBody{} case FRR_ZAPI6_INTERFACE_NBR_ADDRESS_ADD, FRR_ZAPI6_INTERFACE_NBR_ADDRESS_DELETE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI6_INTERFACE_BFD_DEST_UPDATE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI6_IMPORT_CHECK_UPDATE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI6_BFD_DEST_REPLAY: // TODO m.Body = &UnknownBody{} case FRR_ZAPI6_REDISTRIBUTE_ROUTE_ADD, FRR_ZAPI6_REDISTRIBUTE_ROUTE_DEL: m.Body = &IPRouteBody{Api: m.Header.Command} case FRR_ZAPI6_INTERFACE_VRF_UPDATE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI6_INTERFACE_LINK_PARAMS: // TODO m.Body = &UnknownBody{} case FRR_ZAPI6_PW_STATUS_UPDATE: // TODO m.Body = &UnknownBody{} case FRR_ZAPI6_LABEL_MANAGER_CONNECT: // Note: Synchronous message m.Body = &LabelManagerConnectBody{} case FRR_ZAPI6_GET_LABEL_CHUNK: // Note: Synchronous message m.Body = &GetLabelChunkBody{} case FRR_ZAPI6_RELEASE_LABEL_CHUNK: // Note: Synchronous message case FRR_ZAPI6_VRF_LABEL: m.Body = &VrfLabelBody{} default: m.Body = &UnknownBody{} } return m.Body.DecodeFromBytes(data, m.Header.Version, software) } func ParseMessage(hdr *Header, data []byte, software string) (m *Message, err error) { m = &Message{Header: *hdr} if m.Header.Version == 4 { err = m.parseFrrMessage(data, software) } else if m.Header.Version == 5 { err = m.parseFrrZapi5Message(data, software) } else if m.Header.Version == 6 { err = m.parseFrrZapi6Message(data, software) } else { err = m.parseMessage(data, software) } if err != nil { return nil, err } return m, nil } // frr6Zapi6Command adjust command (API_TYPE) between Frr6 Zebra and latest Zapi6 func frr6Zapi6Command(command API_TYPE, softwareName string, from bool) (API_TYPE, error) { if softwareName != "frr6" { return command, fmt.Errorf("softwareName %s is not supported", softwareName) } // frr6 has ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD (ID:28), frr7 (zapi6 latest) removes it. // frr7 adds ZEBRA_DUPLICATE_ADDR_DETECTION (ID:72). if from && FRR_ZAPI6_BFD_DEST_REGISTER < command && command < FRR_ZAPI6_PW_ADD { // if true, command will be converted from Frr6 Zebra to latest Zapi6 (for parsing) return command - 1, nil } else if !from && FRR_ZAPI6_IMPORT_CHECK_UPDATE < command && command < FRR_ZAPI6_DUPLICATE_ADDR_DETECTION { // if false, command will be converted from latest Zapi6 to Frr6 Zebra (for send) return command + 1, nil } return command, nil } // frr4Zapi5Command adjust command (API_TYPE) between Frr4 Zebra and latest Zapi5 func frr4Zapi5Command(command API_TYPE, softwareName string, from bool) (API_TYPE, error) { if softwareName != "frr4" { return command, fmt.Errorf("softwareName %s is not supported", softwareName) } // if from is true, command will be converted from Frr4 Zebra to latest Zapi5 (for parsing) // if from is false, command will be converted from latest Zapi5 to Frr4 Zebra (for send) sign := 1 if !from { sign = -1 } /* frr5 adds ZEBRA_CAPABILITIES (ID:22), ZEBRA_VRF_LABEL (ID:42), ZEBRA_LABEL_MANAGER_CONNECT_ASYNC (ID:53), ZEBRA_ADVERTISE_SUBNET (ID:60), ZEBRA_IP_PREFIX_ROUTE_ADD (ID:70), ZEBRA_IP_PREFIX_ROUTE_DEL (ID:71) and from ZEBRA_RULE_ADD (ID:79) and ZEBRA_IPTABLE_NOTIFY_OWNER (ID:93) */ if FRR_ZAPI5_HELLO < command { // ZEBRA_HELLO: ID:21 in frr4, ID:21 in frr5 command = API_TYPE(int(command) + sign) } if FRR_ZAPI5_VRF_DELETE < command { // ZEBRA_VRF_DELETE: ID:40 in frr4, ID:41 in frr5 command = API_TYPE(int(command) + sign) } // ZEBRA_LABEL_MANAGER_CONNECT : ID:50 in frr4, ID:52 in frr5 if FRR_ZAPI5_LABEL_MANAGER_CONNECT < command { command = API_TYPE(int(command) + sign) } // ZEBRA_ADVERTISE_DEFAULT_GW : ID:56 in frr4, ID:59 in frr5 if FRR_ZAPI5_ADVERTISE_DEFAULT_GW < command { command = API_TYPE(int(command) + sign) } // ZEBRA_MACIP_DEL : ID:64 in frr4, ID:69 in frr5 if FRR_ZAPI5_MACIP_DEL < command { command = API_TYPE(int(command) + (2 * sign)) } // ZBRA_PW_SATUS_UPDATE: ID:72 in frr4, ID:78 in frr5 if FRR_ZAPI5_PW_STATUS_UPDATE < command { return command, fmt.Errorf("unsupported command") } return command, nil }