📄 dn_route.c
字号:
/* * If we have padding, remove it. */ if (flags & DN_RT_F_PF) { padlen = flags & ~DN_RT_F_PF; if (!pskb_may_pull(skb, padlen + 1)) goto dump_it; skb_pull(skb, padlen); flags = *skb->data; } skb_reset_network_header(skb); /* * Weed out future version DECnet */ if (flags & DN_RT_F_VER) goto dump_it; cb->rt_flags = flags; if (decnet_debug_level & 1) printk(KERN_DEBUG "dn_route_rcv: got 0x%02x from %s [%d %d %d]\n", (int)flags, (dev) ? dev->name : "???", len, skb->len, padlen); if (flags & DN_RT_PKT_CNTL) { if (unlikely(skb_linearize(skb))) goto dump_it; switch(flags & DN_RT_CNTL_MSK) { case DN_RT_PKT_INIT: dn_dev_init_pkt(skb); break; case DN_RT_PKT_VERI: dn_dev_veri_pkt(skb); break; } if (dn->parms.state != DN_DEV_S_RU) goto dump_it; switch(flags & DN_RT_CNTL_MSK) { case DN_RT_PKT_HELO: return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_route_ptp_hello); case DN_RT_PKT_L1RT: case DN_RT_PKT_L2RT: return NF_HOOK(PF_DECnet, NF_DN_ROUTE, skb, skb->dev, NULL, dn_route_discard); case DN_RT_PKT_ERTH: return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_neigh_router_hello); case DN_RT_PKT_EEDH: return NF_HOOK(PF_DECnet, NF_DN_HELLO, skb, skb->dev, NULL, dn_neigh_endnode_hello); } } else { if (dn->parms.state != DN_DEV_S_RU) goto dump_it; skb_pull(skb, 1); /* Pull flags */ switch(flags & DN_RT_PKT_MSK) { case DN_RT_PKT_LONG: return dn_route_rx_long(skb); case DN_RT_PKT_SHORT: return dn_route_rx_short(skb); } }dump_it: kfree_skb(skb);out: return NET_RX_DROP;}static int dn_output(struct sk_buff *skb){ struct dst_entry *dst = skb->dst; struct dn_route *rt = (struct dn_route *)dst; struct net_device *dev = dst->dev; struct dn_skb_cb *cb = DN_SKB_CB(skb); struct neighbour *neigh; int err = -EINVAL; if ((neigh = dst->neighbour) == NULL) goto error; skb->dev = dev; cb->src = rt->rt_saddr; cb->dst = rt->rt_daddr; /* * Always set the Intra-Ethernet bit on all outgoing packets * originated on this node. Only valid flag from upper layers * is return-to-sender-requested. Set hop count to 0 too. */ cb->rt_flags &= ~DN_RT_F_RQR; cb->rt_flags |= DN_RT_F_IE; cb->hops = 0; return NF_HOOK(PF_DECnet, NF_DN_LOCAL_OUT, skb, NULL, dev, neigh->output);error: if (net_ratelimit()) printk(KERN_DEBUG "dn_output: This should not happen\n"); kfree_skb(skb); return err;}static int dn_forward(struct sk_buff *skb){ struct dn_skb_cb *cb = DN_SKB_CB(skb); struct dst_entry *dst = skb->dst; struct dn_dev *dn_db = dst->dev->dn_ptr; struct dn_route *rt; struct neighbour *neigh = dst->neighbour; int header_len;#ifdef CONFIG_NETFILTER struct net_device *dev = skb->dev;#endif if (skb->pkt_type != PACKET_HOST) goto drop; /* Ensure that we have enough space for headers */ rt = (struct dn_route *)skb->dst; header_len = dn_db->use_long ? 21 : 6; if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+header_len)) goto drop; /* * Hop count exceeded. */ if (++cb->hops > 30) goto drop; skb->dev = rt->u.dst.dev; /* * If packet goes out same interface it came in on, then set * the Intra-Ethernet bit. This has no effect for short * packets, so we don't need to test for them here. */ cb->rt_flags &= ~DN_RT_F_IE; if (rt->rt_flags & RTCF_DOREDIRECT) cb->rt_flags |= DN_RT_F_IE; return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, dev, skb->dev, neigh->output);drop: kfree_skb(skb); return NET_RX_DROP;}/* * Drop packet. This is used for endnodes and for * when we should not be forwarding packets from * this dest. */static int dn_blackhole(struct sk_buff *skb){ kfree_skb(skb); return NET_RX_DROP;}/* * Used to catch bugs. This should never normally get * called. */static int dn_rt_bug(struct sk_buff *skb){ if (net_ratelimit()) { struct dn_skb_cb *cb = DN_SKB_CB(skb); printk(KERN_DEBUG "dn_rt_bug: skb from:%04x to:%04x\n", dn_ntohs(cb->src), dn_ntohs(cb->dst)); } kfree_skb(skb); return NET_RX_BAD;}static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res){ struct dn_fib_info *fi = res->fi; struct net_device *dev = rt->u.dst.dev; struct neighbour *n; unsigned mss; if (fi) { if (DN_FIB_RES_GW(*res) && DN_FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) rt->rt_gateway = DN_FIB_RES_GW(*res); memcpy(rt->u.dst.metrics, fi->fib_metrics, sizeof(rt->u.dst.metrics)); } rt->rt_type = res->type; if (dev != NULL && rt->u.dst.neighbour == NULL) { n = __neigh_lookup_errno(&dn_neigh_table, &rt->rt_gateway, dev); if (IS_ERR(n)) return PTR_ERR(n); rt->u.dst.neighbour = n; } if (rt->u.dst.metrics[RTAX_MTU-1] == 0 || rt->u.dst.metrics[RTAX_MTU-1] > rt->u.dst.dev->mtu) rt->u.dst.metrics[RTAX_MTU-1] = rt->u.dst.dev->mtu; mss = dn_mss_from_pmtu(dev, dst_mtu(&rt->u.dst)); if (rt->u.dst.metrics[RTAX_ADVMSS-1] == 0 || rt->u.dst.metrics[RTAX_ADVMSS-1] > mss) rt->u.dst.metrics[RTAX_ADVMSS-1] = mss; return 0;}static inline int dn_match_addr(__le16 addr1, __le16 addr2){ __u16 tmp = dn_ntohs(addr1) ^ dn_ntohs(addr2); int match = 16; while(tmp) { tmp >>= 1; match--; } return match;}static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope){ __le16 saddr = 0; struct dn_dev *dn_db = dev->dn_ptr; struct dn_ifaddr *ifa; int best_match = 0; int ret; read_lock(&dev_base_lock); for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_scope > scope) continue; if (!daddr) { saddr = ifa->ifa_local; break; } ret = dn_match_addr(daddr, ifa->ifa_local); if (ret > best_match) saddr = ifa->ifa_local; if (best_match == 0) saddr = ifa->ifa_local; } read_unlock(&dev_base_lock); return saddr;}static inline __le16 __dn_fib_res_prefsrc(struct dn_fib_res *res){ return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope);}static inline __le16 dn_fib_rules_map_destination(__le16 daddr, struct dn_fib_res *res){ __le16 mask = dnet_make_mask(res->prefixlen); return (daddr&~mask)|res->fi->fib_nh->nh_gw;}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, } }, .mark = oldflp->mark, .iif = init_net.loopback_dev->ifindex, .oif = oldflp->oif }; struct dn_route *rt = NULL; struct net_device *dev_out = NULL, *dev; 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->mark, init_net.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(&init_net, 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_each_netdev(&init_net, dev) { if (!dev->dn_ptr) continue; if (!dn_dev_islocal(dev, oldflp->fld_src)) continue; if ((dev->flags & IFF_LOOPBACK) && oldflp->fld_dst && !dn_dev_islocal(dev, oldflp->fld_dst)) continue; dev_out = dev; 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 = init_net.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 = init_net.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 = init_net.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 = init_net.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 = init_net.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; rt->fl.mark = oldflp->mark; 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;}/* * N.B. The flags may be moved into the flowi at some future stage. */static int __dn_route_output_key(struct dst_entry **pprt, const struct flowi *flp, int flags){ unsigned hash = dn_hash(flp->fld_src, flp->fld_dst); struct dn_route *rt = NULL; if (!(flags & MSG_TRYHARD)) { rcu_read_lock_bh(); for(rt = rcu_dereference(dn_rt_hash_table[hash].chain); rt; rt = rcu_dereference(rt->u.dst.dn_next)) { if ((flp->fld_dst == rt->fl.fld_dst) && (flp->fld_src == rt->fl.fld_src) && (flp->mark == rt->fl.mark) && (rt->fl.iif == 0) && (rt->fl.oif == flp->oif)) { dst_use(&rt->u.dst, jiffies); rcu_read_unlock_bh(); *pprt = &rt->u.dst; return 0; } } rcu_read_unlock_bh(); } return dn_route_output_slow(pprt, flp, flags);}static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int flags){ int err; err = __dn_route_output_key(pprt, flp, flags); if (err == 0 && flp->proto) { err = xfrm_lookup(pprt, flp, NULL, 0); } return err;}int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock *sk, int flags){ int err; err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD); if (err == 0 && fl->proto) { err = xfrm_lookup(pprt, fl, sk, !(flags & MSG_DONTWAIT)); } return err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -