/*** * * ipv4/route.c - real-time routing * * Copyright (C) 2004, 2005 Jan Kiszka * * Rewritten version of the original route by David Schleef and Ulrich Marx * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include /* FIXME: should also become some tunable parameter */ #define ROUTER_FORWARD_PRIO \ RTSKB_PRIO_VALUE(QUEUE_MAX_PRIO + \ (QUEUE_MIN_PRIO - QUEUE_MAX_PRIO + 1) / 2, \ RTSKB_DEF_RT_CHANNEL) /* First-level routing: explicite host routes */ struct host_route { struct host_route *next; struct dest_route dest_host; }; /* Second-level routing: routes to other networks */ struct net_route { struct net_route *next; u32 dest_net_ip; u32 dest_net_mask; u32 gw_ip; }; #if (CONFIG_XENO_DRIVERS_NET_RTIPV4_HOST_ROUTES & \ (CONFIG_XENO_DRIVERS_NET_RTIPV4_HOST_ROUTES - 1)) #error CONFIG_XENO_DRIVERS_NET_RTIPV4_HOST_ROUTES must be power of 2 #endif #if CONFIG_XENO_DRIVERS_NET_RTIPV4_HOST_ROUTES < 256 #define HOST_HASH_TBL_SIZE 64 #else #define HOST_HASH_TBL_SIZE \ ((CONFIG_XENO_DRIVERS_NET_RTIPV4_HOST_ROUTES / 256) * 64) #endif #define HOST_HASH_KEY_MASK (HOST_HASH_TBL_SIZE - 1) static struct host_route host_routes[CONFIG_XENO_DRIVERS_NET_RTIPV4_HOST_ROUTES]; static struct host_route *free_host_route; static int allocated_host_routes; static struct host_route *host_hash_tbl[HOST_HASH_TBL_SIZE]; static DEFINE_RTDM_LOCK(host_table_lock); #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING #if (CONFIG_XENO_DRIVERS_NET_RTIPV4_NET_ROUTES & \ (CONFIG_XENO_DRIVERS_NET_RTIPV4_NET_ROUTES - 1)) #error CONFIG_XENO_DRIVERS_NET_RTIPV4_NET_ROUTES must be power of 2 #endif #if CONFIG_XENO_DRIVERS_NET_RTIPV4_NET_ROUTES < 256 #define NET_HASH_TBL_SIZE 64 #else #define NET_HASH_TBL_SIZE \ ((CONFIG_XENO_DRIVERS_NET_RTIPV4_NET_ROUTES / 256) * 64) #endif #define NET_HASH_KEY_MASK (NET_HASH_TBL_SIZE - 1) #define NET_HASH_KEY_SHIFT 8 static struct net_route net_routes[CONFIG_XENO_DRIVERS_NET_RTIPV4_NET_ROUTES]; static struct net_route *free_net_route; static int allocated_net_routes; static struct net_route *net_hash_tbl[NET_HASH_TBL_SIZE + 1]; static unsigned int net_hash_key_shift = NET_HASH_KEY_SHIFT; static DEFINE_RTDM_LOCK(net_table_lock); module_param(net_hash_key_shift, uint, 0444); MODULE_PARM_DESC(net_hash_key_shift, "destination right shift for " "network hash key (default: 8)"); #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ /*** * proc filesystem section */ #ifdef CONFIG_XENO_OPT_VFILE static int rtnet_ipv4_route_show(struct xnvfile_regular_iterator *it, void *d) { #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING u32 mask; #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ xnvfile_printf(it, "Host routes allocated/total:\t%d/%d\n" "Host hash table size:\t\t%d\n", allocated_host_routes, CONFIG_XENO_DRIVERS_NET_RTIPV4_HOST_ROUTES, HOST_HASH_TBL_SIZE); #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING mask = NET_HASH_KEY_MASK << net_hash_key_shift; xnvfile_printf(it, "Network routes allocated/total:\t%d/%d\n" "Network hash table size:\t%d\n" "Network hash key shift/mask:\t%d/%08X\n", allocated_net_routes, CONFIG_XENO_DRIVERS_NET_RTIPV4_NET_ROUTES, NET_HASH_TBL_SIZE, net_hash_key_shift, mask); #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_ROUTER xnvfile_printf(it, "IP Router:\t\t\tyes\n"); #else xnvfile_printf(it, "IP Router:\t\t\tno\n"); #endif return 0; } static int rtnet_ipv4_module_lock(struct xnvfile *vfile) { bool res = try_module_get(THIS_MODULE); if (!res) return -EIDRM; return 0; } static void rtnet_ipv4_module_unlock(struct xnvfile *vfile) { module_put(THIS_MODULE); } static struct xnvfile_lock_ops rtnet_ipv4_module_lock_ops = { .get = rtnet_ipv4_module_lock, .put = rtnet_ipv4_module_unlock, }; static struct xnvfile_regular_ops rtnet_ipv4_route_vfile_ops = { .show = rtnet_ipv4_route_show, }; static struct xnvfile_regular rtnet_ipv4_route_vfile = { .entry = { .lockops = &rtnet_ipv4_module_lock_ops, }, .ops = &rtnet_ipv4_route_vfile_ops, }; static rtdm_lockctx_t rtnet_ipv4_host_route_lock_ctx; static int rtnet_ipv4_host_route_lock(struct xnvfile *vfile) { rtdm_lock_get_irqsave(&host_table_lock, rtnet_ipv4_host_route_lock_ctx); return 0; } static void rtnet_ipv4_host_route_unlock(struct xnvfile *vfile) { rtdm_lock_put_irqrestore(&host_table_lock, rtnet_ipv4_host_route_lock_ctx); } static struct xnvfile_lock_ops rtnet_ipv4_host_route_lock_ops = { .get = rtnet_ipv4_host_route_lock, .put = rtnet_ipv4_host_route_unlock, }; struct rtnet_ipv4_host_route_priv { unsigned key; struct host_route *entry_ptr; }; struct rtnet_ipv4_host_route_data { int key; char name[IFNAMSIZ]; struct dest_route dest_host; }; struct xnvfile_rev_tag host_route_tag; static void *rtnet_ipv4_host_route_begin(struct xnvfile_snapshot_iterator *it) { struct rtnet_ipv4_host_route_priv *priv = xnvfile_iterator_priv(it); struct rtnet_ipv4_host_route_data *data; unsigned routes; int err; routes = allocated_host_routes; if (!routes) return VFILE_SEQ_EMPTY; data = kmalloc(sizeof(*data) * routes, GFP_KERNEL); if (data == NULL) return NULL; err = rtnet_ipv4_module_lock(NULL); if (err < 0) { kfree(data); return VFILE_SEQ_EMPTY; } priv->key = -1; priv->entry_ptr = NULL; return data; } static void rtnet_ipv4_host_route_end(struct xnvfile_snapshot_iterator *it, void *buf) { rtnet_ipv4_module_unlock(NULL); kfree(buf); } static int rtnet_ipv4_host_route_next(struct xnvfile_snapshot_iterator *it, void *data) { struct rtnet_ipv4_host_route_priv *priv = xnvfile_iterator_priv(it); struct rtnet_ipv4_host_route_data *p = data; struct rtnet_device *rtdev; if (priv->entry_ptr == NULL) { if (++priv->key >= HOST_HASH_TBL_SIZE) return 0; priv->entry_ptr = host_hash_tbl[priv->key]; if (priv->entry_ptr == NULL) return VFILE_SEQ_SKIP; } rtdev = priv->entry_ptr->dest_host.rtdev; if (!rtdev_reference(rtdev)) return -EIDRM; memcpy(&p->name, rtdev->name, sizeof(p->name)); rtdev_dereference(rtdev); p->key = priv->key; memcpy(&p->dest_host, &priv->entry_ptr->dest_host, sizeof(p->dest_host)); priv->entry_ptr = priv->entry_ptr->next; return 1; } static int rtnet_ipv4_host_route_show(struct xnvfile_snapshot_iterator *it, void *data) { struct rtnet_ipv4_host_route_data *p = data; if (p == NULL) { xnvfile_printf(it, "Hash\tDestination\tHW Address\t\tDevice\n"); return 0; } xnvfile_printf(it, "%02X\t%u.%u.%u.%-3u\t" "%02X:%02X:%02X:%02X:%02X:%02X\t%s\n", p->key, NIPQUAD(p->dest_host.ip), p->dest_host.dev_addr[0], p->dest_host.dev_addr[1], p->dest_host.dev_addr[2], p->dest_host.dev_addr[3], p->dest_host.dev_addr[4], p->dest_host.dev_addr[5], p->name); return 0; } static struct xnvfile_snapshot_ops rtnet_ipv4_host_route_vfile_ops = { .begin = rtnet_ipv4_host_route_begin, .end = rtnet_ipv4_host_route_end, .next = rtnet_ipv4_host_route_next, .show = rtnet_ipv4_host_route_show, }; static struct xnvfile_snapshot rtnet_ipv4_host_route_vfile = { .entry = { .lockops = &rtnet_ipv4_host_route_lock_ops, }, .privsz = sizeof(struct rtnet_ipv4_host_route_priv), .datasz = sizeof(struct rtnet_ipv4_host_route_data), .tag = &host_route_tag, .ops = &rtnet_ipv4_host_route_vfile_ops, }; static struct xnvfile_link rtnet_ipv4_arp_vfile; #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING static rtdm_lockctx_t rtnet_ipv4_net_route_lock_ctx; static int rtnet_ipv4_net_route_lock(struct xnvfile *vfile) { rtdm_lock_get_irqsave(&net_table_lock, rtnet_ipv4_net_route_lock_ctx); return 0; } static void rtnet_ipv4_net_route_unlock(struct xnvfile *vfile) { rtdm_lock_put_irqrestore(&net_table_lock, rtnet_ipv4_net_route_lock_ctx); } static struct xnvfile_lock_ops rtnet_ipv4_net_route_lock_ops = { .get = rtnet_ipv4_net_route_lock, .put = rtnet_ipv4_net_route_unlock, }; struct rtnet_ipv4_net_route_priv { unsigned key; struct net_route *entry_ptr; }; struct rtnet_ipv4_net_route_data { int key; u32 dest_net_ip; u32 dest_net_mask; u32 gw_ip; }; struct xnvfile_rev_tag net_route_tag; static void *rtnet_ipv4_net_route_begin(struct xnvfile_snapshot_iterator *it) { struct rtnet_ipv4_net_route_priv *priv = xnvfile_iterator_priv(it); struct rtnet_ipv4_net_route_data *data; unsigned routes; int err; routes = allocated_net_routes; if (!routes) return VFILE_SEQ_EMPTY; data = kmalloc(sizeof(*data) * routes, GFP_KERNEL); if (data == NULL) return NULL; err = rtnet_ipv4_module_lock(NULL); if (err < 0) { kfree(data); return VFILE_SEQ_EMPTY; } priv->key = -1; priv->entry_ptr = NULL; return data; } static void rtnet_ipv4_net_route_end(struct xnvfile_snapshot_iterator *it, void *buf) { rtnet_ipv4_module_unlock(NULL); kfree(buf); } static int rtnet_ipv4_net_route_next(struct xnvfile_snapshot_iterator *it, void *data) { struct rtnet_ipv4_net_route_priv *priv = xnvfile_iterator_priv(it); struct rtnet_ipv4_net_route_data *p = data; if (priv->entry_ptr == NULL) { if (++priv->key >= NET_HASH_TBL_SIZE + 1) return 0; priv->entry_ptr = net_hash_tbl[priv->key]; if (priv->entry_ptr == NULL) return VFILE_SEQ_SKIP; } p->key = priv->key; p->dest_net_ip = priv->entry_ptr->dest_net_ip; p->dest_net_mask = priv->entry_ptr->dest_net_mask; p->gw_ip = priv->entry_ptr->gw_ip; priv->entry_ptr = priv->entry_ptr->next; return 1; } static int rtnet_ipv4_net_route_show(struct xnvfile_snapshot_iterator *it, void *data) { struct rtnet_ipv4_net_route_data *p = data; if (p == NULL) { xnvfile_printf(it, "Hash\tDestination\tMask\t\t\tGateway\n"); return 0; } if (p->key < NET_HASH_TBL_SIZE) xnvfile_printf(it, "%02X\t%u.%u.%u.%-3u\t%u.%u.%u.%-3u" "\t\t%u.%u.%u.%-3u\n", p->key, NIPQUAD(p->dest_net_ip), NIPQUAD(p->dest_net_mask), NIPQUAD(p->gw_ip)); else xnvfile_printf(it, "*\t%u.%u.%u.%-3u\t%u.%u.%u.%-3u\t\t" "%u.%u.%u.%-3u\n", NIPQUAD(p->dest_net_ip), NIPQUAD(p->dest_net_mask), NIPQUAD(p->gw_ip)); return 0; } static struct xnvfile_snapshot_ops rtnet_ipv4_net_route_vfile_ops = { .begin = rtnet_ipv4_net_route_begin, .end = rtnet_ipv4_net_route_end, .next = rtnet_ipv4_net_route_next, .show = rtnet_ipv4_net_route_show, }; static struct xnvfile_snapshot rtnet_ipv4_net_route_vfile = { .entry = { .lockops = &rtnet_ipv4_net_route_lock_ops, }, .privsz = sizeof(struct rtnet_ipv4_net_route_priv), .datasz = sizeof(struct rtnet_ipv4_net_route_data), .tag = &net_route_tag, .ops = &rtnet_ipv4_net_route_vfile_ops, }; #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ static int __init rt_route_proc_register(void) { int err; err = xnvfile_init_regular("route", &rtnet_ipv4_route_vfile, &ipv4_proc_root); if (err < 0) goto err1; err = xnvfile_init_snapshot("host_route", &rtnet_ipv4_host_route_vfile, &ipv4_proc_root); if (err < 0) goto err2; /* create "arp" as an alias for "host_route" */ err = xnvfile_init_link("arp", "host_route", &rtnet_ipv4_arp_vfile, &ipv4_proc_root); if (err < 0) goto err3; #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING err = xnvfile_init_snapshot("net_route", &rtnet_ipv4_net_route_vfile, &ipv4_proc_root); if (err < 0) goto err4; #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ return 0; #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING err4: xnvfile_destroy_link(&rtnet_ipv4_arp_vfile); #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ err3: xnvfile_destroy_snapshot(&rtnet_ipv4_host_route_vfile); err2: xnvfile_destroy_regular(&rtnet_ipv4_route_vfile); err1: printk("RTnet: unable to initialize /proc entries (route)\n"); return err; } static void rt_route_proc_unregister(void) { #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING xnvfile_destroy_snapshot(&rtnet_ipv4_net_route_vfile); #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ xnvfile_destroy_link(&rtnet_ipv4_arp_vfile); xnvfile_destroy_snapshot(&rtnet_ipv4_host_route_vfile); xnvfile_destroy_regular(&rtnet_ipv4_route_vfile); } #endif /* CONFIG_XENO_OPT_VFILE */ /*** * rt_alloc_host_route - allocates new host route */ static inline struct host_route *rt_alloc_host_route(void) { rtdm_lockctx_t context; struct host_route *rt; rtdm_lock_get_irqsave(&host_table_lock, context); if ((rt = free_host_route) != NULL) { free_host_route = rt->next; allocated_host_routes++; } rtdm_lock_put_irqrestore(&host_table_lock, context); return rt; } /*** * rt_free_host_route - releases host route * * Note: must be called with host_table_lock held */ static inline void rt_free_host_route(struct host_route *rt) { rt->next = free_host_route; free_host_route = rt; allocated_host_routes--; } /*** * rt_ip_route_add_host: add or update host route */ int rt_ip_route_add_host(u32 addr, unsigned char *dev_addr, struct rtnet_device *rtdev) { rtdm_lockctx_t context; struct host_route *new_route; struct host_route *rt; unsigned int key; int ret = 0; rtdm_lock_get_irqsave(&rtdev->rtdev_lock, context); if ((!test_bit(PRIV_FLAG_UP, &rtdev->priv_flags) || test_and_set_bit(PRIV_FLAG_ADDING_ROUTE, &rtdev->priv_flags))) { rtdm_lock_put_irqrestore(&rtdev->rtdev_lock, context); return -EBUSY; } rtdm_lock_put_irqrestore(&rtdev->rtdev_lock, context); if ((new_route = rt_alloc_host_route()) != NULL) { new_route->dest_host.ip = addr; new_route->dest_host.rtdev = rtdev; memcpy(new_route->dest_host.dev_addr, dev_addr, rtdev->addr_len); } key = ntohl(addr) & HOST_HASH_KEY_MASK; rtdm_lock_get_irqsave(&host_table_lock, context); xnvfile_touch_tag(&host_route_tag); rt = host_hash_tbl[key]; while (rt != NULL) { if ((rt->dest_host.ip == addr) && (rt->dest_host.rtdev->local_ip == rtdev->local_ip)) { rt->dest_host.rtdev = rtdev; memcpy(rt->dest_host.dev_addr, dev_addr, rtdev->addr_len); if (new_route) rt_free_host_route(new_route); rtdm_lock_put_irqrestore(&host_table_lock, context); goto out; } rt = rt->next; } if (new_route) { new_route->next = host_hash_tbl[key]; host_hash_tbl[key] = new_route; rtdm_lock_put_irqrestore(&host_table_lock, context); } else { rtdm_lock_put_irqrestore(&host_table_lock, context); /*ERRMSG*/ rtdm_printk( "RTnet: no more host routes available\n"); ret = -ENOBUFS; } out: clear_bit(PRIV_FLAG_ADDING_ROUTE, &rtdev->priv_flags); return ret; } /*** * rt_ip_route_del_host - deletes specified host route */ int rt_ip_route_del_host(u32 addr, struct rtnet_device *rtdev) { rtdm_lockctx_t context; struct host_route *rt; struct host_route **last_ptr; unsigned int key; key = ntohl(addr) & HOST_HASH_KEY_MASK; last_ptr = &host_hash_tbl[key]; rtdm_lock_get_irqsave(&host_table_lock, context); rt = host_hash_tbl[key]; while (rt != NULL) { if ((rt->dest_host.ip == addr) && (!rtdev || (rt->dest_host.rtdev->local_ip == rtdev->local_ip))) { *last_ptr = rt->next; rt_free_host_route(rt); xnvfile_touch_tag(&host_route_tag); rtdm_lock_put_irqrestore(&host_table_lock, context); return 0; } last_ptr = &rt->next; rt = rt->next; } rtdm_lock_put_irqrestore(&host_table_lock, context); return -ENOENT; } /*** * rt_ip_route_del_all - deletes all routes associated with a specified device */ void rt_ip_route_del_all(struct rtnet_device *rtdev) { rtdm_lockctx_t context; struct host_route *host_rt; struct host_route **last_host_ptr; unsigned int key; u32 ip; for (key = 0; key < HOST_HASH_TBL_SIZE; key++) { host_start_over: last_host_ptr = &host_hash_tbl[key]; rtdm_lock_get_irqsave(&host_table_lock, context); host_rt = host_hash_tbl[key]; while (host_rt != NULL) { if (host_rt->dest_host.rtdev == rtdev) { *last_host_ptr = host_rt->next; rt_free_host_route(host_rt); rtdm_lock_put_irqrestore(&host_table_lock, context); goto host_start_over; } last_host_ptr = &host_rt->next; host_rt = host_rt->next; } rtdm_lock_put_irqrestore(&host_table_lock, context); } if ((ip = rtdev->local_ip) != 0) rt_ip_route_del_host(ip, rtdev); } /*** * rt_ip_route_get_host - check if specified host route is resolved */ int rt_ip_route_get_host(u32 addr, char *if_name, unsigned char *dev_addr, struct rtnet_device *rtdev) { rtdm_lockctx_t context; struct host_route *rt; unsigned int key; key = ntohl(addr) & HOST_HASH_KEY_MASK; rtdm_lock_get_irqsave(&host_table_lock, context); rt = host_hash_tbl[key]; while (rt != NULL) { if ((rt->dest_host.ip == addr) && (!rtdev || rt->dest_host.rtdev->local_ip == rtdev->local_ip)) { memcpy(dev_addr, rt->dest_host.dev_addr, rt->dest_host.rtdev->addr_len); strncpy(if_name, rt->dest_host.rtdev->name, IFNAMSIZ); rtdm_lock_put_irqrestore(&host_table_lock, context); return 0; } rt = rt->next; } rtdm_lock_put_irqrestore(&host_table_lock, context); return -ENOENT; } #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING /*** * rt_alloc_net_route - allocates new network route */ static inline struct net_route *rt_alloc_net_route(void) { rtdm_lockctx_t context; struct net_route *rt; rtdm_lock_get_irqsave(&net_table_lock, context); if ((rt = free_net_route) != NULL) { free_net_route = rt->next; allocated_net_routes++; } rtdm_lock_put_irqrestore(&net_table_lock, context); return rt; } /*** * rt_free_net_route - releases network route * * Note: must be called with net_table_lock held */ static inline void rt_free_net_route(struct net_route *rt) { rt->next = free_net_route; free_net_route = rt; allocated_host_routes--; } /*** * rt_ip_route_add_net: add or update network route */ int rt_ip_route_add_net(u32 addr, u32 mask, u32 gw_addr) { rtdm_lockctx_t context; struct net_route *new_route; struct net_route *rt; struct net_route **last_ptr; unsigned int key; u32 shifted_mask; addr &= mask; if ((new_route = rt_alloc_net_route()) != NULL) { new_route->dest_net_ip = addr; new_route->dest_net_mask = mask; new_route->gw_ip = gw_addr; } shifted_mask = NET_HASH_KEY_MASK << net_hash_key_shift; if ((mask & shifted_mask) == shifted_mask) key = (ntohl(addr) >> net_hash_key_shift) & NET_HASH_KEY_MASK; else key = NET_HASH_TBL_SIZE; last_ptr = &net_hash_tbl[key]; rtdm_lock_get_irqsave(&net_table_lock, context); xnvfile_touch_tag(&net_route_tag); rt = net_hash_tbl[key]; while (rt != NULL) { if ((rt->dest_net_ip == addr) && (rt->dest_net_mask == mask)) { rt->gw_ip = gw_addr; if (new_route) rt_free_net_route(new_route); rtdm_lock_put_irqrestore(&net_table_lock, context); return 0; } last_ptr = &rt->next; rt = rt->next; } if (new_route) { new_route->next = *last_ptr; *last_ptr = new_route; rtdm_lock_put_irqrestore(&net_table_lock, context); return 0; } else { rtdm_lock_put_irqrestore(&net_table_lock, context); /*ERRMSG*/ rtdm_printk( "RTnet: no more network routes available\n"); return -ENOBUFS; } } /*** * rt_ip_route_del_net - deletes specified network route */ int rt_ip_route_del_net(u32 addr, u32 mask) { rtdm_lockctx_t context; struct net_route *rt; struct net_route **last_ptr; unsigned int key; u32 shifted_mask; addr &= mask; shifted_mask = NET_HASH_KEY_MASK << net_hash_key_shift; if ((mask & shifted_mask) == shifted_mask) key = (ntohl(addr) >> net_hash_key_shift) & NET_HASH_KEY_MASK; else key = NET_HASH_TBL_SIZE; last_ptr = &net_hash_tbl[key]; rtdm_lock_get_irqsave(&net_table_lock, context); rt = net_hash_tbl[key]; while (rt != NULL) { if ((rt->dest_net_ip == addr) && (rt->dest_net_mask == mask)) { *last_ptr = rt->next; rt_free_net_route(rt); xnvfile_touch_tag(&net_route_tag); rtdm_lock_put_irqrestore(&net_table_lock, context); return 0; } last_ptr = &rt->next; rt = rt->next; } rtdm_lock_put_irqrestore(&net_table_lock, context); return -ENOENT; } #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ /*** * rt_ip_route_output - looks up output route * * Note: increments refcount on returned rtdev in rt_buf */ int rt_ip_route_output(struct dest_route *rt_buf, u32 daddr, u32 saddr) { rtdm_lockctx_t context; struct host_route *host_rt; unsigned int key; #ifndef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING #define DADDR daddr #else #define DADDR real_daddr struct net_route *net_rt; int lookup_gw = 1; u32 real_daddr = daddr; restart: #endif /* !CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ key = ntohl(daddr) & HOST_HASH_KEY_MASK; rtdm_lock_get_irqsave(&host_table_lock, context); host_rt = host_hash_tbl[key]; if (likely(saddr == INADDR_ANY)) while (host_rt != NULL) { if (host_rt->dest_host.ip == daddr) { host_route_found: if (!rtdev_reference( host_rt->dest_host.rtdev)) { rtdm_lock_put_irqrestore( &host_table_lock, context); goto next; } memcpy(rt_buf->dev_addr, &host_rt->dest_host.dev_addr, sizeof(rt_buf->dev_addr)); rt_buf->rtdev = host_rt->dest_host.rtdev; rtdm_lock_put_irqrestore(&host_table_lock, context); rt_buf->ip = DADDR; return 0; } next: host_rt = host_rt->next; } else while (host_rt != NULL) { if ((host_rt->dest_host.ip == daddr) && (host_rt->dest_host.rtdev->local_ip == saddr)) goto host_route_found; host_rt = host_rt->next; } rtdm_lock_put_irqrestore(&host_table_lock, context); #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING if (lookup_gw) { lookup_gw = 0; key = (ntohl(daddr) >> net_hash_key_shift) & NET_HASH_KEY_MASK; rtdm_lock_get_irqsave(&net_table_lock, context); net_rt = net_hash_tbl[key]; while (net_rt != NULL) { if (net_rt->dest_net_ip == (daddr & net_rt->dest_net_mask)) { daddr = net_rt->gw_ip; rtdm_lock_put_irqrestore(&net_table_lock, context); /* start over, now using the gateway ip as destination */ goto restart; } net_rt = net_rt->next; } rtdm_lock_put_irqrestore(&net_table_lock, context); /* last try: no hash key */ rtdm_lock_get_irqsave(&net_table_lock, context); net_rt = net_hash_tbl[NET_HASH_TBL_SIZE]; while (net_rt != NULL) { if (net_rt->dest_net_ip == (daddr & net_rt->dest_net_mask)) { daddr = net_rt->gw_ip; rtdm_lock_put_irqrestore(&net_table_lock, context); /* start over, now using the gateway ip as destination */ goto restart; } net_rt = net_rt->next; } rtdm_lock_put_irqrestore(&net_table_lock, context); } #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ /*ERRMSG*/ rtdm_printk("RTnet: host %u.%u.%u.%u unreachable\n", NIPQUAD(daddr)); return -EHOSTUNREACH; } #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_ROUTER int rt_ip_route_forward(struct rtskb *rtskb, u32 daddr) { struct rtnet_device *rtdev = rtskb->rtdev; struct dest_route dest; if (likely((daddr == rtdev->local_ip) || (daddr == rtdev->broadcast_ip) || (rtdev->flags & IFF_LOOPBACK))) return 0; if (rtskb_acquire(rtskb, &global_pool) != 0) { /*ERRMSG*/ rtdm_printk( "RTnet: router overloaded, dropping packet\n"); goto error; } if (rt_ip_route_output(&dest, daddr, INADDR_ANY) < 0) { /*ERRMSG*/ rtdm_printk( "RTnet: unable to forward packet from %u.%u.%u.%u\n", NIPQUAD(rtskb->nh.iph->saddr)); goto error; } rtskb->rtdev = dest.rtdev; rtskb->priority = ROUTER_FORWARD_PRIO; if ((dest.rtdev->hard_header) && (dest.rtdev->hard_header(rtskb, dest.rtdev, ETH_P_IP, dest.dev_addr, dest.rtdev->dev_addr, rtskb->len) < 0)) goto error; rtdev_xmit(rtskb); return 1; error: kfree_rtskb(rtskb); return 1; } #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_ROUTER */ /*** * rt_ip_routing_init: initialize */ int __init rt_ip_routing_init(void) { int i; for (i = 0; i < CONFIG_XENO_DRIVERS_NET_RTIPV4_HOST_ROUTES - 2; i++) host_routes[i].next = &host_routes[i + 1]; free_host_route = &host_routes[0]; #ifdef CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING for (i = 0; i < CONFIG_XENO_DRIVERS_NET_RTIPV4_NET_ROUTES - 2; i++) net_routes[i].next = &net_routes[i + 1]; free_net_route = &net_routes[0]; #endif /* CONFIG_XENO_DRIVERS_NET_RTIPV4_NETROUTING */ #ifdef CONFIG_XENO_OPT_VFILE return rt_route_proc_register(); #else /* !CONFIG_XENO_OPT_VFILE */ return 0; #endif /* CONFIG_XENO_OPT_VFILE */ } /*** * rt_ip_routing_realease */ void rt_ip_routing_release(void) { #ifdef CONFIG_XENO_OPT_VFILE rt_route_proc_unregister(); #endif /* CONFIG_XENO_OPT_VFILE */ } EXPORT_SYMBOL_GPL(rt_ip_route_add_host); EXPORT_SYMBOL_GPL(rt_ip_route_del_host); EXPORT_SYMBOL_GPL(rt_ip_route_del_all); EXPORT_SYMBOL_GPL(rt_ip_route_output);