summaryrefslogtreecommitdiffhomepage
path: root/zebra/zebra.go
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/zebra.go')
-rw-r--r--zebra/zebra.go641
1 files changed, 641 insertions, 0 deletions
diff --git a/zebra/zebra.go b/zebra/zebra.go
new file mode 100644
index 00000000..65e2bba9
--- /dev/null
+++ b/zebra/zebra.go
@@ -0,0 +1,641 @@
+// 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"
+ log "github.com/Sirupsen/logrus"
+ "io"
+ "net"
+ "strings"
+ "syscall"
+)
+
+const (
+ HEADER_SIZE = 6
+ HEADER_MARKER = 255
+ VERSION = 2
+ INTERFACE_NAMSIZ = 20
+)
+
+type INTERFACE_STATUS uint8
+
+const (
+ INTERFACE_ACTIVE = 0x01
+ INTERFACE_SUB = 0x02
+ INTERFACE_LINKDETECTION = 0x04
+)
+
+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")
+ }
+ return strings.Join(ss, "|")
+}
+
+// Subsequent Address Family Identifier.
+type SAFI uint8
+
+const (
+ _ SAFI = iota
+ SAFI_UNICAST
+ SAFI_MULTICAST
+ SAFI_RESERVED_3
+ SAFI_MPLS_VPN
+ SAFI_MAX
+)
+
+// API Types.
+type API_TYPE uint16
+
+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
+ MESSAGE_MAX
+)
+
+// Route Types.
+type ROUTE_TYPE uint8
+
+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_HSLS
+ ROUTE_OLSR
+ ROUTE_BABEL
+ ROUTE_MAX
+)
+
+const (
+ MESSAGE_NEXTHOP = 0x01
+ MESSAGE_IFINDEX = 0x02
+ MESSAGE_DISTANCE = 0x04
+ MESSAGE_METRIC = 0x08
+)
+
+// 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
+)
+
+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")
+ }
+ return strings.Join(ss, "|")
+}
+
+// Nexthop Flags.
+type NEXTHOP_FLAG uint8
+
+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
+)
+
+type Client struct {
+ outgoing chan *Message
+ incoming chan *Message
+ redistDefault ROUTE_TYPE
+ conn net.Conn
+}
+
+func NewClient(network, address string, typ ROUTE_TYPE) (*Client, error) {
+ conn, err := net.Dial(network, address)
+ if err != nil {
+ return nil, err
+ }
+ outgoing := make(chan *Message)
+ go func() {
+ for {
+ m, more := <-outgoing
+ if more {
+ b, err := m.Serialize()
+ if err != nil {
+ log.Warnf("failed to serialize: %s", m)
+ continue
+ }
+
+ _, err = conn.Write(b)
+ if err != nil {
+ log.Errorf("failed to write: ", err)
+ return
+ }
+ } else {
+ log.Debug("finish outgoing loop")
+ return
+ }
+ }
+ }()
+
+ incoming := make(chan *Message, 64)
+ go func() error {
+ for {
+ headerBuf, err := readAll(conn, HEADER_SIZE)
+ if err != nil {
+ log.Error("failed to read header: ", err)
+ return err
+ }
+
+ hd := &Header{}
+ err = hd.DecodeFromBytes(headerBuf)
+ if err != nil {
+ log.Error("failed to decode header: ", err)
+ return err
+ }
+
+ bodyBuf, err := readAll(conn, int(hd.Len-HEADER_SIZE))
+ if err != nil {
+ log.Error("failed to read body: ", err)
+ return err
+ }
+
+ m, err := ParseMessage(hd, bodyBuf)
+ if err != nil {
+ log.Warn("failed to parse message: ", err)
+ continue
+ }
+
+ incoming <- m
+ }
+ }()
+ return &Client{
+ outgoing: outgoing,
+ incoming: incoming,
+ redistDefault: typ,
+ conn: conn,
+ }, 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) Recieve() chan *Message {
+ return c.incoming
+}
+
+func (c *Client) Send(m *Message) {
+ c.outgoing <- m
+}
+
+func (c *Client) SendCommand(command API_TYPE, body Body) error {
+ m := &Message{
+ Header: Header{
+ Len: HEADER_SIZE,
+ Marker: HEADER_MARKER,
+ Version: VERSION,
+ Command: command,
+ },
+ Body: body,
+ }
+ c.Send(m)
+ return nil
+}
+
+func (c *Client) SendHello() error {
+ if c.redistDefault > 0 {
+ body := &HelloBody{
+ Redist: c.redistDefault,
+ }
+ return c.SendCommand(HELLO, body)
+ }
+ return nil
+}
+
+func (c *Client) SendRouterIDAdd() error {
+ return c.SendCommand(ROUTER_ID_ADD, nil)
+}
+
+func (c *Client) SendInterfaceAdd() error {
+ return c.SendCommand(INTERFACE_ADD, nil)
+}
+
+func (c *Client) Close() error {
+ close(c.outgoing)
+ return c.conn.Close()
+}
+
+type Header struct {
+ Len uint16
+ Marker uint8
+ Version uint8
+ Command API_TYPE
+}
+
+func (h *Header) Serialize() ([]byte, error) {
+ buf := make([]byte, HEADER_SIZE)
+ binary.BigEndian.PutUint16(buf[0:], h.Len)
+ buf[2] = h.Marker
+ buf[3] = h.Version
+ binary.BigEndian.PutUint16(buf[4:], uint16(h.Command))
+ return buf, nil
+}
+
+func (h *Header) DecodeFromBytes(data []byte) error {
+ if uint16(len(data)) < HEADER_SIZE {
+ return fmt.Errorf("Not all ZAPI message header")
+ }
+ h.Len = binary.BigEndian.Uint16(data[0:2])
+ h.Marker = data[2]
+ h.Version = data[3]
+ h.Command = API_TYPE(binary.BigEndian.Uint16(data[4:6]))
+ return nil
+}
+
+type Body interface {
+ DecodeFromBytes([]byte) error
+ Serialize() ([]byte, error)
+}
+
+type HelloBody struct {
+ Redist ROUTE_TYPE
+}
+
+func (b *HelloBody) DecodeFromBytes(data []byte) error {
+ b.Redist = ROUTE_TYPE(data[0])
+ return nil
+}
+
+func (b *HelloBody) Serialize() ([]byte, error) {
+ return []byte{uint8(b.Redist)}, nil
+}
+
+type InterfaceUpdateBody struct {
+ Name string
+ Index uint32
+ Status INTERFACE_STATUS
+ Flags uint64
+ Metric uint32
+ MTU uint32
+ MTU6 uint32
+ Bandwidth uint32
+ HardwareAddr net.HardwareAddr
+}
+
+func (b *InterfaceUpdateBody) DecodeFromBytes(data []byte) error {
+ if len(data) < INTERFACE_NAMSIZ+29 {
+ return fmt.Errorf("lack of bytes. need %d but %d", INTERFACE_NAMSIZ+29, len(data))
+ }
+
+ b.Name = string(data[:INTERFACE_NAMSIZ])
+ data = data[INTERFACE_NAMSIZ:]
+ b.Index = binary.BigEndian.Uint32(data[:4])
+ b.Status = INTERFACE_STATUS(data[4])
+ b.Flags = binary.BigEndian.Uint64(data[5:13])
+ b.Metric = binary.BigEndian.Uint32(data[13:17])
+ b.MTU = binary.BigEndian.Uint32(data[17:21])
+ b.MTU6 = binary.BigEndian.Uint32(data[21:25])
+ b.Bandwidth = binary.BigEndian.Uint32(data[25:29])
+ l := binary.BigEndian.Uint32(data[29:33])
+ if l > 0 {
+ b.HardwareAddr = data[33 : 33+l]
+ }
+ return nil
+}
+
+func (b *InterfaceUpdateBody) Serialize() ([]byte, error) {
+ return []byte{}, nil
+}
+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, " | ")
+}
+
+func (b *InterfaceUpdateBody) String() string {
+ s := fmt.Sprintf("name: %s, idx: %d, status: %s, flags: %s, metric: %d, mtu: %d, mtu6: %d, bandwith: %d", b.Name, b.Index, b.Status, intfflag2string(b.Flags), b.Metric, b.MTU, b.MTU6, b.Bandwidth)
+ if len(b.HardwareAddr) > 0 {
+ return s + fmt.Sprintf(", mac: %s", b.HardwareAddr)
+ }
+ return s
+}
+
+type InterfaceAddressUpdateBody struct {
+ Index uint32
+ Flags uint8
+ Prefix net.IP
+ Length uint8
+}
+
+func (b *InterfaceAddressUpdateBody) DecodeFromBytes(data []byte) error {
+ b.Index = binary.BigEndian.Uint32(data[:4])
+ b.Flags = 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]
+ return nil
+}
+
+func (b *InterfaceAddressUpdateBody) Serialize() ([]byte, error) {
+ return []byte{}, nil
+}
+
+func (b *InterfaceAddressUpdateBody) String() string {
+ return fmt.Sprintf("idx: %d, flags: %d, addr: %s/%d", b.Index, b.Flags, b.Prefix, b.Length)
+}
+
+type RouterIDUpdateBody struct {
+ Length uint8
+ Prefix net.IP
+}
+
+func (b *RouterIDUpdateBody) DecodeFromBytes(data []byte) 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() ([]byte, error) {
+ return []byte{}, nil
+}
+
+func (b *RouterIDUpdateBody) String() string {
+ return fmt.Sprintf("id: %s/%d", b.Prefix, b.Length)
+}
+
+type IPRouteBody struct {
+ Type ROUTE_TYPE
+ Flags FLAG
+ Message uint8
+ SAFI SAFI
+ Prefix net.IP
+ PrefixLength uint8
+ Nexthops []net.IP
+ Ifindexs []uint32
+ Distance uint8
+ Metric uint32
+}
+
+func (b *IPRouteBody) DecodeFromBytes(data []byte) error {
+ b.Type = ROUTE_TYPE(data[0])
+ b.Flags = FLAG(data[1])
+ b.Message = data[2]
+ b.SAFI = SAFI(data[3])
+ b.Prefix = data[3:7]
+ b.PrefixLength = data[7]
+ return nil
+}
+
+func (b *IPRouteBody) Serialize() ([]byte, error) {
+ buf := make([]byte, 5)
+ buf[0] = uint8(b.Type)
+ buf[1] = uint8(b.Flags)
+ buf[2] = b.Message
+ binary.BigEndian.PutUint16(buf[3:], uint16(b.SAFI))
+ bitlen := b.PrefixLength
+ bytelen := (int(b.PrefixLength) + 7) / 8
+ bbuf := make([]byte, bytelen)
+ copy(bbuf, b.Prefix)
+ if bitlen%8 != 0 {
+ mask := 0xff00 >> (bitlen % 8)
+ last_byte_value := bbuf[bytelen-1] & byte(mask)
+ bbuf[bytelen-1] = last_byte_value
+ }
+ buf = append(buf, bitlen)
+ buf = append(buf, bbuf...)
+
+ if b.Message&MESSAGE_NEXTHOP > 0 {
+ if b.Flags&FLAG_BLACKHOLE > 0 {
+ buf = append(buf, []byte{1, uint8(NEXTHOP_BLACKHOLE)}...)
+ } else {
+ buf = append(buf, uint8(len(b.Nexthops)+len(b.Ifindexs)))
+ }
+
+ for _, v := range b.Nexthops {
+ if v.To4() != nil {
+ buf = append(buf, uint8(NEXTHOP_IPV4))
+ buf = append(buf, v.To4()...)
+ } else {
+ buf = append(buf, uint8(NEXTHOP_IPV6))
+ buf = append(buf, v.To16()...)
+ }
+ }
+
+ for _, v := range b.Ifindexs {
+ buf = append(buf, uint8(NEXTHOP_IFINDEX))
+ 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...)
+ }
+ return buf, nil
+}
+
+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()
+ if err != nil {
+ return nil, err
+ }
+ }
+ m.Header.Len = uint16(len(body)) + HEADER_SIZE
+ hdr, err := m.Header.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ return append(hdr, body...), nil
+}
+
+func ParseMessage(hdr *Header, data []byte) (*Message, error) {
+ m := &Message{Header: *hdr}
+
+ 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{}
+ default:
+ return nil, fmt.Errorf("Unknown zapi command: %d", m.Header.Command)
+ }
+ err := m.Body.DecodeFromBytes(data)
+ if err != nil {
+ return nil, err
+ }
+ return m, nil
+}