/*- * Copyright (c) 2007 * Internet Initiative Japan Inc. All rights reserved. */ /* $Id: rt_zebra.c,v 1.1 2010/01/11 04:20:57 yasuoka Exp $ */ /* * @file This file provides utility functions to help add/delete routing * information with GNU Zebra. This utility uses event(3) and uses a UNIX * domain socket at communication with the zserv(Zebra). *

* example: *

    rt_zebra *rtz;

    rtz = rt_zebra_get_instance();
    rt_zebra_init(rtz);
    rt_zebra_start(rtz);

    // add 10.0.0.0/8 to a blackhole
    rt_zebra_add_ipv4_blackhole_rt(rtz, 0x0a000000, 0xff00000);

    // delete 10.0.0.0/8
    rt_zebra_delete_ipv4_blackhole_rt(rtz, 0x0a000000, 0xff00000);

    rt_zebra_stop(rtz);
    rt_zebra_fini(rtz);
 * 

*/ /* compile-time options */ #ifndef RT_ZEBRA_BLACKHOLE_IFNAME #define RT_ZEBRA_BLACKHOLE_IFNAME "lo0" #endif #ifndef DEFAULT_RT_ZEBRA_BLACKHOLE_DISTANCE #define DEFAULT_RT_ZEBRA_BLACKHOLE_DISTANCE 16 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debugutil.h" #include "bytebuf.h" #include "net_utils.h" #include #include #include #include "rt_zebra.h" #include "rt_zebra_local.h" static void rt_zebra_set_event (rt_zebra *); static void rt_zebra_io_event (int, short, void *); static int rt_zebra_log (rt_zebra *, int, const char *, ...) __printflike(3,4); static int rt_zebra_ipv4_blackhole_rt0(rt_zebra *, uint32_t, uint32_t, int); static rt_zebra rt_zebra_singleton; /* singleton */ static int rt_zebra_blackhole_ifidx = -1; /** Returns the only one rt_zebra context. */ rt_zebra * rt_zebra_get_instance(void) { return &rt_zebra_singleton; } /** Initialize the given rt_zebra context. */ int rt_zebra_init(rt_zebra *_this) { RT_ZEBRA_ASSERT((_this->state == ZEBRA_STATUS_INIT0)); memset(_this, 0, sizeof(rt_zebra)); _this->sock = -1; if ((_this->buffer = bytebuffer_create(8192)) == NULL) return 1; _this->state = ZEBRA_STATUS_INIT; if (rt_zebra_blackhole_ifidx == -1) rt_zebra_blackhole_ifidx = if_nametoindex( RT_ZEBRA_BLACKHOLE_IFNAME); return 0; } /** Finalialize the given rt_zebra context. */ void rt_zebra_fini(rt_zebra *_this) { if (_this->state != ZEBRA_STATUS_STOPPED && _this->state != ZEBRA_STATUS_DISPOSING) rt_zebra_stop(_this); if (_this->buffer != NULL) bytebuffer_destroy(_this->buffer); _this->buffer = NULL; _this->state = ZEBRA_STATUS_DISPOSING; } /** * Add the specified IPv4 blackhole routing entry. * @param addr the detination IPv4 address part in host byte-order. * @param mask the detination IPv4 netmask part in host byte-order. */ int rt_zebra_add_ipv4_blackhole_rt(rt_zebra *_this, uint32_t addr, uint32_t mask) { return rt_zebra_ipv4_blackhole_rt0(_this, addr, mask, 0); } /** * Deletes the specified IPv4 blackhole routing entry. * @param addr the detination IPv4 address part in host byte-order. * @param mask the detination IPv4 netmask part in host byte-order. */ int rt_zebra_delete_ipv4_blackhole_rt(rt_zebra *_this, uint32_t addr, uint32_t mask) { return rt_zebra_ipv4_blackhole_rt0(_this, addr, mask, 1); } /** Start processing */ int rt_zebra_start(rt_zebra *_this) { int ival, sock; struct sockaddr_un sun; RT_ZEBRA_ASSERT(_this->state == ZEBRA_STATUS_INIT || _this->state == ZEBRA_STATUS_STOPPED); sun.sun_len = sizeof(sun); sun.sun_family = AF_LOCAL; strlcpy(sun.sun_path, ZEBRA_SERV_PATH, sizeof(sun.sun_path)); sock = -1; if ((sock = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) { log_printf(LOG_ERR, "Creating a socket to the zserv failed: %m"); return 1; } if ((ival = fcntl(sock, F_GETFL, 0)) < 0) { log_printf(LOG_ERR, "fcntl(,F_GETFL) failed: %m"); goto reigai; } else if (fcntl(sock, F_SETFL, ival | O_NONBLOCK) < 0) { log_printf(LOG_ERR, "fcntl(,F_SETFL, +O_NONBLOCK) failed: %m"); goto reigai; } _this->state = ZEBRA_STATUS_CONNECTING; _this->sock = sock; sock = -1; if (connect(_this->sock, (struct sockaddr *)&sun, sizeof(sun)) == 0) { /* * don't change the state here, but change it on the next * write ready event. */ } else { switch (errno) { case EINPROGRESS: break; default: log_printf(LOG_ERR, "Connection to the zserv failed: %m"); goto reigai; } } event_set(&_this->ev_sock, _this->sock, EV_READ|EV_WRITE, rt_zebra_io_event, _this); event_add(&_this->ev_sock, NULL); return 0; reigai: if (sock >= 0) close(sock); rt_zebra_stop(_this); return 1; } /** Stop processing */ void rt_zebra_stop(rt_zebra *_this) { if (_this->sock >= 0) { event_del(&_this->ev_sock); close(_this->sock); } _this->sock = -1; _this->state = ZEBRA_STATUS_STOPPED; } /** Is processing */ int rt_zebra_is_running(rt_zebra *_this) { return (_this->state == ZEBRA_STATUS_INIT || _this->state == ZEBRA_STATUS_STOPPED || _this->state == ZEBRA_STATUS_DISPOSING)? 0 : 1; } static int rt_zebra_ipv4_blackhole_rt0(rt_zebra *_this, uint32_t addr0, uint32_t mask0, int delete) { int i, prefix, msg_flags, len, flags0, distance; u_char buf[1024], *cp; uint32_t addr, mask; RT_ZEBRA_DBG((_this, LOG_DEBUG, "%s %s(%08x,%08x)", __func__, (delete)? "delete" : "add", addr0, mask0)); if (_this->state == ZEBRA_STATUS_INIT || _this->state == ZEBRA_STATUS_DISPOSING) return 1; distance = DEFAULT_RT_ZEBRA_BLACKHOLE_DISTANCE; addr = ntohl(addr0); mask = ntohl(mask0); /* * Create a zebra protocol message. */ cp = buf; msg_flags = ZAPI_MESSAGE_NEXTHOP | ZAPI_MESSAGE_DISTANCE; flags0 = ZEBRA_FLAG_STATIC | ZEBRA_FLAG_BLACKHOLE; /* zebra protocol header */ PUTSHORT(0, cp); /* length place holder */ if (delete) { PUTCHAR(ZEBRA_IPV4_ROUTE_DELETE, cp); /* command */ } else { PUTCHAR(ZEBRA_IPV4_ROUTE_ADD, cp); /* command */ } PUTCHAR(ZEBRA_ROUTE_STATIC, cp); /* route type */ PUTCHAR(flags0, cp); /* flags */ PUTCHAR(msg_flags, cp); /* message */ /* destination address/netmask */ prefix = netmask2prefixlen(mask); PUTCHAR(prefix, cp); /* prefix */ for (i = 0; i < (prefix + 7) / 8; i++) PUTCHAR(*(((u_char *)&addr0) + i), cp); /* nexthop */ PUTCHAR(1, cp); /* number of message */ PUTCHAR(ZEBRA_NEXTHOP_IFINDEX, cp); PUTLONG(rt_zebra_blackhole_ifidx , cp); if ((msg_flags & ZAPI_MESSAGE_DISTANCE) != 0) PUTCHAR(distance, cp); /* distance */ len = cp - buf; /* save length */ cp = buf; /* rewind the position */ PUTSHORT(len, cp); /* length */ if (bytebuffer_put(_this->buffer, buf, len) == NULL) return 1; if (_this->state == ZEBRA_STATUS_CONNECTED) { if (_this->write_ready) rt_zebra_io_event(_this->sock, 0, _this); } if (_this->state == ZEBRA_STATUS_STOPPED) rt_zebra_start(_this); return 0; } static void rt_zebra_io_event(int fd, short ev, void *ctx) { int sz; u_char buf[BUFSIZ]; rt_zebra *_this; _this = ctx; RT_ZEBRA_DBG((_this, LOG_DEBUG, "%s [%s%s%s%s]", __func__, (ev & EV_READ)? "R" : "-", (ev & EV_WRITE)? "W" : "-", (_this->write_ready)? "w" : "-", (bytebuffer_position(_this->buffer) != 0)? "P" : "-")); if ((ev & EV_WRITE) != 0) { if (_this->state == ZEBRA_STATUS_CONNECTING) { rt_zebra_log(_this, LOG_NOTICE, "Established a new connection to the zserv."); _this->state = ZEBRA_STATUS_CONNECTED; } _this->write_ready = 1; } if ((ev & EV_READ) != 0) { if ((sz = read(_this->sock, buf, sizeof(buf))) <= 0) { if (sz == 0 || errno == ECONNRESET) { /* connection closed or reseted by the peer. */ rt_zebra_log(_this, LOG_INFO, "Connection closed by the zserv"); rt_zebra_stop(_this); return; } if (errno != EAGAIN) { rt_zebra_log(_this, LOG_INFO, "read() failed from the zserv: %m"); rt_zebra_stop(_this); return; } } else { /* assumes no responce from a zebra. */ RT_ZEBRA_ASSERT("NOTREACHED" == NULL); RT_ZEBRA_DBG((_this, LOG_DEBUG, "Received unexpected %d bytes message.", sz)); } } if (_this->write_ready != 0) { bytebuffer_flip(_this->buffer); while (bytebuffer_has_remaining(_this->buffer)) { if ((sz = write(_this->sock, bytebuffer_pointer(_this->buffer), bytebuffer_remaining(_this->buffer))) < 0) { if (errno == EAGAIN) break; bytebuffer_compact(_this->buffer); rt_zebra_log(_this, LOG_ERR, "write() failed to the zserv.:%m"); _this->state = ZEBRA_STATUS_CONNECTED; rt_zebra_stop(_this); return; } _this->write_ready = 0; bytebuffer_get(_this->buffer, BYTEBUFFER_GET_DIRECT, sz); } bytebuffer_compact(_this->buffer); } rt_zebra_set_event(_this); return; } static int rt_zebra_log(rt_zebra *_this, int logprio, const char *fmt, ...) { int rval; char buf[BUFSIZ]; va_list ap; strlcpy(buf, "rt_zebra ", sizeof(buf)); strlcat(buf, fmt, sizeof(buf)); va_start(ap, fmt); rval = vlog_printf(logprio, buf, ap); va_end(ap); return rval; } static void rt_zebra_set_event(rt_zebra *_this) { int evmask; RT_ZEBRA_ASSERT(_this->sock >= 0); evmask = EV_READ; if (_this->write_ready == 0) evmask |= EV_WRITE; event_del(&_this->ev_sock); event_set(&_this->ev_sock, _this->sock, evmask, rt_zebra_io_event, _this); event_add(&_this->ev_sock, NULL); }