/* * device anycast group decrement */ int __ipv6_dev_ac_dec(struct inet6_dev *idev, struct in6_addr *addr) { struct ifacaddr6 *aca, *prev_aca; write_lock_bh(&idev->lock); prev_aca = NULL; for (aca = idev->ac_list; aca; aca = aca->aca_next) { if (ipv6_addr_equal(&aca->aca_addr, addr)) break; prev_aca = aca; } if (!aca) { write_unlock_bh(&idev->lock); return -ENOENT; } if (--aca->aca_users > 0) { write_unlock_bh(&idev->lock); return 0; } if (prev_aca) prev_aca->aca_next = aca->aca_next; else idev->ac_list = aca->aca_next; write_unlock_bh(&idev->lock); addrconf_leave_solict(idev, &aca->aca_addr); dst_hold(&aca->aca_rt->u.dst); if (ip6_del_rt(aca->aca_rt)) dst_free(&aca->aca_rt->u.dst); else dst_release(&aca->aca_rt->u.dst); aca_put(aca); return 0; }
int ip6_rt_addr_add(struct in6_addr *addr, struct net_device *dev) { struct rt6_info *rt; rt = dst_alloc(&ip6_dst_ops); if (rt == NULL) return -ENOMEM; rt->u.dst.flags = DST_HOST; rt->u.dst.input = ip6_input; rt->u.dst.output = ip6_output; rt->rt6i_dev = dev_get_by_name("lo"); rt->u.dst.pmtu = ipv6_get_mtu(rt->rt6i_dev); rt->u.dst.advmss = max_t(unsigned int, rt->u.dst.pmtu - 60, ip6_rt_min_advmss); if (rt->u.dst.advmss > 65535-20) rt->u.dst.advmss = 65535; rt->rt6i_hoplimit = ipv6_get_hoplimit(rt->rt6i_dev); rt->u.dst.obsolete = -1; rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); if (rt->rt6i_nexthop == NULL) { dst_free((struct dst_entry *) rt); return -ENOMEM; } ipv6_addr_copy(&rt->rt6i_dst.addr, addr); rt->rt6i_dst.plen = 128; rt6_ins(rt, NULL); return 0; }
/* Release a nexthop info record */ static void free_fib_info_rcu(struct rcu_head *head) { struct fib_info *fi = container_of(head, struct fib_info, rcu); change_nexthops(fi) { if (nexthop_nh->nh_dev) dev_put(nexthop_nh->nh_dev); if (nexthop_nh->nh_exceptions) free_nh_exceptions(nexthop_nh); if (nexthop_nh->nh_rth_output) dst_free(&nexthop_nh->nh_rth_output->dst); if (nexthop_nh->nh_rth_input) dst_free(&nexthop_nh->nh_rth_input->dst); } endfor_nexthops(fi); release_net(fi->fib_net); if (fi->fib_metrics != (u32 *) dst_default_metrics) kfree(fi->fib_metrics); kfree(fi); }
struct mpls_ilm* mpls_ilm_dst_alloc(unsigned int key, struct mpls_label *ml, unsigned short family, struct mpls_instr_elem *instr, int instr_len) { struct mpls_ilm *ilm; int result; MPLS_ENTER; ilm = dst_alloc (&ilm_dst_ops); if (unlikely(!ilm)) goto ilm_dst_alloc_0; memcpy(&(ilm->ilm_label),ml,sizeof(struct mpls_label)); INIT_LIST_HEAD(&ilm->dev_entry); INIT_LIST_HEAD(&ilm->nhlfe_entry); INIT_LIST_HEAD(&ilm->global); ilm->ilm_instr = NULL; ilm->ilm_key = key; ilm->ilm_labelspace = ml->ml_index; ilm->ilm_age = jiffies; ilm->ilm_proto = mpls_proto_find_by_family(family); ilm->ilm_fix_hh = 0; if (unlikely(!ilm->ilm_proto)) { MPLS_DEBUG("Unable to find protocol driver for '0x%04x'\n", family); goto ilm_dst_alloc_1; } else { ilm->u.dst.input = ilm->ilm_proto->local_deliver; } ilm->u.dst.dev = init_net.loopback_dev; result = mpls_set_in_instrs(instr, instr_len, ilm); if (result) goto ilm_dst_alloc_2; MPLS_EXIT; return ilm; /* Error Path */ ilm_dst_alloc_2: mpls_proto_release(ilm->ilm_proto); ilm_dst_alloc_1: ilm->u.dst.obsolete = 1; dst_free(&ilm->u.dst); ilm_dst_alloc_0: MPLS_EXIT; return NULL; }
static void rt_fibinfo_free(struct rtable __rcu **rtp) { struct rtable *rt = rcu_dereference_protected(*rtp, 1); if (!rt) return; /* Not even needed : RCU_INIT_POINTER(*rtp, NULL); * because we waited an RCU grace period before calling * free_fib_info_rcu() */ dst_free(&rt->dst); }
void dn_run_flush(unsigned long dummy) { int i; struct dn_route *rt, *next; for(i = 0; i < dn_rt_hash_mask; i++) { spin_lock_bh(&dn_rt_hash_table[i].lock); if ((rt = xchg(&dn_rt_hash_table[i].chain, NULL)) == NULL) goto nothing_to_declare; for(; rt; rt=next) { next = rt->u.rt_next; rt->u.rt_next = NULL; dst_free((struct dst_entry *)rt); } nothing_to_declare: spin_unlock_bh(&dn_rt_hash_table[i].lock); } }
static int __xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, struct flowi *fl, struct dst_entry **dst_p) { struct dst_entry *dst, *dst_prev; struct rtable *rt0 = (struct rtable*)(*dst_p); struct rtable *rt = rt0; struct flowi fl_tunnel = { .nl_u = { .ip4_u = { .saddr = fl->fl4_src, .daddr = fl->fl4_dst, .tos = fl->fl4_tos } } }; int i; int err; int header_len = 0; int trailer_len = 0; dst = dst_prev = NULL; dst_hold(&rt->u.dst); for (i = 0; i < nx; i++) { struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops); struct xfrm_dst *xdst; if (unlikely(dst1 == NULL)) { err = -ENOBUFS; dst_release(&rt->u.dst); goto error; } if (!dst) dst = dst1; else { dst_prev->child = dst1; dst1->flags |= DST_NOHASH; dst_clone(dst1); } xdst = (struct xfrm_dst *)dst1; xdst->route = &rt->u.dst; xdst->genid = xfrm[i]->genid; dst1->next = dst_prev; dst_prev = dst1; header_len += xfrm[i]->props.header_len; trailer_len += xfrm[i]->props.trailer_len; if (xfrm[i]->props.mode == XFRM_MODE_TUNNEL) { unsigned short encap_family = xfrm[i]->props.family; switch(encap_family) { case AF_INET: fl_tunnel.fl4_dst = xfrm[i]->id.daddr.a4; fl_tunnel.fl4_src = xfrm[i]->props.saddr.a4; break; #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) case AF_INET6: ipv6_addr_copy(&fl_tunnel.fl6_dst, (struct in6_addr*)&xfrm[i]->id.daddr.a6); ipv6_addr_copy(&fl_tunnel.fl6_src, (struct in6_addr*)&xfrm[i]->props.saddr.a6); break; #endif default: BUG_ON(1); } err = xfrm_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel, encap_family); if (err) goto error; } else dst_hold(&rt->u.dst); } dst_prev->child = &rt->u.dst; dst->path = &rt->u.dst; *dst_p = dst; dst = dst_prev; dst_prev = *dst_p; i = 0; for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; struct xfrm_state_afinfo *afinfo; x->u.rt.fl = *fl; dst_prev->xfrm = xfrm[i++]; dst_prev->dev = rt->u.dst.dev; if (rt->u.dst.dev) dev_hold(rt->u.dst.dev); dst_prev->obsolete = -1; dst_prev->flags |= DST_HOST; dst_prev->lastuse = jiffies; dst_prev->header_len = header_len; dst_prev->nfheader_len = 0; dst_prev->trailer_len = trailer_len; memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics)); /* Copy neighbout for reachability confirmation */ dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); dst_prev->input = rt->u.dst.input; /* XXX: When IPv6 module can be unloaded, we should manage reference * to xfrm6_output in afinfo->output. Miyazawa * */ afinfo = xfrm_state_get_afinfo(dst_prev->xfrm->props.family); if (!afinfo) { dst = *dst_p; err = -EAFNOSUPPORT; goto error; } dst_prev->output = afinfo->output; xfrm_state_put_afinfo(afinfo); if (dst_prev->xfrm->props.family == AF_INET && rt->peer) atomic_inc(&rt->peer->refcnt); x->u.rt.peer = rt->peer; /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); x->u.rt.rt_type = rt->rt_type; x->u.rt.rt_src = rt0->rt_src; x->u.rt.rt_dst = rt0->rt_dst; x->u.rt.rt_gateway = rt->rt_gateway; x->u.rt.rt_spec_dst = rt0->rt_spec_dst; x->u.rt.idev = rt0->idev; in_dev_hold(rt0->idev); header_len -= x->u.dst.xfrm->props.header_len; trailer_len -= x->u.dst.xfrm->props.trailer_len; } xfrm_init_pmtu(dst); return 0; error: if (dst) dst_free(dst); return err; }
static __inline__ void rt6_release(struct rt6_info *rt) { if (atomic_dec_and_test(&rt->rt6i_ref)) dst_free(&rt->dst); }
int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh) { int err; struct rtmsg *r; struct rt6_info *rt; struct net_device *dev = NULL; int addr_type; if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128) return -EINVAL; #ifndef CONFIG_IPV6_SUBTREES if (rtmsg->rtmsg_src_len) return -EINVAL; #endif if (rtmsg->rtmsg_metric == 0) rtmsg->rtmsg_metric = IP6_RT_PRIO_USER; rt = dst_alloc(&ip6_dst_ops); if (rt == NULL) return -ENOMEM; rt->u.dst.obsolete = -1; rt->rt6i_expires = rtmsg->rtmsg_info; if (nlh && (r = NLMSG_DATA(nlh))) { rt->rt6i_protocol = r->rtm_protocol; } else { rt->rt6i_protocol = RTPROT_BOOT; } addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst); if (addr_type & IPV6_ADDR_MULTICAST) rt->u.dst.input = ip6_mc_input; else rt->u.dst.input = ip6_forward; rt->u.dst.output = ip6_output; if (rtmsg->rtmsg_ifindex) { dev = dev_get_by_index(rtmsg->rtmsg_ifindex); err = -ENODEV; if (dev == NULL) goto out; } ipv6_addr_prefix(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst, rtmsg->rtmsg_dst_len); rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len; if (rt->rt6i_dst.plen == 128) rt->u.dst.flags = DST_HOST; #ifdef CONFIG_IPV6_SUBTREES ipv6_addr_prefix(&rt->rt6i_src.addr, &rtmsg->rtmsg_src, rtmsg->rtmsg_src_len); rt->rt6i_src.plen = rtmsg->rtmsg_src_len; #endif rt->rt6i_metric = rtmsg->rtmsg_metric; /* We cannot add true routes via loopback here, they would result in kernel looping; promote them to reject routes */ if ((rtmsg->rtmsg_flags&RTF_REJECT) || (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { if (dev) dev_put(dev); dev = &loopback_dev; dev_hold(dev); rt->u.dst.output = ip6_pkt_discard; rt->u.dst.input = ip6_pkt_discard; rt->u.dst.error = -ENETUNREACH; rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; goto install_route; } if (rtmsg->rtmsg_flags & RTF_GATEWAY) { struct in6_addr *gw_addr; int gwa_type; gw_addr = &rtmsg->rtmsg_gateway; ipv6_addr_copy(&rt->rt6i_gateway, &rtmsg->rtmsg_gateway); gwa_type = ipv6_addr_type(gw_addr); if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { struct rt6_info *grt; /* IPv6 strictly inhibits using not link-local addresses as nexthop address. Otherwise, router will not able to send redirects. It is very good, but in some (rare!) curcumstances (SIT, PtP, NBMA NOARP links) it is handy to allow some exceptions. --ANK */ err = -EINVAL; if (!(gwa_type&IPV6_ADDR_UNICAST)) goto out; grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1); err = -EHOSTUNREACH; if (grt == NULL) goto out; if (dev) { if (dev != grt->rt6i_dev) { dst_release(&grt->u.dst); goto out; } } else { dev = grt->rt6i_dev; dev_hold(dev); } if (!(grt->rt6i_flags&RTF_GATEWAY)) err = 0; dst_release(&grt->u.dst); if (err) goto out; } err = -EINVAL; if (dev == NULL || (dev->flags&IFF_LOOPBACK)) goto out; } err = -ENODEV; if (dev == NULL) goto out; if (rtmsg->rtmsg_flags & (RTF_GATEWAY|RTF_NONEXTHOP)) { rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev); if (IS_ERR(rt->rt6i_nexthop)) { err = PTR_ERR(rt->rt6i_nexthop); rt->rt6i_nexthop = NULL; goto out; } } if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS; else rt->rt6i_hoplimit = ipv6_get_hoplimit(dev); rt->rt6i_flags = rtmsg->rtmsg_flags; install_route: rt->u.dst.pmtu = ipv6_get_mtu(dev); rt->u.dst.advmss = max_t(unsigned int, rt->u.dst.pmtu - 60, ip6_rt_min_advmss); /* Maximal non-jumbo IPv6 payload is 65535 and corresponding MSS is 65535 - tcp_header_size. 65535 is also valid and means: "any MSS, rely only on pmtu discovery" */ if (rt->u.dst.advmss > 65535-20) rt->u.dst.advmss = 65535; rt->u.dst.dev = dev; return rt6_ins(rt, nlh); out: if (dev) dev_put(dev); dst_free((struct dst_entry *) rt); return err; }
int mpls_add_in_label (const struct mpls_in_label_req *in) { struct mpls_ilm *ilm = NULL; /* New ILM to insert */ struct mpls_label *ml = NULL; /* Requested Label */ unsigned int key = 0; /* Key to use */ int retval = 0; struct mpls_instr_elem instr[2]; MPLS_ENTER; BUG_ON(!in); ml = (struct mpls_label *)&in->mil_label; if (mpls_is_reserved_label(ml)) { MPLS_DEBUG("Unable to add reserved label to ILM\n"); retval = -EINVAL; goto error; } /* Obtain key */ key = mpls_label2key(/* labelspace*/ ml->ml_index, ml); /* Check if the node already exists */ ilm = mpls_get_ilm(key); if (unlikely(ilm)) { printk (MPLS_ERR "MPLS: node %u already exists\n",key); mpls_ilm_release(ilm); retval = -EEXIST; goto error; } /* * Allocate a new input Information/Label, */ instr[0].mir_direction = MPLS_IN; instr[0].mir_opcode = MPLS_OP_POP; instr[1].mir_direction = MPLS_IN; instr[1].mir_opcode = MPLS_OP_PEEK; ilm = mpls_ilm_dst_alloc (key, ml, in->mil_proto, instr, 2); if (unlikely(!ilm)) { retval = -ENOMEM; goto error; } /* Insert into ILM tree */ spin_lock_bh (&mpls_ilm_lock); if (unlikely(mpls_insert_ilm(key,ilm))) { mpls_ilm_release (ilm); spin_unlock_bh (&mpls_ilm_lock); ilm->u.dst.obsolete = 1; dst_free (&ilm->u.dst); retval = -ENOMEM; goto error; } mpls_ilm_hold(ilm); spin_unlock_bh (&mpls_ilm_lock); /* we have hold a refcnt to the ilm across mpls_ilm_event() * to make sure it can't disappear */ mpls_ilm_event(MPLS_CMD_NEWILM, ilm); mpls_ilm_release(ilm); error: MPLS_EXIT; return retval; }
static int __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, struct flowi *fl, struct dst_entry **dst_p) { struct dst_entry *dst, *dst_prev; struct rt6_info *rt0 = (struct rt6_info*)(*dst_p); struct rt6_info *rt = rt0; struct in6_addr *remote = &fl->fl6_dst; struct in6_addr *local = &fl->fl6_src; int i; int err = 0; int header_len = 0; int trailer_len = 0; dst = dst_prev = NULL; for (i = 0; i < nx; i++) { struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); if (unlikely(dst1 == NULL)) { err = -ENOBUFS; goto error; } if (!dst) dst = dst1; else { dst_prev->child = dst1; dst1->flags |= DST_NOHASH; dst_clone(dst1); } dst_prev = dst1; if (xfrm[i]->props.mode) { remote = (struct in6_addr*)&xfrm[i]->id.daddr; local = (struct in6_addr*)&xfrm[i]->props.saddr; } header_len += xfrm[i]->props.header_len; trailer_len += xfrm[i]->props.trailer_len; } if (ipv6_addr_cmp(remote, &fl->fl6_dst)) { struct flowi fl_tunnel; memset(&fl_tunnel, 0, sizeof(fl_tunnel)); ipv6_addr_copy(&fl_tunnel.fl6_dst, remote); ipv6_addr_copy(&fl_tunnel.fl6_src, local); err = xfrm_dst_lookup((struct xfrm_dst **) &rt, &fl_tunnel, AF_INET6); if (err) goto error; } else { dst_hold(&rt->u.dst); } dst_prev->child = &rt->u.dst; i = 0; for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; dst_prev->xfrm = xfrm[i++]; dst_prev->dev = rt->u.dst.dev; if (rt->u.dst.dev) dev_hold(rt->u.dst.dev); dst_prev->obsolete = -1; dst_prev->flags |= DST_HOST; dst_prev->lastuse = jiffies; dst_prev->header_len = header_len; dst_prev->trailer_len = trailer_len; memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics)); dst_prev->path = &rt->u.dst; /* Copy neighbour for reachability confirmation */ dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); dst_prev->input = rt->u.dst.input; dst_prev->output = dst_prev->xfrm->type->output; /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); x->u.rt6.rt6i_metric = rt0->rt6i_metric; x->u.rt6.rt6i_node = rt0->rt6i_node; x->u.rt6.rt6i_gateway = rt0->rt6i_gateway; memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); x->u.rt6.rt6i_dst = rt0->rt6i_dst; x->u.rt6.rt6i_src = rt0->rt6i_src; header_len -= x->u.dst.xfrm->props.header_len; trailer_len -= x->u.dst.xfrm->props.trailer_len; } *dst_p = dst; return 0; error: if (dst) dst_free(dst); return err; }
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; }
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; __le16 gateway = 0; __le16 local_src = 0; struct flowi fl = { .nl_u = { .dn_u = { .daddr = cb->dst, .saddr = cb->src, .scope = RT_SCOPE_UNIVERSE, #ifdef CONFIG_DECNET_ROUTE_FWMARK .fwmark = skb->nfmark #endif } }, .iif = skb->dev->ifindex }; struct dn_fib_res res = { .fi = NULL, .type = RTN_UNREACHABLE }; int err = -EINVAL; int free_res = 0; dev_hold(in_dev); if ((dn_db = in_dev->dn_ptr) == NULL) goto out; /* Zero source addresses are not allowed */ if (fl.fld_src == 0) goto out; /* * In this case we've just received a packet from a source * outside ourselves pretending to come from us. We don't * allow it any further to prevent routing loops, spoofing and * other nasties. Loopback packets already have the dst attached * so this only affects packets which have originated elsewhere. */ err = -ENOTUNIQ; if (dn_dev_islocal(in_dev, cb->src)) goto out; err = dn_fib_lookup(&fl, &res); if (err) { if (err != -ESRCH) goto out; /* * Is the destination us ? */ if (!dn_dev_islocal(in_dev, cb->dst)) goto e_inval; res.type = RTN_LOCAL; } else { __le16 src_map = fl.fld_src; free_res = 1; out_dev = DN_FIB_RES_DEV(res); if (out_dev == NULL) { if (net_ratelimit()) printk(KERN_CRIT "Bug in dn_route_input_slow() " "No output device\n"); goto e_inval; } dev_hold(out_dev); if (res.r) src_map = fl.fld_src; /* no NAT support for now */ gateway = DN_FIB_RES_GW(res); if (res.type == RTN_NAT) { fl.fld_dst = dn_fib_rules_map_destination(fl.fld_dst, &res); dn_fib_res_put(&res); free_res = 0; if (dn_fib_lookup(&fl, &res)) goto e_inval; free_res = 1; if (res.type != RTN_UNICAST) goto e_inval; flags |= RTCF_DNAT; gateway = fl.fld_dst; } fl.fld_src = src_map; } switch(res.type) { case RTN_UNICAST: /* * Forwarding check here, we only check for forwarding * being turned off, if you want to only forward intra * area, its up to you to set the routing tables up * correctly. */ if (dn_db->parms.forwarding == 0) goto e_inval; if (res.fi->fib_nhs > 1 && fl.oif == 0) dn_fib_select_multipath(&fl, &res); /* * Check for out_dev == in_dev. We use the RTCF_DOREDIRECT * flag as a hint to set the intra-ethernet bit when * forwarding. If we've got NAT in operation, we don't do * this optimisation. */ if (out_dev == in_dev && !(flags & RTCF_NAT)) flags |= RTCF_DOREDIRECT; local_src = DN_FIB_RES_PREFSRC(res); case RTN_BLACKHOLE: case RTN_UNREACHABLE: break; case RTN_LOCAL: flags |= RTCF_LOCAL; fl.fld_src = cb->dst; fl.fld_dst = cb->src; /* Routing tables gave us a gateway */ if (gateway) goto make_route; /* Packet was intra-ethernet, so we know its on-link */ if (cb->rt_flags & DN_RT_F_IE) { gateway = cb->src; flags |= RTCF_DIRECTSRC; goto make_route; } /* Use the default router if there is one */ neigh = neigh_clone(dn_db->router); if (neigh) { gateway = ((struct dn_neigh *)neigh)->addr; goto make_route; } /* Close eyes and pray */ gateway = cb->src; flags |= RTCF_DIRECTSRC; goto make_route; default: goto e_inval; } make_route: rt = dst_alloc(&dn_dst_ops); if (rt == NULL) goto e_nobufs; rt->rt_saddr = fl.fld_src; rt->rt_daddr = fl.fld_dst; rt->rt_gateway = fl.fld_dst; if (gateway) rt->rt_gateway = gateway; rt->rt_local_src = local_src ? local_src : rt->rt_saddr; rt->rt_dst_map = fl.fld_dst; rt->rt_src_map = fl.fld_src; rt->fl.fld_src = cb->src; rt->fl.fld_dst = cb->dst; rt->fl.oif = 0; rt->fl.iif = in_dev->ifindex; rt->fl.fld_fwmark = fl.fld_fwmark; rt->u.dst.flags = DST_HOST; rt->u.dst.neighbour = neigh; rt->u.dst.dev = out_dev; rt->u.dst.lastuse = jiffies; rt->u.dst.output = dn_rt_bug; switch(res.type) { case RTN_UNICAST: rt->u.dst.input = dn_forward; break; case RTN_LOCAL: rt->u.dst.output = dn_output; rt->u.dst.input = dn_nsp_rx; rt->u.dst.dev = in_dev; flags |= RTCF_LOCAL; break; default: case RTN_UNREACHABLE: case RTN_BLACKHOLE: rt->u.dst.input = dn_blackhole; } rt->rt_flags = flags; if (rt->u.dst.dev) dev_hold(rt->u.dst.dev); 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 **)&skb->dst); done: if (neigh) neigh_release(neigh); if (free_res) dn_fib_res_put(&res); dev_put(in_dev); if (out_dev) dev_put(out_dev); out: return err; e_inval: err = -EINVAL; goto done; e_nobufs: err = -ENOBUFS; goto done; e_neighbour: dst_free(&rt->u.dst); goto done; }
int fib6_add(struct fib6_node *root, struct rt6_info *rt) { struct fib6_node *fn; int err = -ENOMEM; fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); if (fn == NULL) return -ENOMEM; #ifdef CONFIG_IPV6_SUBTREES if (rt->rt6i_src.plen) { struct fib6_node *sn; if (fn->subtree == NULL) { struct fib6_node *sfn; /* * Create subtree. * * fn[main tree] * | * sfn[subtree root] * \ * sn[new leaf node] */ /* Create subtree root node */ sfn = node_alloc(); if (sfn == NULL) goto st_failure; sfn->leaf = &ip6_null_entry; atomic_inc(&ip6_null_entry.rt6i_ref); sfn->fn_flags = RTN_ROOT; sfn->fn_sernum = fib6_new_sernum(); /* Now add the first leaf node to new subtree */ sn = fib6_add_1(sfn, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt); if (sn == NULL) { /* If it is failed, discard just allocated root, and then (in st_failure) stale node in main tree. */ node_free(sfn); goto st_failure; } /* Now link new subtree to main tree */ sfn->parent = fn; fn->subtree = sfn; if (fn->leaf == NULL) { fn->leaf = rt; atomic_inc(&rt->rt6i_ref); } } else { sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, sizeof(struct in6_addr), rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt); if (sn == NULL) goto st_failure; } fn = sn; } #endif err = fib6_add_rt2node(fn, rt); if (err == 0) { fib6_start_gc(rt); if (!(rt->rt6i_flags&RTF_CACHE)) fib6_prune_clones(fn, rt); } if (err) dst_free(&rt->u.dst); return err; #ifdef CONFIG_IPV6_SUBTREES /* Subtree creation failed, probably main tree node is orphan. If it is, shot it. */ st_failure: if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT)) fib_repair_tree(fn); dst_free(&rt->u.dst); return err; #endif }
int mpls_add_out_label (struct mpls_out_label_req *out, int seq, int pid) { struct mpls_nhlfe *nhlfe = NULL; unsigned int key = 0; int retval = 0; MPLS_ENTER; BUG_ON(!out); /* Create a new key */ key = mpls_get_out_key(); /* * Check if the NHLFE is already in the tree. * It should not exist. In fact, it is impossible :) */ nhlfe = mpls_get_nhlfe (key); if (unlikely(nhlfe)) { MPLS_DEBUG("Node %u already exists in radix tree\n",key); /* release the refcnt held by mpls_get_nhlfe */ mpls_nhlfe_release (nhlfe); retval = -EEXIST; goto error; } /* * Allocate a new Output Information/Label, */ nhlfe = nhlfe_dst_alloc (key); if (unlikely(!nhlfe)) { retval = -ENOMEM; goto error; } /* Insert into NHLFE tree */ spin_lock_bh (&mpls_nhlfe_lock); if (unlikely(mpls_insert_nhlfe (key,nhlfe))) { spin_unlock_bh (&mpls_nhlfe_lock); nhlfe->u.dst.obsolete = 1; dst_free (&nhlfe->u.dst); goto error; } /* make sure that the dst system doesn't delete this until we're * done with it */ dst_hold(&nhlfe->u.dst); mpls_nhlfe_hold(nhlfe); spin_unlock_bh (&mpls_nhlfe_lock); /* we need to hold a ref to the nhlfe while calling * mpls_nhlfe_event so it can't disappear */ mpls_nhlfe_event(MPLS_CMD_NEWNHLFE, nhlfe, seq, pid); mpls_nhlfe_release(nhlfe); out->mol_label.ml_type = MPLS_LABEL_KEY; out->mol_label.u.ml_key = key; error: MPLS_EXIT; return retval; }
static int __xfrm4_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, struct flowi *fl, struct dst_entry **dst_p) { struct dst_entry *dst, *dst_prev; struct rtable *rt0 = (struct rtable*)(*dst_p); struct rtable *rt = rt0; u32 remote = fl->fl4_dst; u32 local = fl->fl4_src; struct flowi fl_tunnel = { .nl_u = { .ip4_u = { .saddr = local, .daddr = remote, .tos = fl->fl4_tos } } }; int i; int err; int header_len = 0; int trailer_len = 0; dst = dst_prev = NULL; dst_hold(&rt->u.dst); for (i = 0; i < nx; i++) { struct dst_entry *dst1 = dst_alloc(&xfrm4_dst_ops); struct xfrm_dst *xdst; int tunnel = 0; if (unlikely(dst1 == NULL)) { err = -ENOBUFS; dst_release(&rt->u.dst); goto error; } if (!dst) dst = dst1; else { dst_prev->child = dst1; dst1->flags |= DST_NOHASH; dst_clone(dst1); } xdst = (struct xfrm_dst *)dst1; xdst->route = &rt->u.dst; xdst->genid = xfrm[i]->genid; dst1->next = dst_prev; dst_prev = dst1; if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { remote = xfrm[i]->id.daddr.a4; local = xfrm[i]->props.saddr.a4; tunnel = 1; } header_len += xfrm[i]->props.header_len; trailer_len += xfrm[i]->props.trailer_len; if (tunnel) { fl_tunnel.fl4_src = local; fl_tunnel.fl4_dst = remote; err = xfrm_dst_lookup((struct xfrm_dst **)&rt, &fl_tunnel, AF_INET); if (err) goto error; } else dst_hold(&rt->u.dst); } dst_prev->child = &rt->u.dst; dst->path = &rt->u.dst; *dst_p = dst; dst = dst_prev; dst_prev = *dst_p; i = 0; for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; x->u.rt.fl = *fl; dst_prev->xfrm = xfrm[i++]; dst_prev->dev = rt->u.dst.dev; if (rt->u.dst.dev) dev_hold(rt->u.dst.dev); dst_prev->obsolete = -1; dst_prev->flags |= DST_HOST; dst_prev->lastuse = jiffies; dst_prev->header_len = header_len; dst_prev->nfheader_len = 0; dst_prev->trailer_len = trailer_len; memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics)); /* Copy neighbout for reachability confirmation */ dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); dst_prev->input = rt->u.dst.input; dst_prev->output = xfrm4_output; if (rt->peer) atomic_inc(&rt->peer->refcnt); x->u.rt.peer = rt->peer; /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ x->u.rt.rt_flags = rt0->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); x->u.rt.rt_type = rt->rt_type; x->u.rt.rt_src = rt0->rt_src; x->u.rt.rt_dst = rt0->rt_dst; x->u.rt.rt_gateway = rt->rt_gateway; x->u.rt.rt_spec_dst = rt0->rt_spec_dst; x->u.rt.idev = rt0->idev; in_dev_hold(rt0->idev); header_len -= x->u.dst.xfrm->props.header_len; trailer_len -= x->u.dst.xfrm->props.trailer_len; } xfrm_init_pmtu(dst); return 0; error: if (dst) dst_free(dst); return err; }
static int __xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx, struct flowi *fl, struct dst_entry **dst_p) { struct dst_entry *dst, *dst_prev; struct rt6_info *rt0 = (struct rt6_info*)(*dst_p); struct rt6_info *rt = rt0; struct in6_addr *remote = &fl->fl6_dst; struct in6_addr *local = &fl->fl6_src; struct flowi fl_tunnel = { .nl_u = { .ip6_u = { .saddr = *local, .daddr = *remote } } }; int i; int err = 0; int header_len = 0; int nfheader_len = 0; int trailer_len = 0; dst = dst_prev = NULL; dst_hold(&rt->u.dst); for (i = 0; i < nx; i++) { struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops); struct xfrm_dst *xdst; int tunnel = 0; if (unlikely(dst1 == NULL)) { err = -ENOBUFS; dst_release(&rt->u.dst); goto error; } if (!dst) dst = dst1; else { dst_prev->child = dst1; dst1->flags |= DST_NOHASH; dst_clone(dst1); } xdst = (struct xfrm_dst *)dst1; xdst->route = &rt->u.dst; xdst->genid = xfrm[i]->genid; if (rt->rt6i_node) xdst->route_cookie = rt->rt6i_node->fn_sernum; dst1->next = dst_prev; dst_prev = dst1; if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { remote = __xfrm6_bundle_addr_remote(xfrm[i], remote); local = __xfrm6_bundle_addr_local(xfrm[i], local); tunnel = 1; } __xfrm6_bundle_len_inc(&header_len, &nfheader_len, xfrm[i]); trailer_len += xfrm[i]->props.trailer_len; if (tunnel) { ipv6_addr_copy(&fl_tunnel.fl6_dst, remote); ipv6_addr_copy(&fl_tunnel.fl6_src, local); err = xfrm_dst_lookup((struct xfrm_dst **) &rt, &fl_tunnel, AF_INET6); if (err) goto error; } else dst_hold(&rt->u.dst); } dst_prev->child = &rt->u.dst; dst->path = &rt->u.dst; if (rt->rt6i_node) ((struct xfrm_dst *)dst)->path_cookie = rt->rt6i_node->fn_sernum; *dst_p = dst; dst = dst_prev; dst_prev = *dst_p; i = 0; for (; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) { struct xfrm_dst *x = (struct xfrm_dst*)dst_prev; dst_prev->xfrm = xfrm[i++]; dst_prev->dev = rt->u.dst.dev; if (rt->u.dst.dev) dev_hold(rt->u.dst.dev); dst_prev->obsolete = -1; dst_prev->flags |= DST_HOST; dst_prev->lastuse = jiffies; dst_prev->header_len = header_len; dst_prev->nfheader_len = nfheader_len; dst_prev->trailer_len = trailer_len; memcpy(&dst_prev->metrics, &x->route->metrics, sizeof(dst_prev->metrics)); /* Copy neighbour for reachability confirmation */ dst_prev->neighbour = neigh_clone(rt->u.dst.neighbour); dst_prev->input = rt->u.dst.input; dst_prev->output = xfrm6_output; /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ x->u.rt6.rt6i_flags = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL); x->u.rt6.rt6i_metric = rt0->rt6i_metric; x->u.rt6.rt6i_node = rt0->rt6i_node; x->u.rt6.rt6i_gateway = rt0->rt6i_gateway; memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); x->u.rt6.rt6i_dst = rt0->rt6i_dst; x->u.rt6.rt6i_src = rt0->rt6i_src; x->u.rt6.rt6i_idev = rt0->rt6i_idev; in6_dev_hold(rt0->rt6i_idev); __xfrm6_bundle_len_dec(&header_len, &nfheader_len, x->u.dst.xfrm); trailer_len -= x->u.dst.xfrm->props.trailer_len; } xfrm_init_pmtu(dst); return 0; error: if (dst) dst_free(dst); return err; }