📄 dn_route.c
字号:
} skb->nh.raw = skb->data; /* * 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_is_nonlinear(skb)) && skb_linearize(skb, GFP_ATOMIC) != 0) 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 **pskb){ struct sk_buff *skb = *pskb; 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", cb->src, cb->dst); } kfree_skb(skb); return NET_RX_BAD;}static int dn_rt_bug_out(struct sk_buff **pskb){ return dn_rt_bug(*pskb);}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_pmtu(&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(__u16 addr1, __u16 addr2){ __u16 tmp = dn_ntohs(addr1) ^ dn_ntohs(addr2); int match = 16; while(tmp) { tmp >>= 1; match--; } return match;}static __u16 dnet_select_source(const struct net_device *dev, __u16 daddr, int scope){ __u16 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 __u16 __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 __u16 dn_fib_rules_map_destination(__u16 daddr, struct dn_fib_res *res){ __u16 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,#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; __u16 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", oldflp->fld_dst, 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)) 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", fl.fld_dst, 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;}/* * 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.rt_next)) { if ((flp->fld_dst == rt->fl.fld_dst) && (flp->fld_src == rt->fl.fld_src) &&#ifdef CONFIG_DECNET_ROUTE_FWMARK (flp->fld_fwmark == rt->fl.fld_fwmark) &&#endif (rt->fl.iif == 0) && (rt->fl.oif == flp->oif)) { rt->u.dst.lastuse = jiffies; dst_hold(&rt->u.dst); rt->u.dst.__use++; 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;}static int dn_route_input_slow(struct sk_buff *skb){ struct dn_route *rt = NULL; struct dn_skb_cb *cb = DN_SKB_CB(skb); struct net_device *in_dev = skb->dev; struct net_device *out_dev = NULL; struct dn_dev *dn_db; struct neighbour *neigh = NULL; unsigned hash; int flags = 0; __u16 gateway = 0; __u16 local_src = 0; struct flowi fl = { .nl_u = { .dn_u =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -