diff --git a/.gitignore b/.gitignore
index c456e1c..c8a0da2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,4 @@ bin/
 .go-pkg-cache/
 .idea/
 .editorconfig
-vendor/
 gobgp/
diff --git a/aixnet/route_aix.go b/aixnet/route_aix.go
new file mode 100644
index 0000000..26288f3
--- /dev/null
+++ b/aixnet/route_aix.go
@@ -0,0 +1,423 @@
+package aixnet
+
+import (
+	"fmt"
+	"net"
+	"os"
+	"syscall"
+	"unsafe"
+)
+
+//extern getkerninfo
+func getkerninfo(op int32, where uintptr, size uintptr, arg int64) int32
+
+const (
+	KINFO_VERS = 0x1<<30
+	KINFO_RT_COM = 0x1<<8
+	KINFO_RT_DUMP_COM = KINFO_RT_COM|1
+	KINFO_RT_DUMP = KINFO_RT_DUMP_COM|KINFO_VERS
+	AF_V4 = syscall.AF_INET << 24
+	AF_V6 = syscall.AF_INET6 << 24
+	KINFO_RT_DUMP_INET = KINFO_RT_DUMP|AF_V4
+	KINFO_RT_DUMP_INET6 = KINFO_RT_DUMP|AF_V6
+)
+
+type RawSockaddrDatalink struct {
+        Len uint8
+        Family uint8
+        Index uint16
+        Type uint8
+        Nlen uint8
+        Alen uint8
+        Slen uint8
+        Data [120]byte
+}
+const SizeofSockaddrDatalink = 128
+
+type Route struct {
+	Dst       *net.IPNet
+	Gw        net.IP
+	LinkIndex int
+	Flags     int
+}
+
+type RouteUpdate struct {
+	Type  int
+	Route
+}
+
+type RtMsgHdr struct {
+	MsgLen	uint16
+	Version	uint8
+	Type	uint8
+	Index	uint16
+	Pid	int32
+	Addrs	int32
+	Seq	int32
+	Errno	int32
+	Flags	int32
+	Use	int32
+	Inits	uint32
+	Metrics	[9]uint32
+}
+
+const (
+	SizeofRtMsgHdr           = 72
+	SizeofRtMsgHdrPolicy     = 16468
+	SizeofRtMsgHdrPolicyExt  = 16472
+	SizeofRtMsgHdrPolicyPrfn = 16476
+	SizeofNewRtMsgHdr        = 16460
+	SizeofNewRtMsgHdrCompat  = 332
+)
+
+func parseMsg(buf []byte) (*Route, error) {
+	rtm := (*RtMsgHdr)(unsafe.Pointer(&buf[0]))
+
+	off := uint(SizeofRtMsgHdr)
+	switch {
+	case rtm.Version == syscall.RTM_VERSION:
+	case rtm.Version == syscall.RTM_VERSION_POLICY:
+		off = SizeofRtMsgHdrPolicy
+	case rtm.Version == syscall.RTM_VERSION_POLICY_EXT:
+		off = SizeofRtMsgHdrPolicyExt
+	case rtm.Version == syscall.RTM_VERSION_POLICY_PRFN:
+		off = SizeofRtMsgHdrPolicyPrfn
+	case rtm.Version == syscall.RTM_VERSION_GR:
+		off = SizeofNewRtMsgHdr
+	case rtm.Version == syscall.RTM_VERSION_GR_COMPAT:
+		off = SizeofNewRtMsgHdrCompat
+	default:
+		// ignore
+		return nil, nil
+	}
+
+	route := &Route{}
+	route.Flags = int(rtm.Flags)
+	route.LinkIndex = int(rtm.Index)
+	for i := uint(0); i < syscall.RTAX_MAX; i++ {
+		if rtm.Addrs&(1<<i) == 0 {
+			continue
+		}
+		sa := (*syscall.RawSockaddr)(unsafe.Pointer(&buf[off]))
+		switch {
+		case i == syscall.RTAX_DST:
+			if sa.Family == syscall.AF_INET {
+				sin := (*syscall.RawSockaddrInet4)(unsafe.Pointer(sa))
+				route.Dst = &net.IPNet{IP: sin.Addr[:], Mask: net.CIDRMask(32, 32)}
+			} else if sa.Family == syscall.AF_INET6 {
+				sin := (*syscall.RawSockaddrInet6)(unsafe.Pointer(sa))
+				route.Dst = &net.IPNet{IP: sin.Addr[:], Mask: net.CIDRMask(128, 128)}
+			}
+		case i == syscall.RTAX_GATEWAY:
+			if sa.Family == syscall.AF_INET {
+				sin := (*syscall.RawSockaddrInet4)(unsafe.Pointer(sa))
+				route.Gw = sin.Addr[:]
+			} else if sa.Family == syscall.AF_INET6 {
+				sin := (*syscall.RawSockaddrInet6)(unsafe.Pointer(sa))
+				route.Gw = sin.Addr[:]
+			}
+		case i == syscall.RTAX_NETMASK:
+			if route.Dst != nil {
+				if sa.Family == syscall.AF_UNSPEC {
+// XXX IPv6 uses AF_UNSPEC for netmask too (but length==struct sockaddr_in6)
+					sin := (*syscall.RawSockaddrInet4)(unsafe.Pointer(sa))
+					route.Dst.Mask = net.CIDRMask(0, 32)
+					len := sin.Len
+					if len < 4 {
+						len = 4
+					}
+					if len > 8 {
+						len = 8
+					}
+					copy(route.Dst.Mask, sin.Addr[:len-4])
+				} else if sa.Family == syscall.AF_INET {
+					sin := (*syscall.RawSockaddrInet4)(unsafe.Pointer(sa))
+					route.Dst.Mask = sin.Addr[:]
+				} else if sa.Family == syscall.AF_INET6 {
+					sin := (*syscall.RawSockaddrInet6)(unsafe.Pointer(sa))
+					route.Dst.Mask = sin.Addr[:]
+				}
+			}
+		case i == syscall.RTAX_IFP:
+			if sa.Family == syscall.AF_LINK {
+				sdl := (*RawSockaddrDatalink)(unsafe.Pointer(sa))
+				route.LinkIndex = int(sdl.Index)
+			}
+		}
+		if sa.Len == 0 {
+			sa.Len = 4
+		}
+		off += (uint(sa.Len) + 3) &^ 3
+	}
+	return route, nil
+}
+
+func RouteGet(dst net.IP) ([]Route, error) {
+	s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
+	if err != nil {
+		return nil, err
+	}
+	defer syscall.Close(s)
+
+	buf := make([]byte, 8*4096)
+
+	// Prepare RTM_GET request
+	rtm := (*RtMsgHdr)(unsafe.Pointer(&buf[0]))
+	rtm.Type = syscall.RTM_GET
+	rtm.Version = syscall.RTM_VERSION
+	rtm.Addrs = syscall.RTA_DST
+rtm.Flags = syscall.RTF_UP | syscall.RTF_HOST | syscall.RTF_GATEWAY
+// XXX RTF_UP RTF_HOST RTF_GATEWAY ?
+	seq := int32(1)
+	rtm.Seq = seq
+	len := uint(unsafe.Sizeof(RtMsgHdr))
+	if dst.To4() != nil {
+		len += addInet(dst, syscall.AF_INET, buf[len:])
+	} else {
+		len += addInet(dst, syscall.AF_INET6, buf[len:])
+	}
+	rtm.MsgLen = uint16(len)
+
+	syscall.Write(s, buf[:len])
+
+	// Read result
+	routes := []Route{}
+	for {
+		n, err := syscall.Read(s, buf)
+		if err != nil {
+			return nil, err
+		}
+		if n == 0 {
+			break
+		}
+		rtm = (*RtMsgHdr)(unsafe.Pointer(&buf[0]))
+		if rtm.Seq != seq || int(rtm.Pid) != os.Getpid() {
+			continue
+		}
+		if rtm.Errno != 0 {
+			break
+		}
+		route, err := parseMsg(buf[:rtm.MsgLen])
+		if err != nil {
+			return nil, err
+		}
+		routes = append(routes, *route)
+		break
+	}
+	return routes, nil
+}
+
+func RouteListFiltered(family int, filterMask int32) ([]Route, error) {
+	var op int32
+	switch family {
+	case syscall.AF_UNSPEC:
+		op = KINFO_RT_DUMP
+	case syscall.AF_INET:
+		op = KINFO_RT_DUMP_INET
+	case syscall.AF_INET6:
+		op = KINFO_RT_DUMP_INET6
+	default:
+		return nil, fmt.Errorf("address family not supported")
+	}
+	needed := getkerninfo(op, 0, 0, 0)
+	if needed < 0 {
+		return nil, fmt.Errorf("getkerninfo failed")
+	}
+	tab := make([]byte, needed)
+	err := getkerninfo(op, uintptr(unsafe.Pointer(&tab[0])), uintptr(unsafe.Pointer(&needed)), 0)
+	if err < 0 {
+		return nil, fmt.Errorf("getkerninfo failed")
+	}
+	tab = tab[:needed]
+
+	routes := []Route{}
+	for len(tab) > 0 {
+		rtm := (*RtMsgHdr)(unsafe.Pointer(&tab[0]))
+		if rtm.MsgLen == 0 {
+			break
+		}
+		route, err := parseMsg(tab[:rtm.MsgLen])
+		if err != nil {
+			return nil, err
+		}
+		if rtm.Flags&filterMask == filterMask {
+			routes = append(routes, *route)
+		}
+		tab = tab[rtm.MsgLen:]
+	}
+	return routes, nil
+}
+
+func addInet(ip net.IP, family int, buf []byte) uint {
+	len := uint(0)
+	if family == syscall.AF_INET {
+		sin := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&buf[0]))
+		sin.Len = syscall.SizeofSockaddrInet4
+		sin.Family = syscall.AF_INET
+		copy(sin.Addr[:], ip[:])
+		len += (uint(sin.Len) + 3) &^ 3
+	} else if family == syscall.AF_INET6 {
+		sin := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&buf[0]))
+		sin.Len = syscall.SizeofSockaddrInet6
+		sin.Family = syscall.AF_INET6
+		copy(sin.Addr[:], ip[:])
+		len += (uint(sin.Len) + 3) &^ 3
+	}
+	return len
+}
+
+func marshallRoute(route *Route, buf []byte) (uint, error) {
+	rtm := (*RtMsgHdr)(unsafe.Pointer(&buf[0]))
+	off := uint(unsafe.Sizeof(RtMsgHdr))
+
+	// Add Destination IP address
+	if route.Dst != nil && route.Dst.IP != nil {
+		rtm.Addrs |= syscall.RTA_DST
+		if route.Dst.IP.To4() != nil {
+			off += addInet(route.Dst.IP, syscall.AF_INET, buf[off:])
+		} else {
+			off += addInet(route.Dst.IP, syscall.AF_INET6, buf[off:])
+		}
+	}
+	// Add Gateway IP address
+	if route.Gw != nil {
+		rtm.Flags |= syscall.RTF_GATEWAY
+		rtm.Addrs |= syscall.RTA_GATEWAY
+		if route.Gw.To4() != nil {
+			off += addInet(route.Gw, syscall.AF_INET, buf[off:])
+		} else {
+			off += addInet(route.Gw, syscall.AF_INET6, buf[off:])
+		}
+	}
+	// Add netmask
+	if route.Dst != nil && route.Dst.Mask != nil {
+		one, bits := route.Dst.Mask.Size()
+		if one == bits {
+			rtm.Flags |= syscall.RTF_HOST
+		} else {
+			rtm.Addrs |= syscall.RTA_NETMASK
+// XXX AF_UNSPEC
+			if route.Dst.IP.To4() != nil {
+				off += addInet(net.IP(route.Dst.Mask), syscall.AF_INET, buf[off:])
+			} else {
+				off += addInet(net.IP(route.Dst.Mask), syscall.AF_INET6, buf[off:])
+			}
+		}
+	}
+	// Add network interface index
+	if route.LinkIndex != 0 {
+		rtm.Addrs |= syscall.RTA_IFP
+		sdl := (*RawSockaddrDatalink)(unsafe.Pointer(&buf[off]))
+		sdl.Family = syscall.AF_LINK
+		sdl.Len = SizeofSockaddrDatalink
+		sdl.Index = uint16(route.LinkIndex)
+		off += (uint(sdl.Len) + 3) &^ 3
+	}
+	return off, nil
+}
+
+func RouteDel(route *Route) error {
+	s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
+	if err != nil {
+		return err
+	}
+	defer syscall.Close(s)
+
+	buf := make([]byte, 4096)
+
+	// Prepare RTM_DELETE request
+	rtm := (*RtMsgHdr)(unsafe.Pointer(&buf[0]))
+	rtm.Type = syscall.RTM_DELETE
+	rtm.Version = syscall.RTM_VERSION
+	rtm.Flags = int32(route.Flags)
+	seq := int32(1)
+	rtm.Seq = seq
+	len, _ := marshallRoute(route, buf[:])
+	rtm.MsgLen = uint16(len)
+
+	syscall.Write(s, buf[:len])
+
+	return nil
+}
+
+func RouteReplace(route *Route) error {
+	s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
+	if err != nil {
+		return err
+	}
+	defer syscall.Close(s)
+
+	buf := make([]byte, 4096)
+
+delroute := *route
+RouteDel(&delroute)
+
+// Try RTM_CHANGE first, if it fails with ESRCH, try RTM_ADD
+
+	// Prepare RTM_ADD request
+	rtm := (*RtMsgHdr)(unsafe.Pointer(&buf[0]))
+	rtm.Type = syscall.RTM_ADD
+	rtm.Version = syscall.RTM_VERSION
+	rtm.Flags = int32(route.Flags)
+	rtm.Flags |= syscall.RTF_UP
+	seq := int32(1)
+	rtm.Seq = seq
+	len, _ := marshallRoute(route, buf[:])
+	rtm.MsgLen = uint16(len)
+
+	syscall.Write(s, buf[:len])
+
+	return nil
+}
+
+func RouteSubscribe(ch chan RouteUpdate, done chan struct{}) error {
+// XXX meme socket pour IPv4 et IPv6 ???
+	s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
+	if err != nil {
+		return err
+	}
+	go func() {
+		defer syscall.Close(s)
+
+		buf := make([]byte, 8*4096)
+		for {
+			n, err := syscall.Read(s, buf)
+			if err != nil {
+				return
+			}
+			if n == 0 {
+				break
+			}
+			rtm := (*RtMsgHdr)(unsafe.Pointer(&buf[0]))
+			if int(rtm.MsgLen) > n {
+				continue
+			}
+			if rtm.Flags&syscall.RTF_DONE == 0 {
+				continue
+			}
+// Only RTF_DONE !!! => sinon on voit passer les requetes qui echouent!
+// Only RTF_STATIC & !RTF_PROTO1 => created by kernel???
+// Process only ADD, DEL, CHANGE? and NEWADDR/DELADDR?
+			if rtm.Type == syscall.RTM_ADD {
+				_, err := parseMsg(buf[:rtm.MsgLen])
+				if err != nil {
+					continue
+				}
+			//	ch <- RouteUpdate{Type: int(rtm.Type), Route: *route}
+			} else if rtm.Type == syscall.RTM_DELETE {
+				_, err := parseMsg(buf[:rtm.MsgLen])
+				if err != nil {
+					continue
+				}
+		//		ch <- RouteUpdate{Type: int(rtm.Type), Route: *route}
+			}
+		}
+	}()
+
+	return nil
+}
+
+func ParseIPNet(s string) (*net.IPNet, error) {
+	_, ipnet, err := net.ParseCIDR(s)
+	return ipnet, err
+}
diff --git a/main.go b/main.go
index 0e6ed5a..c033577 100644
--- a/main.go
+++ b/main.go
@@ -30,10 +30,10 @@ import (
 	svbgp "github.com/osrg/gobgp/packet/bgp"
 	bgpserver "github.com/osrg/gobgp/server"
 	bgptable "github.com/osrg/gobgp/table"
+	"github.com/projectcalico/calico-bgp-daemon/aixnet"
 	"github.com/projectcalico/libcalico-go/lib/apiconfig"
 	"github.com/projectcalico/libcalico-go/lib/numorstring"
 	log "github.com/sirupsen/logrus"
-	"github.com/vishvananda/netlink"
 	"gopkg.in/tomb.v2"
 )
 
@@ -85,7 +85,7 @@ type Processor interface {
 }
 
 // VERSION is filled out during the build process (using git describe output)
-var VERSION string
+var VERSION string = "fkakuma-v3"
 
 func underscore(ip string) string {
 	return strings.Map(func(r rune) rune {
@@ -178,7 +178,7 @@ func GetNodeMap(values map[string]string) map[string]map[string]string {
 // When the BGP nexthop can be reached with a connected route,
 // this function returns the BGP nexthop.
 func recursiveNexthopLookup(bgpNexthop net.IP) (net.IP, error) {
-	routes, err := netlink.RouteGet(bgpNexthop)
+	routes, err := aixnet.RouteGet(bgpNexthop)
 	if err != nil {
 		return nil, err
 	}
@@ -186,7 +186,7 @@ func recursiveNexthopLookup(bgpNexthop net.IP) (net.IP, error) {
 		return nil, fmt.Errorf("no route for path: %s", bgpNexthop)
 	}
 	r := routes[0]
-	if r.Gw != nil {
+	if r.Flags&syscall.RTF_GATEWAY != 0 && r.Gw != nil {
 		return r.Gw, nil
 	}
 	// bgpNexthop can be reached by a connected route
@@ -195,19 +195,16 @@ func recursiveNexthopLookup(bgpNexthop net.IP) (net.IP, error) {
 
 func cleanUpRoutes() error {
 	log.Println("Clean up injected routes")
-	filter := &netlink.Route{
-		Protocol: RTPROT_GOBGP,
-	}
-	list4, err := netlink.RouteListFiltered(netlink.FAMILY_V4, filter, netlink.RT_FILTER_PROTOCOL)
+	list4, err := aixnet.RouteListFiltered(syscall.AF_INET, syscall.RTF_PROTO1)
 	if err != nil {
 		return err
 	}
-	list6, err := netlink.RouteListFiltered(netlink.FAMILY_V6, filter, netlink.RT_FILTER_PROTOCOL)
+	list6, err := aixnet.RouteListFiltered(syscall.AF_INET6, syscall.RTF_PROTO1)
 	if err != nil {
 		return err
 	}
 	for _, route := range append(list4, list6...) {
-		netlink.RouteDel(&route)
+		aixnet.RouteDel(&route)
 	}
 	return nil
 }
@@ -388,10 +385,7 @@ func isCrossSubnet(gw net.IP, subnet string) bool {
 }
 
 func (s *Server) ipamUpdateHandler(pool *IpPool) error {
-	filter := &netlink.Route{
-		Protocol: RTPROT_GOBGP,
-	}
-	list, err := netlink.RouteListFiltered(netlink.FAMILY_V4, filter, netlink.RT_FILTER_PROTOCOL)
+	list, err := aixnet.RouteListFiltered(syscall.AF_INET, syscall.RTF_PROTO1)
 	if err != nil {
 		return err
 	}
@@ -420,7 +414,6 @@ func (s *Server) ipamUpdateHandler(pool *IpPool) error {
 					return err
 				}
 				route.LinkIndex = i.Index
-				route.SetFlag(netlink.FLAG_ONLINK)
 			} else {
 				tbl, err := s.BgpServer.GetRib("", svbgp.RF_IPv4_UC, []*bgptable.LookupPrefix{
 					&bgptable.LookupPrefix{
@@ -446,7 +439,7 @@ func (s *Server) ipamUpdateHandler(pool *IpPool) error {
 				}
 				route.Gw = gw
 				route.Flags = 0
-				rs, err := netlink.RouteGet(gw)
+				rs, err := aixnet.RouteGet(gw)
 				if err != nil {
 					return err
 				}
@@ -456,7 +449,7 @@ func (s *Server) ipamUpdateHandler(pool *IpPool) error {
 				r := rs[0]
 				route.LinkIndex = r.LinkIndex
 			}
-			return netlink.RouteReplace(&route)
+			return aixnet.RouteReplace(&route)
 		}
 	}
 	return nil
@@ -529,7 +522,7 @@ func (s *Server) MakePath(prefix string, isWithdrawal bool) (*bgptable.Path, err
 	return bgptable.NewPath(nil, nlri, isWithdrawal, attrs, time.Now(), false), nil
 }
 
-// watchKernelRoute receives netlink route update notification and announces
+// watchKernelRoute receives kernel route update notification and announces
 // kernel/boot routes using BGP.
 func (s *Server) watchKernelRoute() error {
 	err := s.loadKernelRoute()
@@ -537,27 +530,22 @@ func (s *Server) watchKernelRoute() error {
 		return err
 	}
 
-	ch := make(chan netlink.RouteUpdate)
-	err = netlink.RouteSubscribe(ch, nil)
+	ch := make(chan aixnet.RouteUpdate)
+	err = aixnet.RouteSubscribe(ch, nil)
 	if err != nil {
 		return err
 	}
 	for update := range ch {
 		log.Printf("kernel update: %s", update)
-		if update.Table == syscall.RT_TABLE_MAIN && (update.Protocol == syscall.RTPROT_KERNEL || update.Protocol == syscall.RTPROT_BOOT) {
+		if update.Type == syscall.RTM_ADD || update.Type == syscall.RTM_DELETE {
 			// TODO: handle ipPool deletion. RTM_DELROUTE message
 			// can belong to previously valid ipPool.
 			if s.ipam.Match(update.Dst.String()) == nil {
 				continue
 			}
 			isWithdrawal := false
-			switch update.Type {
-			case syscall.RTM_DELROUTE:
+			if update.Type == syscall.RTM_DELETE {
 				isWithdrawal = true
-			case syscall.RTM_NEWROUTE:
-			default:
-				log.Printf("unhandled rtm type: %d", update.Type)
-				continue
 			}
 			path, err := s.MakePath(update.Dst.String(), isWithdrawal)
 			if err != nil {
@@ -567,7 +555,7 @@ func (s *Server) watchKernelRoute() error {
 			if _, err = s.BgpServer.AddPath("", []*bgptable.Path{path}); err != nil {
 				return err
 			}
-		} else if update.Table == syscall.RT_TABLE_LOCAL {
+		} else if update.Type == syscall.RTM_NEWADDR || update.Type == syscall.RTM_DELADDR {
 			// This means the interface address is updated
 			// Some routes we injected may be deleted by the kernel
 			// Reload routes from BGP RIB and inject again
@@ -583,35 +571,10 @@ func (s *Server) watchKernelRoute() error {
 			s.reloadCh <- tbl.Bests("")
 		}
 	}
-	return fmt.Errorf("netlink route subscription ended")
+	return fmt.Errorf("aixnet route subscription ended")
 }
 
 func (s *Server) loadKernelRoute() error {
-	filter := &netlink.Route{
-		Table: syscall.RT_TABLE_MAIN,
-	}
-	list, err := netlink.RouteListFiltered(netlink.FAMILY_V4, filter, netlink.RT_FILTER_TABLE)
-	if err != nil {
-		return err
-	}
-	for _, route := range list {
-		if route.Dst == nil {
-			continue
-		}
-		if s.ipam.Match(route.Dst.String()) == nil {
-			continue
-		}
-		if route.Protocol == syscall.RTPROT_KERNEL || route.Protocol == syscall.RTPROT_BOOT {
-			path, err := s.MakePath(route.Dst.String(), false)
-			if err != nil {
-				return err
-			}
-			log.Printf("made path from kernel route: %s", path)
-			if _, err = s.BgpServer.AddPath("", []*bgptable.Path{path}); err != nil {
-				return err
-			}
-		}
-	}
 	return nil
 }
 
@@ -620,11 +583,11 @@ func (s *Server) loadKernelRoute() error {
 func (s *Server) injectRoute(path *bgptable.Path) error {
 	nexthop := path.GetNexthop()
 	nlri := path.GetNlri()
-	dst, _ := netlink.ParseIPNet(nlri.String())
-	route := &netlink.Route{
-		Dst:      dst,
-		Gw:       nexthop,
-		Protocol: RTPROT_GOBGP,
+	dst, _ := aixnet.ParseIPNet(nlri.String())
+	route := &aixnet.Route{
+		Dst:   dst,
+		Gw:    nexthop,
+		Flags: syscall.RTF_PROTO1,
 	}
 
 	ipip := false
@@ -650,7 +613,6 @@ func (s *Server) injectRoute(path *bgptable.Path) error {
 					return err
 				}
 				route.LinkIndex = i.Index
-				route.SetFlag(netlink.FLAG_ONLINK)
 			}
 		}
 		// TODO: if !IsWithdraw, we'd ignore that
@@ -658,7 +620,7 @@ func (s *Server) injectRoute(path *bgptable.Path) error {
 
 	if path.IsWithdraw {
 		log.Printf("removed route %s from kernel", nlri)
-		return netlink.RouteDel(route)
+		return aixnet.RouteDel(route)
 	}
 	if !ipip {
 		gw, err := recursiveNexthopLookup(path.GetNexthop())
@@ -668,7 +630,7 @@ func (s *Server) injectRoute(path *bgptable.Path) error {
 		route.Gw = gw
 	}
 	log.Printf("added route %s to kernel %s", nlri, route)
-	return netlink.RouteReplace(route)
+	return aixnet.RouteReplace(route)
 }
 
 // watchBGPPath watches BGP routes from other peers and inject them into
diff --git a/vendor/github.com/fsnotify/fsnotify/aix.go b/vendor/github.com/fsnotify/fsnotify/aix.go
new file mode 100644
index 0000000..2f2296c
--- /dev/null
+++ b/vendor/github.com/fsnotify/fsnotify/aix.go
@@ -0,0 +1,437 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix 
+
+package fsnotify
+
+import (
+	"os"
+	"path/filepath"
+	"io/ioutil"
+	"time"
+	"fmt"
+	"sync"
+	"strings"
+        "errors"
+)
+
+const (
+        sleepTime time.Duration = 1000000000
+)
+
+var (
+	ErrWatchedFileDeleted = errors.New("error: watched file or folder deleted")
+)
+
+// Watcher watches a set of files, delivering events to a channel.
+type Watcher struct {
+	Events   chan Event
+	Errors   chan error
+	mu       *sync.Mutex     // Map access
+        closed chan struct{}     // Channel to respond to Close
+        close chan struct{}
+        wg *sync.WaitGroup
+        running      bool
+	names        map[string]bool        // bool for recursive or not.
+	files        map[string]os.FileInfo // map of files.
+	ignored      map[string]struct{}    // ignored files or directories.
+        ops map[Op]struct{} // Op filtering.
+        maxEvents int
+}
+
+// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
+func NewWatcher() (*Watcher, error) {
+        // Set up the WaitGroup for w.Wait().
+	var wg sync.WaitGroup
+        wg.Add(1)
+
+	w := &Watcher{
+                Events:  make(chan Event),
+		Errors:  make(chan error),
+		closed:  make(chan struct{}),
+		close:   make(chan struct{}),
+		mu:      new(sync.Mutex),
+                wg:      &wg,
+		files:   make(map[string]os.FileInfo),
+		ignored: make(map[string]struct{}),
+                names: make(map[string]bool),
+	}
+	go w.readEvents()
+	return w, nil
+}
+
+// Close removes all watches and closes the events channel.
+func (w *Watcher) Close() error {
+        w.mu.Lock()
+	if !w.running {
+		w.mu.Unlock()
+		return nil
+	}
+	w.running = false
+	w.files = make(map[string]os.FileInfo)
+	w.names = make(map[string]bool)
+	w.mu.Unlock()
+	// Send a close signal to the Start method.
+        w.close <- struct{}{}
+        return nil
+}
+
+// Add starts watching the named file or directory (non-recursively).
+func (w *Watcher) Add(name string) error {
+        w.mu.Lock()
+	defer w.mu.Unlock()
+
+	name, err := filepath.Abs(name)
+	if err != nil {
+		return err
+	}
+
+	// If name is on the ignored list or if hidden files are
+	// ignored and name is a hidden file or directory, simply return.
+	_, ignored := w.ignored[name]
+	if ignored || (strings.HasPrefix(name, ".")) {
+		return nil
+	}
+
+	// Add the directory's contents to the files list.
+	fileList, err := w.list(name)
+	if err != nil {
+		return err
+	}
+	for k, v := range fileList {
+		w.files[k] = v
+	}
+
+	// Add the name to the names list.
+	w.names[name] = false
+
+        return nil
+}
+
+// Remove stops watching the the named file or directory (non-recursively).
+func (w *Watcher) Remove(name string) error {
+        w.mu.Lock()
+	defer w.mu.Unlock()
+
+	name, err := filepath.Abs(name)
+	if err != nil {
+		return err
+	}
+
+	// Remove the name from w's names list.
+	delete(w.names, name)
+
+	// If name is a single file, remove it and return.
+	info, found := w.files[name]
+	if !found {
+		return nil // Doesn't exist, just return.
+	}
+	if !info.IsDir() {
+		delete(w.files, name)
+		return nil
+	}
+
+	// Delete the actual directory from w.files
+	delete(w.files, name)
+
+	// If it's a directory, delete all of it's contents from w.files.
+	for path := range w.files {
+		if filepath.Dir(path) == name {
+			delete(w.files, path)
+		}
+	}
+        return nil
+}
+
+func (w *Watcher) list(name string) (map[string]os.FileInfo, error) {
+	fileList := make(map[string]os.FileInfo)
+
+	// Make sure name exists.
+	stat, err := os.Stat(name)
+	if err != nil {
+		return nil, err
+	}
+
+	fileList[name] = stat
+
+	// If it's not a directory, just return.
+	if !stat.IsDir() {
+		return fileList, nil
+	}
+
+	// It's a directory.
+	fInfoList, err := ioutil.ReadDir(name)
+	if err != nil {
+		return nil, err
+	}
+	// Add all of the files in the directory to the file list as long
+	// as they aren't on the ignored list or are hidden files if ignoreHidden
+	// is set to true.
+	for _, fInfo := range fInfoList {
+		path := filepath.Join(name, fInfo.Name())
+		_, ignored := w.ignored[path]
+		if ignored || (strings.HasPrefix(fInfo.Name(), ".")) {
+			continue
+		}
+		fileList[path] = fInfo
+	}
+	return fileList, nil
+}
+
+func (w *Watcher) retrieveFileList() map[string]os.FileInfo {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+
+	fileList := make(map[string]os.FileInfo)
+
+	var list map[string]os.FileInfo
+	var err error
+
+	for name, recursive := range w.names {
+		if recursive {
+			list, err = w.listRecursive(name)
+			if err != nil {
+				if os.IsNotExist(err) {
+					w.Errors <- ErrWatchedFileDeleted
+					w.mu.Unlock()
+					w.removeRecursive(name)
+					w.mu.Lock()
+				} else {
+					w.Errors <- err
+				}
+			}
+		} else {
+			list, err = w.list(name)
+			if err != nil {
+				if os.IsNotExist(err) {
+					w.Errors <- ErrWatchedFileDeleted
+					w.mu.Unlock()
+					w.Remove(name)
+					w.mu.Lock()
+				} else {
+					w.Errors <- err
+				}
+			}
+		}
+		// Add the file's to the file list.
+		for k, v := range list {
+			fileList[k] = v
+		}
+	}
+
+	return fileList
+}
+
+func (w *Watcher) readEvents() {
+	// Make sure the Watcher is not already running.
+	w.mu.Lock()
+	if w.running {
+		w.mu.Unlock()
+		return
+	}
+	w.running = true
+	w.mu.Unlock()
+
+	// Unblock w.Wait().
+	w.wg.Done()
+
+	for {
+		// done lets the inner polling cycle loop know when the
+		// current cycle's method has finished executing.
+		done := make(chan struct{})
+
+		// Any events that are found are first piped to evt before
+		// being sent to the main Event channel.
+		evt := make(chan Event)
+
+		// Retrieve the file list for all watched file's and dirs.
+		fileList := w.retrieveFileList()
+
+		// cancel can be used to cancel the current event polling function.
+		cancel := make(chan struct{})
+
+		// Look for events.
+		go func() {
+			w.pollEvents(fileList, evt, cancel)
+			done <- struct{}{}
+		}()
+
+		// numEvents holds the number of events for the current cycle.
+                numEvents := 0
+inner:
+		for {
+			select {
+			case <-w.close:
+				close(cancel)
+				close(w.closed)
+				return
+			case event := <-evt:
+				if len(w.ops) > 0 { // Filter Ops.
+					_, found := w.ops[event.Op]
+					if !found {
+						continue
+					}
+				}
+				numEvents++
+				if w.maxEvents > 0 && numEvents > w.maxEvents {
+					close(cancel)
+					break inner
+				}
+				w.Events <- event
+			case <-done: // Current cycle is finished.
+				break inner
+			}
+		}
+
+		// Update the file's list.
+		w.mu.Lock()
+		w.files = fileList
+		w.mu.Unlock()
+
+		// Sleep and then continue to the next loop iteration.
+		time.Sleep(sleepTime)
+	}
+}
+
+func (w *Watcher) pollEvents(files map[string]os.FileInfo, evt chan Event,
+	cancel chan struct{}) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+
+	// Store create and remove events for use to check for rename events.
+	creates := make(map[string]os.FileInfo)
+	removes := make(map[string]os.FileInfo)
+
+	// Check for removed files.
+	for path, info := range w.files {
+		if _, found := files[path]; !found {
+			removes[path] = info
+		}
+	}
+
+	// Check for created files, writes and chmods.
+	for path, info := range files {
+		oldInfo, found := w.files[path]
+		if !found {
+			// A file was created.
+			creates[path] = info
+			continue
+		}
+		if oldInfo.ModTime() != info.ModTime() {
+			select {
+			case <-cancel:
+				return
+			case evt <- Event{path, Write}:
+			}
+		}
+		if oldInfo.Mode() != info.Mode() {
+			select {
+			case <-cancel:
+				return
+			case evt <- Event{path, Chmod}:
+			}
+		}
+	}
+
+	// Check for renames and moves.
+	for path1, info1 := range removes {
+		for path2, info2 := range creates {
+			if os.SameFile(info1, info2) {
+				e := Event{
+					Op:       Rename, // was "Moved"
+					Name:     fmt.Sprintf("%s -> %s", path1, path2),
+					//FileInfo: info1,
+				}
+				// If they are from the same directory, it's a rename
+				// instead of a move event.
+				if filepath.Dir(path1) == filepath.Dir(path2) {
+					e.Op = Rename
+				}
+
+				delete(removes, path1)
+				delete(creates, path2)
+
+				select {
+				case <-cancel:
+					return
+				case evt <- e:
+				}
+			}
+		}
+	}
+
+	// Send all the remaining create and remove events.
+	for path,_ := range creates {
+		select {
+		case <-cancel:
+			return
+		case evt <- Event{path, Create}:
+		}
+	}
+	for path,_ := range removes {
+		select {
+		case <-cancel:
+			return
+		case evt <- Event{path, Remove}:
+		}
+	}
+}
+
+// Remove removes either a single file or a directory recursively from
+// the file's list.
+func (w *Watcher) removeRecursive(name string) (err error) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+
+	name, err = filepath.Abs(name)
+	if err != nil {
+		return err
+	}
+
+	// Remove the name from w's names list.
+	delete(w.names, name)
+
+	// If name is a single file, remove it and return.
+	info, found := w.files[name]
+	if !found {
+		return nil // Doesn't exist, just return.
+	}
+	if !info.IsDir() {
+		delete(w.files, name)
+		return nil
+	}
+
+	// If it's a directory, delete all of it's contents recursively
+	// from w.files.
+	for path := range w.files {
+		if strings.HasPrefix(path, name) {
+			delete(w.files, path)
+		}
+	}
+	return nil
+}
+
+func (w *Watcher) listRecursive(name string) (map[string]os.FileInfo, error) {
+	fileList := make(map[string]os.FileInfo)
+
+	return fileList, filepath.Walk(name, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+		// If path is ignored and it's a directory, skip the directory. If it's
+		// ignored and it's a single file, skip the file.
+		_, ignored := w.ignored[path]
+		if ignored || (strings.HasPrefix(info.Name(), ".")) {
+			if info.IsDir() {
+				return filepath.SkipDir
+			}
+			return nil
+		}
+		// Add the path and it's info to the file list.
+		fileList[path] = info
+		return nil
+	})
+}
+
diff --git a/vendor/github.com/osrg/gobgp/zebra/zapi_bsd.go b/vendor/github.com/osrg/gobgp/zebra/zapi_bsd.go
index 8960e79..a62e82b 100644
--- a/vendor/github.com/osrg/gobgp/zebra/zapi_bsd.go
+++ b/vendor/github.com/osrg/gobgp/zebra/zapi_bsd.go
@@ -13,7 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// +build freebsd netbsd openbsd
+// +build aix freebsd netbsd openbsd
 
 package zebra
 
diff --git a/vendor/github.com/sirupsen/logrus/terminal_aix.go b/vendor/github.com/sirupsen/logrus/terminal_aix.go
new file mode 100644
index 0000000..6841999
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/terminal_aix.go
@@ -0,0 +1,26 @@
+// Based on ssh/terminal:
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build aix
+
+package logrus
+
+import (
+	"io"
+	"os"
+	"syscall"
+)
+
+// IsTerminal returns true if stderr's file descriptor is a terminal.
+func IsTerminal(f io.Writer) bool {
+	var termios syscall.Termios
+	switch v := f.(type) {
+	case *os.File:
+		err := syscall.Tcgetattr(int(v.Fd()), &termios)
+		return err == nil
+	default:
+		return false
+	}
+}
diff --git a/vendor/github.com/spf13/afero/const_bsds.go b/vendor/github.com/spf13/afero/const_bsds.go
index 5728243..18b4582 100644
--- a/vendor/github.com/spf13/afero/const_bsds.go
+++ b/vendor/github.com/spf13/afero/const_bsds.go
@@ -11,7 +11,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// +build darwin openbsd freebsd netbsd dragonfly
+// +build aix darwin openbsd freebsd netbsd dragonfly
 
 package afero
 
diff --git a/vendor/github.com/spf13/afero/const_win_unix.go b/vendor/github.com/spf13/afero/const_win_unix.go
index 968fc27..a407a67 100644
--- a/vendor/github.com/spf13/afero/const_win_unix.go
+++ b/vendor/github.com/spf13/afero/const_win_unix.go
@@ -10,6 +10,7 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+// +build !aix
 // +build !darwin
 // +build !openbsd
 // +build !freebsd
diff --git a/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go b/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go
new file mode 100644
index 0000000..c906345
--- /dev/null
+++ b/vendor/golang.org/x/crypto/ssh/terminal/util_aix.go
@@ -0,0 +1,102 @@
+// Based on ssh/terminal:
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package terminal
+
+import (
+	"io"
+	"syscall"
+)
+
+// State represents the state of the terminal.
+type State struct {
+	termios syscall.Termios
+}
+
+// IsTerminal returns true if the given file descriptor is a terminal.
+func IsTerminal(fd int) bool {
+	var termios syscall.Termios
+	err := syscall.Tcgetattr(fd, &termios)
+	return err == nil
+}
+
+// MakeRaw put the terminal connected to the given file descriptor into raw
+// mode and returns the previous state of the terminal so that it can be
+// restored.
+func MakeRaw(fd int) (*State, error) {
+	var oldState State
+	if err := syscall.Tcgetattr(fd, &oldState.termios); err != nil {
+		return nil, err
+	}
+
+	newState := oldState.termios
+	newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
+	newState.Oflag &^= syscall.OPOST
+	newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
+	newState.Cflag &^= syscall.CSIZE | syscall.PARENB
+	newState.Cflag |= syscall.CS8
+
+	if err := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); err != nil {
+		return nil, err
+	}
+	return &oldState, nil
+}
+
+// Restore restores the terminal connected to the given file descriptor to a
+// previous state.
+func Restore(fd int, oldState *State) error {
+	return syscall.Tcsetattr(fd, syscall.TCSANOW, &oldState.termios)
+}
+
+// passwordReader is an io.Reader that reads from a specific file descriptor.
+type passwordReader int
+
+func (r passwordReader) Read(buf []byte) (int, error) {
+	return syscall.Read(int(r), buf)
+}
+
+// ReadPassword reads a line of input from a terminal without local echo.  This
+// is commonly used for inputting passwords and other sensitive data. The slice
+// returned does not include the \n.
+func ReadPassword(fd int) ([]byte, error) {
+	var oldState syscall.Termios
+	if err := syscall.Tcgetattr(fd, &oldState); err != nil {
+		return nil, err
+	}
+
+	newState := oldState
+	newState.Lflag &^= syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL
+	if err := syscall.Tcsetattr(fd, syscall.TCSANOW, &newState); err != nil {
+		return nil, err
+	}
+
+	defer func() {
+		syscall.Tcsetattr(fd, syscall.TCSANOW, &oldState)
+	}()
+
+	var buf [16]byte
+	var ret []byte
+	for {
+		n, err := syscall.Read(fd, buf[:])
+		if err != nil {
+			return nil, err
+		}
+		if n == 0 {
+			if len(ret) == 0 {
+				return nil, io.EOF
+			}
+			break
+		}
+		if buf[n-1] == '\n' {
+			n--
+		}
+		ret = append(ret, buf[:n]...)
+		if n < len(buf) {
+			break
+		}
+	}
+
+	return ret, nil
+}
