diff options
Diffstat (limited to 'net/rose/rose_route.c')
-rw-r--r-- | net/rose/rose_route.c | 162 |
1 files changed, 118 insertions, 44 deletions
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 5b1338609..43358644c 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -1,5 +1,5 @@ /* - * ROSE release 002 + * ROSE release 003 * * This code REQUIRES 2.1.15 or higher/ NET3.038 * @@ -15,6 +15,8 @@ * address masks. * ROSE 002 Jonathan(G4KLX) Uprated through routing of packets. * Routing loop detection. + * ROSE 003 Jonathan(G4KLX) New timer architecture. + * Added use count to neighbours. */ #include <linux/config.h> @@ -80,24 +82,32 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct device *de rose_neigh->callsign = rose_route->neighbour; rose_neigh->digipeat = NULL; + rose_neigh->ax25 = NULL; rose_neigh->dev = dev; rose_neigh->count = 0; + rose_neigh->use = 0; rose_neigh->dce_mode = 0; rose_neigh->number = rose_neigh_no++; rose_neigh->restarted = 0; + skb_queue_head_init(&rose_neigh->queue); - rose_neigh->t0timer = 0; - rose_neigh->ftimer = 0; - init_timer(&rose_neigh->timer); + + init_timer(&rose_neigh->ftimer); + init_timer(&rose_neigh->t0timer); if (rose_route->ndigis != 0) { if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { kfree(rose_neigh); return -ENOMEM; } - rose_neigh->digipeat->ndigi = rose_route->ndigis; - for (i = 0; i < rose_route->ndigis; i++) - rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; + + rose_neigh->digipeat->ndigi = rose_route->ndigis; + rose_neigh->digipeat->lastrepeat = -1; + + for (i = 0; i < rose_route->ndigis; i++) { + rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; + rose_neigh->digipeat->repeated[i] = 0; + } } save_flags(flags); cli(); @@ -207,13 +217,13 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) unsigned long flags; struct sk_buff *skb; - del_timer(&rose_neigh->timer); + rose_stop_ftimer(rose_neigh); + rose_stop_t0timer(rose_neigh); while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL) kfree_skb(skb, FREE_WRITE); - save_flags(flags); - cli(); + save_flags(flags); cli(); if ((s = rose_neigh_list) == rose_neigh) { rose_neigh_list = rose_neigh->next; @@ -244,9 +254,14 @@ static void rose_remove_route(struct rose_route *rose_route) { struct rose_route *s; unsigned long flags; - - save_flags(flags); - cli(); + + if (rose_route->neigh1 != NULL) + rose_route->neigh1->use--; + + if (rose_route->neigh2 != NULL) + rose_route->neigh2->use--; + + save_flags(flags); cli(); if ((s = rose_route_list) == rose_route) { rose_route_list = rose_route->next; @@ -295,7 +310,7 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct device *de if (rose_node->neighbour[i] == rose_neigh) { rose_neigh->count--; - if (rose_neigh->count == 0) + if (rose_neigh->count == 0 && rose_neigh->use == 0) rose_remove_neigh(rose_neigh); rose_node->count--; @@ -381,6 +396,35 @@ void rose_route_device_down(struct device *dev) } /* + * Clear all nodes and neighbours out, except for neighbours with + * active connections going through them. + */ +static int rose_clear_routes(void) +{ + struct rose_neigh *s, *rose_neigh = rose_neigh_list; + struct rose_node *t, *rose_node = rose_node_list; + + while (rose_node != NULL) { + t = rose_node; + rose_node = rose_node->next; + + rose_remove_node(t); + } + + while (rose_neigh != NULL) { + s = rose_neigh; + rose_neigh = rose_neigh->next; + + s->count = 0; + + if (s->use == 0) + rose_remove_neigh(s); + } + + return 0; +} + +/* * Check that the device given is a valid AX.25 interface that is "up". */ struct device *rose_ax25_dev_get(char *devname) @@ -440,20 +484,31 @@ struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neig /* * Find a neighbour given a ROSE address. */ -struct rose_neigh *rose_get_neigh(rose_address *addr) +struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic) { struct rose_node *node; + int failed = 0; int i; for (node = rose_node_list; node != NULL; node = node->next) { if (rosecmpm(addr, &node->address, node->mask) == 0) { for (i = 0; i < node->count; i++) { - if (node->neighbour[i]->ftimer == 0) + if (!rose_ftimer_running(node->neighbour[i])) return node->neighbour[i]; + else + failed = 1; } } } + if (failed) { + *cause = ROSE_OUT_OF_ORDER; + *diagnostic = 0; + } else { + *cause = ROSE_NOT_OBTAINABLE; + *diagnostic = 0; + } + return NULL; } @@ -464,14 +519,12 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) { struct rose_route_struct rose_route; struct device *dev; - int err; switch (cmd) { case SIOCADDRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0) - return err; - copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)); + if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) + return -EFAULT; if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) return -EINVAL; if (rose_dev_get(&rose_route.address) != NULL) /* Can't add routes to ourself */ @@ -482,13 +535,15 @@ int rose_rt_ioctl(unsigned int cmd, void *arg) return rose_add_node(&rose_route, dev); case SIOCDELRT: - if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0) - return err; - copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)); + if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct))) + return -EFAULT; if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL) return -EINVAL; return rose_del_node(&rose_route, dev); + case SIOCRSCLRRT: + return rose_clear_routes(); + default: return -EINVAL; } @@ -502,10 +557,9 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) struct sk_buff *skb; rose_neigh->restarted = 0; - rose_neigh->t0timer = 0; - rose_neigh->ftimer = sysctl_rose_link_fail_timeout; - rose_link_set_timer(rose_neigh); + rose_stop_t0timer(rose_neigh); + rose_start_ftimer(rose_neigh); while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL) kfree_skb(skb, FREE_WRITE); @@ -523,13 +577,15 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) } if (rose_route->neigh1 == rose_neigh) { + rose_route->neigh1->use--; rose_route->neigh1 = NULL; - rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, 0x0D); + rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0); } if (rose_route->neigh2 == rose_neigh) { + rose_route->neigh2->use--; rose_route->neigh2 = NULL; - rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, 0x0D); + rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0); } rose_route = rose_route->next; @@ -541,16 +597,18 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh) * then don't use that neighbour until it is reset. Blow away all through * routes and connections using this route. */ -void rose_link_failed(ax25_address *callsign, struct device *dev) +void rose_link_failed(ax25_cb *ax25, int reason) { struct rose_neigh *rose_neigh; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_neigh->callsign, callsign) == 0 && rose_neigh->dev == dev) + if (rose_neigh->ax25 == ax25) break; if (rose_neigh == NULL) return; + rose_neigh->ax25 = NULL; + rose_del_route_by_neigh(rose_neigh); rose_kill_by_neigh(rose_neigh); } @@ -583,6 +641,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) struct sock *sk; unsigned short frametype; unsigned int lci, new_lci; + unsigned char cause, diagnostic; struct device *dev; unsigned long flags; @@ -604,7 +663,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) /* * Obviously the link is working, halt the ftimer. */ - rose_neigh->ftimer = 0; + rose_stop_ftimer(rose_neigh); /* * LCI of zero is always for us, and its always a restart @@ -631,7 +690,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) return rose_rx_call_request(skb, dev, rose_neigh, lci); if (!sysctl_rose_routing_control) { - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0); return 0; } @@ -679,7 +738,10 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) if (frametype != ROSE_CALL_REQUEST) /* XXX */ return 0; - rose_parse_facilities(skb, &facilities); + if (!rose_parse_facilities(skb, &facilities)) { + rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); + return 0; + } /* * Check for routing loops. @@ -691,25 +753,25 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) { printk(KERN_DEBUG "ROSE: routing loop from %s\n", rose2asc(src_addr)); printk(KERN_DEBUG "ROSE: to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120); return 0; } } - if ((new_neigh = rose_get_neigh(dest_addr)) == NULL) { - printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) { + if (cause == ROSE_NOT_OBTAINABLE) + printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr)); + rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic); return 0; } if ((new_lci = rose_new_lci(new_neigh)) == 0) { - printk(KERN_DEBUG "ROSE: no spare VCs to %s\n", rose2asc(dest_addr)); - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); return 0; } if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) { - rose_transmit_clear_request(rose_neigh, lci, 0x0D); + rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); return 0; } @@ -723,6 +785,9 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) rose_route->lci2 = new_lci; rose_route->neigh2 = new_neigh; + rose_route->neigh1->use++; + rose_route->neigh2->use++; + save_flags(flags); cli(); rose_route->next = rose_route_list; rose_route_list = rose_route; @@ -790,21 +855,30 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int len = 0; off_t pos = 0; off_t begin = 0; + int i; cli(); - len += sprintf(buffer, "addr callsign dev count mode restart t0 tf\n"); + len += sprintf(buffer, "addr callsign dev count use mode restart t0 tf digipeaters\n"); for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) { - len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3s %3s %3d %3d\n", + len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", rose_neigh->number, ax2asc(&rose_neigh->callsign), rose_neigh->dev ? rose_neigh->dev->name : "???", rose_neigh->count, + rose_neigh->use, (rose_neigh->dce_mode) ? "DCE" : "DTE", (rose_neigh->restarted) ? "yes" : "no", - rose_neigh->t0timer / ROSE_SLOWHZ, - rose_neigh->ftimer / ROSE_SLOWHZ); + ax25_display_timer(&rose_neigh->t0timer) / HZ, + ax25_display_timer(&rose_neigh->ftimer) / HZ); + + if (rose_neigh->digipeat != NULL) { + for (i = 0; i < rose_neigh->digipeat->ndigi; i++) + len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i])); + } + + len += sprintf(buffer + len, "\n"); pos = begin + len; |