/*- * Copyright (c) 2009 Internet Initiative Japan Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* $Id: rtev_common.c,v 1.3 2010/07/02 21:20:57 yasuoka Exp $ */ /* * PF_ROUTE related utility functions. *

* When use with libevent, call rtev_libevent_init() to initialize this * library.

* usage: *
 *	    #include 
 *	    #include 
 *	    #include 
 *	    #include 
 *	    #include 
 *
 *	    int main()
 *	    {
 *	    	event_init();
 *	    	rtev_libevent_init(5, 100, 16);	// init after event_init()
 *
 *	    	event_loop();
 *
 *	    	rtev_fini();			// fini before exit()
 *	    	exit(0);
 *	    }
 *
 *	    void hogehoge()
 *	    {
 *		struct ifaddrs *ifa = NULL;
 *
 *		getifaddrs(&ifa);	// rtev.h replaces getifaddrs(3)
 *		    :
 *		freeifaddrs(&ifa);
 *	    }
 * 
*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NO_RTEV_WRAPPER 1 #include "bytebuf.h" #include "rtev.h" #include "rtev_local.h" #ifdef RTEV_DEBUG #include "debugutil.h" #endif #ifndef RTEV_BUFSIZ #define RTEV_BUFSIZ 131072 /* 128K */ #endif static int rtev_base_on_rtevent(rtev_impl *); static int rtev_base_on_write(rtev_impl *, int, int); static struct ifaddrs *rtev_cached_ifa = NULL; static int rtev_event_serial = 0; static int rtev_send_serial = 0; static int rtev_ifa_cache_serial = 0; static bytebuffer *rtev_sndbuf = NULL; static u_char rtev_buffer_space[RTEV_BUFSIZ]; static rtev_impl *singleton_impl = NULL; static inline int rtev_update_ifa_cache(rtev_impl *); static int rtev_update_ifa_cache_do(rtev_impl *); static void ifa_rbentry_init (void); static void ifa_rbentry_fini (void); static inline int ifa_rb_ifacenam_insert (struct ifaddrs *); static inline int ifa_rb_sockaddr_insert (struct ifaddrs *); static inline struct ifaddrs *ifa_rb_ifacenam_find (const char *); static inline struct ifaddrs *ifa_rb_sockaddr_find (struct sockaddr const *); /** * Write a routing message. * @return not zero indicates error. See errno. */ int rtev_write(void *rtm_msg) { int rval; struct rt_msghdr *rtm; rtm = rtm_msg; rtm->rtm_seq = rtev_send_serial++; if (bytebuffer_put(rtev_sndbuf, rtm, rtm->rtm_msglen) == NULL) rval = -1; else rval = rtm->rtm_msglen; if (singleton_impl->impl_on_write != NULL) singleton_impl->impl_on_write(singleton_impl); return rval; } /** * same as getifaddrs(3) but returned obeject is cached. * The cached object may be freed by the event handler of this library, * so you cannot use it after the event handler returns the event loop. */ int rtev_getifaddrs(struct ifaddrs **ifa) { if (rtev_update_ifa_cache(singleton_impl) != 0) return 1; *ifa = rtev_cached_ifa; return 0; } /** * checks whether given address is the primary address of the interface. * @return not zero if the address is the primary. */ int rtev_ifa_is_primary(const char *ifname, struct sockaddr *sa) { int count; struct ifaddrs *ifa; for (count = 0, ifa = rtev_getifaddrs_by_ifname(ifname); ifa != NULL; ifa = ifa->ifa_next) { if (strcmp(ifa->ifa_name, ifname) != 0) break; if (ifa->ifa_addr->sa_family != sa->sa_family) continue; switch (sa->sa_family) { case AF_INET: if (((struct sockaddr_in *)sa)->sin_addr.s_addr == ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr .s_addr) { if (count == 0) return 1; return 0; } count++; break; case AF_INET6: if (IN6_ARE_ADDR_EQUAL( &((struct sockaddr_in6 *)sa)->sin6_addr, &((struct sockaddr_in6 *)ifa->ifa_addr) ->sin6_addr)){ if (count == 0) return 1; return 0; } count++; break; } } return 0; } /** returns the routing event serial number */ int rtev_get_event_serial() { return rtev_event_serial; } /** same as ifaddrs(3), but returned object is the first entry of 'ifname' */ struct ifaddrs * rtev_getifaddrs_by_ifname(const char *ifname) { if (rtev_update_ifa_cache(singleton_impl) != 0) return NULL; return ifa_rb_ifacenam_find(ifname); } struct ifaddrs * rtev_getifaddrs_by_sockaddr(struct sockaddr const *sa) { if (rtev_update_ifa_cache(singleton_impl) != 0) return NULL; return ifa_rb_sockaddr_find(sa); } /** same as if_nametoindex(3), but fast and no memory allocation. */ unsigned int rtev_if_nametoindex(const char *ifname) { unsigned int ni; struct ifaddrs *ifa; /* adapted from lib/libc/net/if_nametoindex.c */ ni = 0; for (ifa = rtev_getifaddrs_by_ifname(ifname); ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_LINK) { if (strcmp(ifa->ifa_name, ifname) == 0) { ni = ((struct sockaddr_dl*)ifa->ifa_addr) ->sdl_index; break; } else break; } } if (!ni) errno = ENXIO; return ni; } /** API have write pending packet internaly */ int rtev_has_write_pending(void) { if (bytebuffer_position(rtev_sndbuf) > 0) return 1; return 0; } /** finalize this library. */ void rtev_fini(void) { if (singleton_impl != NULL) singleton_impl->impl_fini(singleton_impl->impl); singleton_impl = NULL; if (rtev_cached_ifa != NULL) freeifaddrs(rtev_cached_ifa); rtev_cached_ifa = NULL; ifa_rbentry_fini(); if (rtev_sndbuf != NULL) { bytebuffer_unwrap(rtev_sndbuf); bytebuffer_destroy(rtev_sndbuf); } rtev_sndbuf = NULL; } /* protected virtual */ int rtev_base_init(rtev_impl *impl, int flags) { ifa_rbentry_init(); if (rtev_sndbuf == NULL) { if ((rtev_sndbuf = bytebuffer_wrap(rtev_buffer_space, sizeof(rtev_buffer_space))) == NULL) goto fail; bytebuffer_clear(rtev_sndbuf); } impl->base_on_rtevent = rtev_base_on_rtevent; impl->base_on_write = rtev_base_on_write; impl->base_flags = flags; singleton_impl = impl; return 0; fail: rtev_fini(); return 1; } static int rtev_base_on_rtevent(rtev_impl *impl) { RTEV_DBG((LOG_DEBUG, "%s", __func__)); rtev_event_serial++; if ((impl->base_flags & RTEV_UPDATE_IFA_ON_DEMAND) == 0) return rtev_update_ifa_cache_do(impl); return 0; } static inline int rtev_update_ifa_cache(rtev_impl *impl) { if (rtev_event_serial == rtev_ifa_cache_serial && rtev_cached_ifa != NULL) return 0; return rtev_update_ifa_cache_do(impl); } static int rtev_update_ifa_cache_do(rtev_impl *impl) { const char *ifname; struct ifaddrs *ifa; struct ifaddrs *ifa0; RTEV_DBG((LOG_DEBUG, "%s", __func__)); ifa0 = NULL; rtev_ifa_cache_serial = rtev_event_serial; if (getifaddrs(&ifa0) != 0) return 1; if (rtev_cached_ifa != NULL) { ifa_rbentry_fini(); freeifaddrs(rtev_cached_ifa); rtev_cached_ifa = NULL; } for (ifa = ifa0, ifname = NULL; ifa != NULL; ifa = ifa->ifa_next) { if (ifname == NULL || strcmp(ifa->ifa_name, ifname)) { ifname = ifa->ifa_name; if (ifa_rb_ifacenam_find(ifname) == NULL) { if (ifa_rb_ifacenam_insert(ifa) != 0) goto on_error; } } if (ifa->ifa_addr != NULL && ifa_rb_sockaddr_find(ifa->ifa_addr) == NULL) { if (ifa_rb_sockaddr_insert(ifa) != 0) goto on_error; } } rtev_cached_ifa = ifa0; return 0; on_error: if (ifa0) freeifaddrs(ifa0); rtev_cached_ifa = NULL; ifa_rbentry_fini(); return 1; } static int rtev_base_on_write(rtev_impl *impl, int rtsock, int npackets) { int i, rval; struct rt_msghdr *rtm; rval = 0; bytebuffer_flip(rtev_sndbuf); for (i = 0; i < npackets && bytebuffer_remaining(rtev_sndbuf) > 0; i++){ rtm = bytebuffer_pointer(rtev_sndbuf); if (send(rtsock, rtm, rtm->rtm_msglen, 0) <= 0 && !(rtm->rtm_type == RTM_DELETE && errno == ESRCH) && !(rtm->rtm_type == RTM_ADD && errno == EEXIST)) { rval = 1; } bytebuffer_get(rtev_sndbuf, BYTEBUFFER_GET_DIRECT, rtm->rtm_msglen); } bytebuffer_compact(rtev_sndbuf); return rval; } /* * Red-black trees for interface name and interface address lookups. */ #include /* BSD sys/tree.h */ struct ifa_rbentry { struct ifaddrs *ifa; RB_ENTRY(ifa_rbentry) rbe; }; static inline int ifacenam_compar(struct ifa_rbentry *, struct ifa_rbentry *); static RB_HEAD(ifa_rb_ifacenam, ifa_rbentry) ifa_rb_ifacenam; RB_PROTOTYPE(ifa_rb_ifacenam, ifa_rbentry, rbe, ifacenam_compar); RB_GENERATE(ifa_rb_ifacenam, ifa_rbentry, rbe, ifacenam_compar); static inline int sockaddr_compar(struct ifa_rbentry *, struct ifa_rbentry *); static RB_HEAD(ifa_rb_sockaddr, ifa_rbentry) ifa_rb_sockaddr; RB_PROTOTYPE(ifa_rb_sockaddr, ifa_rbentry, rbe, sockaddr_compar); RB_GENERATE(ifa_rb_sockaddr, ifa_rbentry, rbe, sockaddr_compar); static void ifa_rbentry_init(void) { RB_INIT(&ifa_rb_ifacenam); RB_INIT(&ifa_rb_sockaddr); } static void ifa_rbentry_fini(void) { struct ifa_rbentry *e, *n; for (e = RB_MIN(ifa_rb_ifacenam, &ifa_rb_ifacenam); e; e = n) { n = RB_NEXT(ifa_rb_ifacenam, &ifa_rb_ifacenam, e); RB_REMOVE(ifa_rb_ifacenam, &ifa_rb_ifacenam, e); free(e); } for (e = RB_MIN(ifa_rb_sockaddr, &ifa_rb_sockaddr); e; e = n) { n = RB_NEXT(ifa_rb_sockaddr, &ifa_rb_sockaddr, e); RB_REMOVE(ifa_rb_sockaddr, &ifa_rb_sockaddr, e); free(e); } } static inline int ifa_rb_ifacenam_insert(struct ifaddrs *ifa) { struct ifa_rbentry *e; if ((e = malloc(sizeof(struct ifa_rbentry))) == NULL) return -1; e->ifa = ifa; RB_INSERT(ifa_rb_ifacenam, &ifa_rb_ifacenam, e); return 0; } static inline int ifa_rb_sockaddr_insert(struct ifaddrs *ifa) { struct ifa_rbentry *e; if ((e = malloc(sizeof(struct ifa_rbentry))) == NULL) return -1; e->ifa = ifa; RB_INSERT(ifa_rb_sockaddr, &ifa_rb_sockaddr, e); return 0; } static inline struct ifaddrs * ifa_rb_ifacenam_find(const char *ifname) { struct ifa_rbentry *e, e0; struct ifaddrs ifa; e = &e0; e->ifa = &ifa; e->ifa->ifa_name = (char *)ifname; e = RB_FIND(ifa_rb_ifacenam, &ifa_rb_ifacenam, e); if (e == NULL) return NULL; return e->ifa; } static inline struct ifaddrs * ifa_rb_sockaddr_find(struct sockaddr const *sa) { struct ifa_rbentry *e, e0; struct ifaddrs ifa; e = &e0; e->ifa = &ifa; e->ifa->ifa_addr = (struct sockaddr *)sa; e = RB_FIND(ifa_rb_sockaddr, &ifa_rb_sockaddr, e); if (e == NULL) return NULL; return e->ifa; } static inline int ifacenam_compar(struct ifa_rbentry *a, struct ifa_rbentry *b) { return strcmp(a->ifa->ifa_name, b->ifa->ifa_name); } static inline int sockaddr_compar(struct ifa_rbentry *a, struct ifa_rbentry *b) { int cmp; cmp = b->ifa->ifa_addr->sa_family - a->ifa->ifa_addr->sa_family; if (cmp != 0) return cmp; return memcmp(a->ifa->ifa_addr->sa_data, b->ifa->ifa_addr->sa_data, MIN(a->ifa->ifa_addr->sa_len, b->ifa->ifa_addr->sa_len) - offsetof(struct sockaddr, sa_data)); }