linux/net/ax25/ax25_route.c
<<
>>
Prefs
   1/*
   2 * This program is free software; you can redistribute it and/or modify
   3 * it under the terms of the GNU General Public License as published by
   4 * the Free Software Foundation; either version 2 of the License, or
   5 * (at your option) any later version.
   6 *
   7 * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
   8 * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
   9 * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org)
  10 * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
  11 * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de)
  12 * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
  13 */
  14
  15#include <linux/capability.h>
  16#include <linux/errno.h>
  17#include <linux/types.h>
  18#include <linux/socket.h>
  19#include <linux/timer.h>
  20#include <linux/in.h>
  21#include <linux/kernel.h>
  22#include <linux/sched.h>
  23#include <linux/string.h>
  24#include <linux/sockios.h>
  25#include <linux/net.h>
  26#include <linux/slab.h>
  27#include <net/ax25.h>
  28#include <linux/inet.h>
  29#include <linux/netdevice.h>
  30#include <linux/if_arp.h>
  31#include <linux/skbuff.h>
  32#include <linux/spinlock.h>
  33#include <net/sock.h>
  34#include <asm/uaccess.h>
  35#include <linux/fcntl.h>
  36#include <linux/mm.h>
  37#include <linux/interrupt.h>
  38#include <linux/init.h>
  39#include <linux/seq_file.h>
  40#include <linux/export.h>
  41
  42static ax25_route *ax25_route_list;
  43static DEFINE_RWLOCK(ax25_route_lock);
  44
  45void ax25_rt_device_down(struct net_device *dev)
  46{
  47        ax25_route *s, *t, *ax25_rt;
  48
  49        write_lock_bh(&ax25_route_lock);
  50        ax25_rt = ax25_route_list;
  51        while (ax25_rt != NULL) {
  52                s       = ax25_rt;
  53                ax25_rt = ax25_rt->next;
  54
  55                if (s->dev == dev) {
  56                        if (ax25_route_list == s) {
  57                                ax25_route_list = s->next;
  58                                kfree(s->digipeat);
  59                                kfree(s);
  60                        } else {
  61                                for (t = ax25_route_list; t != NULL; t = t->next) {
  62                                        if (t->next == s) {
  63                                                t->next = s->next;
  64                                                kfree(s->digipeat);
  65                                                kfree(s);
  66                                                break;
  67                                        }
  68                                }
  69                        }
  70                }
  71        }
  72        write_unlock_bh(&ax25_route_lock);
  73}
  74
  75static int __must_check ax25_rt_add(struct ax25_routes_struct *route)
  76{
  77        ax25_route *ax25_rt;
  78        ax25_dev *ax25_dev;
  79        int i;
  80
  81        if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL)
  82                return -EINVAL;
  83        if (route->digi_count > AX25_MAX_DIGIS)
  84                return -EINVAL;
  85
  86        write_lock_bh(&ax25_route_lock);
  87
  88        ax25_rt = ax25_route_list;
  89        while (ax25_rt != NULL) {
  90                if (ax25cmp(&ax25_rt->callsign, &route->dest_addr) == 0 &&
  91                            ax25_rt->dev == ax25_dev->dev) {
  92                        kfree(ax25_rt->digipeat);
  93                        ax25_rt->digipeat = NULL;
  94                        if (route->digi_count != 0) {
  95                                if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
  96                                        write_unlock_bh(&ax25_route_lock);
  97                                        return -ENOMEM;
  98                                }
  99                                ax25_rt->digipeat->lastrepeat = -1;
 100                                ax25_rt->digipeat->ndigi      = route->digi_count;
 101                                for (i = 0; i < route->digi_count; i++) {
 102                                        ax25_rt->digipeat->repeated[i] = 0;
 103                                        ax25_rt->digipeat->calls[i]    = route->digi_addr[i];
 104                                }
 105                        }
 106                        write_unlock_bh(&ax25_route_lock);
 107                        return 0;
 108                }
 109                ax25_rt = ax25_rt->next;
 110        }
 111
 112        if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) {
 113                write_unlock_bh(&ax25_route_lock);
 114                return -ENOMEM;
 115        }
 116
 117        atomic_set(&ax25_rt->refcount, 1);
 118        ax25_rt->callsign     = route->dest_addr;
 119        ax25_rt->dev          = ax25_dev->dev;
 120        ax25_rt->digipeat     = NULL;
 121        ax25_rt->ip_mode      = ' ';
 122        if (route->digi_count != 0) {
 123                if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
 124                        write_unlock_bh(&ax25_route_lock);
 125                        kfree(ax25_rt);
 126                        return -ENOMEM;
 127                }
 128                ax25_rt->digipeat->lastrepeat = -1;
 129                ax25_rt->digipeat->ndigi      = route->digi_count;
 130                for (i = 0; i < route->digi_count; i++) {
 131                        ax25_rt->digipeat->repeated[i] = 0;
 132                        ax25_rt->digipeat->calls[i]    = route->digi_addr[i];
 133                }
 134        }
 135        ax25_rt->next   = ax25_route_list;
 136        ax25_route_list = ax25_rt;
 137        write_unlock_bh(&ax25_route_lock);
 138
 139        return 0;
 140}
 141
 142void __ax25_put_route(ax25_route *ax25_rt)
 143{
 144        kfree(ax25_rt->digipeat);
 145        kfree(ax25_rt);
 146}
 147
 148static int ax25_rt_del(struct ax25_routes_struct *route)
 149{
 150        ax25_route *s, *t, *ax25_rt;
 151        ax25_dev *ax25_dev;
 152
 153        if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL)
 154                return -EINVAL;
 155
 156        write_lock_bh(&ax25_route_lock);
 157
 158        ax25_rt = ax25_route_list;
 159        while (ax25_rt != NULL) {
 160                s       = ax25_rt;
 161                ax25_rt = ax25_rt->next;
 162                if (s->dev == ax25_dev->dev &&
 163                    ax25cmp(&route->dest_addr, &s->callsign) == 0) {
 164                        if (ax25_route_list == s) {
 165                                ax25_route_list = s->next;
 166                                ax25_put_route(s);
 167                        } else {
 168                                for (t = ax25_route_list; t != NULL; t = t->next) {
 169                                        if (t->next == s) {
 170                                                t->next = s->next;
 171                                                ax25_put_route(s);
 172                                                break;
 173                                        }
 174                                }
 175                        }
 176                }
 177        }
 178        write_unlock_bh(&ax25_route_lock);
 179
 180        return 0;
 181}
 182
 183static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option)
 184{
 185        ax25_route *ax25_rt;
 186        ax25_dev *ax25_dev;
 187        int err = 0;
 188
 189        if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL)
 190                return -EINVAL;
 191
 192        write_lock_bh(&ax25_route_lock);
 193
 194        ax25_rt = ax25_route_list;
 195        while (ax25_rt != NULL) {
 196                if (ax25_rt->dev == ax25_dev->dev &&
 197                    ax25cmp(&rt_option->dest_addr, &ax25_rt->callsign) == 0) {
 198                        switch (rt_option->cmd) {
 199                        case AX25_SET_RT_IPMODE:
 200                                switch (rt_option->arg) {
 201                                case ' ':
 202                                case 'D':
 203                                case 'V':
 204                                        ax25_rt->ip_mode = rt_option->arg;
 205                                        break;
 206                                default:
 207                                        err = -EINVAL;
 208                                        goto out;
 209                                }
 210                                break;
 211                        default:
 212                                err = -EINVAL;
 213                                goto out;
 214                        }
 215                }
 216                ax25_rt = ax25_rt->next;
 217        }
 218
 219out:
 220        write_unlock_bh(&ax25_route_lock);
 221        return err;
 222}
 223
 224int ax25_rt_ioctl(unsigned int cmd, void __user *arg)
 225{
 226        struct ax25_route_opt_struct rt_option;
 227        struct ax25_routes_struct route;
 228
 229        switch (cmd) {
 230        case SIOCADDRT:
 231                if (copy_from_user(&route, arg, sizeof(route)))
 232                        return -EFAULT;
 233                return ax25_rt_add(&route);
 234
 235        case SIOCDELRT:
 236                if (copy_from_user(&route, arg, sizeof(route)))
 237                        return -EFAULT;
 238                return ax25_rt_del(&route);
 239
 240        case SIOCAX25OPTRT:
 241                if (copy_from_user(&rt_option, arg, sizeof(rt_option)))
 242                        return -EFAULT;
 243                return ax25_rt_opt(&rt_option);
 244
 245        default:
 246                return -EINVAL;
 247        }
 248}
 249
 250#ifdef CONFIG_PROC_FS
 251
 252static void *ax25_rt_seq_start(struct seq_file *seq, loff_t *pos)
 253        __acquires(ax25_route_lock)
 254{
 255        struct ax25_route *ax25_rt;
 256        int i = 1;
 257
 258        read_lock(&ax25_route_lock);
 259        if (*pos == 0)
 260                return SEQ_START_TOKEN;
 261
 262        for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
 263                if (i == *pos)
 264                        return ax25_rt;
 265                ++i;
 266        }
 267
 268        return NULL;
 269}
 270
 271static void *ax25_rt_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 272{
 273        ++*pos;
 274        return (v == SEQ_START_TOKEN) ? ax25_route_list :
 275                ((struct ax25_route *) v)->next;
 276}
 277
 278static void ax25_rt_seq_stop(struct seq_file *seq, void *v)
 279        __releases(ax25_route_lock)
 280{
 281        read_unlock(&ax25_route_lock);
 282}
 283
 284static int ax25_rt_seq_show(struct seq_file *seq, void *v)
 285{
 286        char buf[11];
 287
 288        if (v == SEQ_START_TOKEN)
 289                seq_puts(seq, "callsign  dev  mode digipeaters\n");
 290        else {
 291                struct ax25_route *ax25_rt = v;
 292                const char *callsign;
 293                int i;
 294
 295                if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
 296                        callsign = "default";
 297                else
 298                        callsign = ax2asc(buf, &ax25_rt->callsign);
 299
 300                seq_printf(seq, "%-9s %-4s",
 301                        callsign,
 302                        ax25_rt->dev ? ax25_rt->dev->name : "???");
 303
 304                switch (ax25_rt->ip_mode) {
 305                case 'V':
 306                        seq_puts(seq, "   vc");
 307                        break;
 308                case 'D':
 309                        seq_puts(seq, "   dg");
 310                        break;
 311                default:
 312                        seq_puts(seq, "    *");
 313                        break;
 314                }
 315
 316                if (ax25_rt->digipeat != NULL)
 317                        for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
 318                                seq_printf(seq, " %s",
 319                                     ax2asc(buf, &ax25_rt->digipeat->calls[i]));
 320
 321                seq_puts(seq, "\n");
 322        }
 323        return 0;
 324}
 325
 326static const struct seq_operations ax25_rt_seqops = {
 327        .start = ax25_rt_seq_start,
 328        .next = ax25_rt_seq_next,
 329        .stop = ax25_rt_seq_stop,
 330        .show = ax25_rt_seq_show,
 331};
 332
 333static int ax25_rt_info_open(struct inode *inode, struct file *file)
 334{
 335        return seq_open(file, &ax25_rt_seqops);
 336}
 337
 338const struct file_operations ax25_route_fops = {
 339        .owner = THIS_MODULE,
 340        .open = ax25_rt_info_open,
 341        .read = seq_read,
 342        .llseek = seq_lseek,
 343        .release = seq_release,
 344};
 345
 346#endif
 347
 348/*
 349 *      Find AX.25 route
 350 *
 351 *      Only routes with a reference count of zero can be destroyed.
 352 */
 353ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
 354{
 355        ax25_route *ax25_spe_rt = NULL;
 356        ax25_route *ax25_def_rt = NULL;
 357        ax25_route *ax25_rt;
 358
 359        read_lock(&ax25_route_lock);
 360        /*
 361         *      Bind to the physical interface we heard them on, or the default
 362         *      route if none is found;
 363         */
 364        for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
 365                if (dev == NULL) {
 366                        if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
 367                                ax25_spe_rt = ax25_rt;
 368                        if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
 369                                ax25_def_rt = ax25_rt;
 370                } else {
 371                        if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev)
 372                                ax25_spe_rt = ax25_rt;
 373                        if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev)
 374                                ax25_def_rt = ax25_rt;
 375                }
 376        }
 377
 378        ax25_rt = ax25_def_rt;
 379        if (ax25_spe_rt != NULL)
 380                ax25_rt = ax25_spe_rt;
 381
 382        if (ax25_rt != NULL)
 383                ax25_hold_route(ax25_rt);
 384
 385        read_unlock(&ax25_route_lock);
 386
 387        return ax25_rt;
 388}
 389
 390/*
 391 *      Adjust path: If you specify a default route and want to connect
 392 *      a target on the digipeater path but w/o having a special route
 393 *      set before, the path has to be truncated from your target on.
 394 */
 395static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
 396{
 397        int k;
 398
 399        for (k = 0; k < digipeat->ndigi; k++) {
 400                if (ax25cmp(addr, &digipeat->calls[k]) == 0)
 401                        break;
 402        }
 403
 404        digipeat->ndigi = k;
 405}
 406
 407
 408/*
 409 *      Find which interface to use.
 410 */
 411int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
 412{
 413        ax25_uid_assoc *user;
 414        ax25_route *ax25_rt;
 415        int err = 0;
 416
 417        if ((ax25_rt = ax25_get_route(addr, NULL)) == NULL)
 418                return -EHOSTUNREACH;
 419
 420        if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) {
 421                err = -EHOSTUNREACH;
 422                goto put;
 423        }
 424
 425        user = ax25_findbyuid(current_euid());
 426        if (user) {
 427                ax25->source_addr = user->call;
 428                ax25_uid_put(user);
 429        } else {
 430                if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) {
 431                        err = -EPERM;
 432                        goto put;
 433                }
 434                ax25->source_addr = *(ax25_address *)ax25->ax25_dev->dev->dev_addr;
 435        }
 436
 437        if (ax25_rt->digipeat != NULL) {
 438                ax25->digipeat = kmemdup(ax25_rt->digipeat, sizeof(ax25_digi),
 439                                         GFP_ATOMIC);
 440                if (ax25->digipeat == NULL) {
 441                        err = -ENOMEM;
 442                        goto put;
 443                }
 444                ax25_adjust_path(addr, ax25->digipeat);
 445        }
 446
 447        if (ax25->sk != NULL) {
 448                bh_lock_sock(ax25->sk);
 449                sock_reset_flag(ax25->sk, SOCK_ZAPPED);
 450                bh_unlock_sock(ax25->sk);
 451        }
 452
 453put:
 454        ax25_put_route(ax25_rt);
 455
 456        return err;
 457}
 458
 459struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src,
 460        ax25_address *dest, ax25_digi *digi)
 461{
 462        struct sk_buff *skbn;
 463        unsigned char *bp;
 464        int len;
 465
 466        len = digi->ndigi * AX25_ADDR_LEN;
 467
 468        if (skb_headroom(skb) < len) {
 469                if ((skbn = skb_realloc_headroom(skb, len)) == NULL) {
 470                        printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n");
 471                        return NULL;
 472                }
 473
 474                if (skb->sk != NULL)
 475                        skb_set_owner_w(skbn, skb->sk);
 476
 477                consume_skb(skb);
 478
 479                skb = skbn;
 480        }
 481
 482        bp = skb_push(skb, len);
 483
 484        ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS);
 485
 486        return skb;
 487}
 488
 489/*
 490 *      Free all memory associated with routing structures.
 491 */
 492void __exit ax25_rt_free(void)
 493{
 494        ax25_route *s, *ax25_rt = ax25_route_list;
 495
 496        write_lock_bh(&ax25_route_lock);
 497        while (ax25_rt != NULL) {
 498                s       = ax25_rt;
 499                ax25_rt = ax25_rt->next;
 500
 501                kfree(s->digipeat);
 502                kfree(s);
 503        }
 504        write_unlock_bh(&ax25_route_lock);
 505}
 506
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.