static int dn_def_dev_strategy(ctl_table *table, int *name, int nlen, void *oldval, size_t *oldlenp, void *newval, size_t newlen, void **context) { size_t len; struct net_device *dev; char devname[17]; size_t namel; int rv = 0; devname[0] = 0; if (oldval && oldlenp) { if (get_user(len, oldlenp)) return -EFAULT; if (len) { dev = dn_dev_get_default(); if (dev) { strcpy(devname, dev->name); dev_put(dev); } namel = strlen(devname) + 1; if (len > namel) len = namel; if (copy_to_user(oldval, devname, len)) return -EFAULT; if (put_user(len, oldlenp)) return -EFAULT; } } if (newval && newlen) { if (newlen > 16) return -E2BIG; if (copy_from_user(devname, newval, newlen)) return -EFAULT; devname[newlen] = 0; dev = dev_get_by_name(devname); if (dev == NULL) return -ENODEV; rv = -ENODEV; if (dev->dn_ptr != NULL) { rv = dn_dev_set_default(dev, 1); if (rv) dev_put(dev); } } return rv; }
static int dn_def_dev_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { size_t len; struct net_device *dev; char devname[17]; if (!*lenp || (*ppos && !write)) { *lenp = 0; return 0; } if (write) { if (*lenp > 16) return -E2BIG; if (copy_from_user(devname, buffer, *lenp)) return -EFAULT; devname[*lenp] = 0; strip_it(devname); dev = dev_get_by_name(&init_net, devname); if (dev == NULL) return -ENODEV; if (dev->dn_ptr == NULL) { dev_put(dev); return -ENODEV; } if (dn_dev_set_default(dev, 1)) { dev_put(dev); return -ENODEV; } *ppos += *lenp; return 0; } dev = dn_dev_get_default(); if (dev == NULL) { *lenp = 0; return 0; } strcpy(devname, dev->name); dev_put(dev); len = strlen(devname); devname[len++] = '\n'; if (len > *lenp) len = *lenp; if (copy_to_user(buffer, devname, len)) return -EFAULT; *lenp = len; *ppos += len; return 0; }
static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *oldflp, int try_hard) { struct flowi fl = { .nl_u = { .dn_u = { .daddr = oldflp->fld_dst, .saddr = oldflp->fld_src, .scope = RT_SCOPE_UNIVERSE, #ifdef CONFIG_DECNET_ROUTE_FWMARK .fwmark = oldflp->fld_fwmark #endif } }, .iif = loopback_dev.ifindex, .oif = oldflp->oif }; struct dn_route *rt = NULL; struct net_device *dev_out = NULL; struct neighbour *neigh = NULL; unsigned hash; unsigned flags = 0; struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST }; int err; int free_res = 0; __le16 gateway = 0; if (decnet_debug_level & 16) printk(KERN_DEBUG "dn_route_output_slow: dst=%04x src=%04x mark=%d" " iif=%d oif=%d\n", dn_ntohs(oldflp->fld_dst), dn_ntohs(oldflp->fld_src), oldflp->fld_fwmark, loopback_dev.ifindex, oldflp->oif); /* If we have an output interface, verify its a DECnet device */ if (oldflp->oif) { dev_out = dev_get_by_index(oldflp->oif); err = -ENODEV; if (dev_out && dev_out->dn_ptr == NULL) { dev_put(dev_out); dev_out = NULL; } if (dev_out == NULL) goto out; } /* If we have a source address, verify that its a local address */ if (oldflp->fld_src) { err = -EADDRNOTAVAIL; if (dev_out) { if (dn_dev_islocal(dev_out, oldflp->fld_src)) goto source_ok; dev_put(dev_out); goto out; } read_lock(&dev_base_lock); for(dev_out = dev_base; dev_out; dev_out = dev_out->next) { if (!dev_out->dn_ptr) continue; if (!dn_dev_islocal(dev_out, oldflp->fld_src)) continue; if ((dev_out->flags & IFF_LOOPBACK) && oldflp->fld_dst && !dn_dev_islocal(dev_out, oldflp->fld_dst)) continue; break; } read_unlock(&dev_base_lock); if (dev_out == NULL) goto out; dev_hold(dev_out); source_ok: ; } /* No destination? Assume its local */ if (!fl.fld_dst) { fl.fld_dst = fl.fld_src; err = -EADDRNOTAVAIL; if (dev_out) dev_put(dev_out); dev_out = &loopback_dev; dev_hold(dev_out); if (!fl.fld_dst) { fl.fld_dst = fl.fld_src = dnet_select_source(dev_out, 0, RT_SCOPE_HOST); if (!fl.fld_dst) goto out; } fl.oif = loopback_dev.ifindex; res.type = RTN_LOCAL; goto make_route; } if (decnet_debug_level & 16) printk(KERN_DEBUG "dn_route_output_slow: initial checks complete." " dst=%o4x src=%04x oif=%d try_hard=%d\n", dn_ntohs(fl.fld_dst), dn_ntohs(fl.fld_src), fl.oif, try_hard); /* * N.B. If the kernel is compiled without router support then * dn_fib_lookup() will evaluate to non-zero so this if () block * will always be executed. */ err = -ESRCH; if (try_hard || (err = dn_fib_lookup(&fl, &res)) != 0) { struct dn_dev *dn_db; if (err != -ESRCH) goto out; /* * Here the fallback is basically the standard algorithm for * routing in endnodes which is described in the DECnet routing * docs * * If we are not trying hard, look in neighbour cache. * The result is tested to ensure that if a specific output * device/source address was requested, then we honour that * here */ if (!try_hard) { neigh = neigh_lookup_nodev(&dn_neigh_table, &fl.fld_dst); if (neigh) { if ((oldflp->oif && (neigh->dev->ifindex != oldflp->oif)) || (oldflp->fld_src && (!dn_dev_islocal(neigh->dev, oldflp->fld_src)))) { neigh_release(neigh); neigh = NULL; } else { if (dev_out) dev_put(dev_out); if (dn_dev_islocal(neigh->dev, fl.fld_dst)) { dev_out = &loopback_dev; res.type = RTN_LOCAL; } else { dev_out = neigh->dev; } dev_hold(dev_out); goto select_source; } } } /* Not there? Perhaps its a local address */ if (dev_out == NULL) dev_out = dn_dev_get_default(); err = -ENODEV; if (dev_out == NULL) goto out; dn_db = dev_out->dn_ptr; /* Possible improvement - check all devices for local addr */ if (dn_dev_islocal(dev_out, fl.fld_dst)) { dev_put(dev_out); dev_out = &loopback_dev; dev_hold(dev_out); res.type = RTN_LOCAL; goto select_source; } /* Not local either.... try sending it to the default router */ neigh = neigh_clone(dn_db->router); BUG_ON(neigh && neigh->dev != dev_out); /* Ok then, we assume its directly connected and move on */ select_source: if (neigh) gateway = ((struct dn_neigh *)neigh)->addr; if (gateway == 0) gateway = fl.fld_dst; if (fl.fld_src == 0) { fl.fld_src = dnet_select_source(dev_out, gateway, res.type == RTN_LOCAL ? RT_SCOPE_HOST : RT_SCOPE_LINK); if (fl.fld_src == 0 && res.type != RTN_LOCAL) goto e_addr; } fl.oif = dev_out->ifindex; goto make_route; } free_res = 1; if (res.type == RTN_NAT) goto e_inval; if (res.type == RTN_LOCAL) { if (!fl.fld_src) fl.fld_src = fl.fld_dst; if (dev_out) dev_put(dev_out); dev_out = &loopback_dev; dev_hold(dev_out); fl.oif = dev_out->ifindex; if (res.fi) dn_fib_info_put(res.fi); res.fi = NULL; goto make_route; } if (res.fi->fib_nhs > 1 && fl.oif == 0) dn_fib_select_multipath(&fl, &res); /* * We could add some logic to deal with default routes here and * get rid of some of the special casing above. */ if (!fl.fld_src) fl.fld_src = DN_FIB_RES_PREFSRC(res); if (dev_out) dev_put(dev_out); dev_out = DN_FIB_RES_DEV(res); dev_hold(dev_out); fl.oif = dev_out->ifindex; gateway = DN_FIB_RES_GW(res); make_route: if (dev_out->flags & IFF_LOOPBACK) flags |= RTCF_LOCAL; rt = dst_alloc(&dn_dst_ops); if (rt == NULL) goto e_nobufs; atomic_set(&rt->u.dst.__refcnt, 1); rt->u.dst.flags = DST_HOST; rt->fl.fld_src = oldflp->fld_src; rt->fl.fld_dst = oldflp->fld_dst; rt->fl.oif = oldflp->oif; rt->fl.iif = 0; #ifdef CONFIG_DECNET_ROUTE_FWMARK rt->fl.fld_fwmark = oldflp->fld_fwmark; #endif rt->rt_saddr = fl.fld_src; rt->rt_daddr = fl.fld_dst; rt->rt_gateway = gateway ? gateway : fl.fld_dst; rt->rt_local_src = fl.fld_src; rt->rt_dst_map = fl.fld_dst; rt->rt_src_map = fl.fld_src; rt->u.dst.dev = dev_out; dev_hold(dev_out); rt->u.dst.neighbour = neigh; neigh = NULL; rt->u.dst.lastuse = jiffies; rt->u.dst.output = dn_output; rt->u.dst.input = dn_rt_bug; rt->rt_flags = flags; if (flags & RTCF_LOCAL) rt->u.dst.input = dn_nsp_rx; err = dn_rt_set_next_hop(rt, &res); if (err) goto e_neighbour; hash = dn_hash(rt->fl.fld_src, rt->fl.fld_dst); dn_insert_route(rt, hash, (struct dn_route **)pprt); done: if (neigh) neigh_release(neigh); if (free_res) dn_fib_res_put(&res); if (dev_out) dev_put(dev_out); out: return err; e_addr: err = -EADDRNOTAVAIL; goto done; e_inval: err = -EINVAL; goto done; e_nobufs: err = -ENOBUFS; goto done; e_neighbour: dst_free(&rt->u.dst); goto e_nobufs; }