/* * Ethernet router hello message received */ int dn_neigh_router_hello(struct sk_buff *skb) { struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data; struct neighbour *neigh; struct dn_neigh *dn; struct dn_dev *dn_db; dn_address src; src = dn_htons(dn_eth2dn(msg->id)); neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1); dn = (struct dn_neigh *)neigh; if (neigh) { write_lock(&neigh->lock); neigh->used = jiffies; dn_db = (struct dn_dev *)neigh->dev->dn_ptr; if (!(neigh->nud_state & NUD_PERMANENT)) { neigh->updated = jiffies; if (neigh->dev->type == ARPHRD_ETHER) memcpy(neigh->ha, &skb->mac.ethernet->h_source, ETH_ALEN); dn->blksize = dn_ntohs(msg->blksize); dn->priority = msg->priority; dn->flags &= ~DN_NDFLAG_P3; switch(msg->iinfo & DN_RT_INFO_TYPE) { case DN_RT_INFO_L1RT: dn->flags &=~DN_NDFLAG_R2; dn->flags |= DN_NDFLAG_R1; break; case DN_RT_INFO_L2RT: dn->flags |= DN_NDFLAG_R2; } } if (!dn_db->router) { dn_db->router = neigh_clone(neigh); } else { if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority) neigh_release(xchg(&dn_db->router, neigh_clone(neigh))); } write_unlock(&neigh->lock); neigh_release(neigh); } kfree_skb(skb); return 0; }
static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, const struct flowi *fl) { struct rt6_info *rt = (struct rt6_info*)xdst->route; xdst->u.dst.dev = dev; dev_hold(dev); xdst->u.rt6.rt6i_idev = in6_dev_get(dev); if (!xdst->u.rt6.rt6i_idev) { dev_put(dev); return -ENODEV; } rt6_transfer_peer(&xdst->u.rt6, rt); /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ xdst->u.rt6.n = neigh_clone(rt->n); xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | RTF_LOCAL); xdst->u.rt6.rt6i_metric = rt->rt6i_metric; xdst->u.rt6.rt6i_node = rt->rt6i_node; if (rt->rt6i_node) xdst->route_cookie = rt->rt6i_node->fn_sernum; xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway; xdst->u.rt6.rt6i_dst = rt->rt6i_dst; xdst->u.rt6.rt6i_src = rt->rt6i_src; return 0; }
/* Create a child dst_entry with locked interface MTU, and attach it to `dst'. This is needed on newer linux kernels and IP_ONLY_INTERCEPTOR builds, where the IP stack fragments packets to path MTU after ssh_interceptor_send. */ static struct dst_entry * interceptor_route_create_child_dst(struct dst_entry *dst) { struct dst_entry *child; /* Allocate a dst_entry and copy relevant fields from dst. */ child = dst_alloc(dst->ops); if (child == NULL) return NULL; child->input = dst->input; child->output = dst->output; /* Child is not added to dst hash, and linux native IPsec is disabled. */ child->flags |= (DST_NOHASH | DST_NOPOLICY | DST_NOXFRM); /* Copy route metrics and lock MTU to interface MTU. */ memcpy(child->metrics, dst->metrics, sizeof(child->metrics)); child->metrics[RTAX_LOCK-1] |= 1 << RTAX_MTU; #ifdef CONFIG_NET_CLS_ROUTE child->tclassid = dst->tclassid; #endif /* CONFIG_NET_CLS_ROUTE */ child->xfrm = NULL; if (dst->hh) { atomic_inc(&dst->hh->hh_refcnt); child->hh = dst->hh; } if (dst->neighbour) { child->neighbour = neigh_clone(dst->neighbour); } if (dst->dev) { dev_hold(dst->dev); child->dev = dst->dev; } SSH_ASSERT(dst->child == NULL); dst->child = dst_clone(child); SSH_DEBUG(SSH_D_MIDOK, ("Allocated child %p dst_entry for dst %p mtu %d", child, dst, dst_mtu(dst))); return child; }
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; }
/* * Handle redirects */ void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr, struct neighbour *neigh, int on_link) { struct rt6_info *rt, *nrt; /* Locate old route to this destination. */ rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1); if (rt == NULL) return; if (neigh->dev != rt->rt6i_dev) goto out; /* Redirect received -> path was valid. Look, redirects are sent only in response to data packets, so that this nexthop apparently is reachable. --ANK */ dst_confirm(&rt->u.dst); /* Duplicate redirect: silently ignore. */ if (neigh == rt->u.dst.neighbour) goto out; /* Current route is on-link; redirect is always invalid. Seems, previous statement is not true. It could be node, which looks for us as on-link (f.e. proxy ndisc) But then router serving it might decide, that we should know truth 8)8) --ANK (980726). */ if (!(rt->rt6i_flags&RTF_GATEWAY)) goto out; /* * RFC 1970 specifies that redirects should only be * accepted if they come from the nexthop to the target. * Due to the way default routers are chosen, this notion * is a bit fuzzy and one might need to check all default * routers. */ if (ipv6_addr_cmp(saddr, &rt->rt6i_gateway)) { if (rt->rt6i_flags & RTF_DEFAULT) { struct rt6_info *rt1; read_lock(&rt6_lock); for (rt1 = ip6_routing_table.leaf; rt1; rt1 = rt1->u.next) { if (!ipv6_addr_cmp(saddr, &rt1->rt6i_gateway)) { dst_hold(&rt1->u.dst); dst_release(&rt->u.dst); read_unlock(&rt6_lock); rt = rt1; goto source_ok; } } read_unlock(&rt6_lock); } if (net_ratelimit()) printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop " "for redirect target\n"); goto out; } source_ok: /* * We have finally decided to accept it. */ nrt = ip6_rt_copy(rt); if (nrt == NULL) goto out; nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; if (on_link) nrt->rt6i_flags &= ~RTF_GATEWAY; ipv6_addr_copy(&nrt->rt6i_dst.addr, dest); nrt->rt6i_dst.plen = 128; nrt->u.dst.flags |= DST_HOST; ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key); nrt->rt6i_nexthop = neigh_clone(neigh); /* Reset pmtu, it may be better */ nrt->u.dst.pmtu = ipv6_get_mtu(neigh->dev); nrt->u.dst.advmss = max_t(unsigned int, nrt->u.dst.pmtu - 60, ip6_rt_min_advmss); if (rt->u.dst.advmss > 65535-20) rt->u.dst.advmss = 65535; nrt->rt6i_hoplimit = ipv6_get_hoplimit(neigh->dev); if (rt6_ins(nrt, NULL)) goto out; if (rt->rt6i_flags&RTF_CACHE) { ip6_del_rt(rt, NULL); return; } out: dst_release(&rt->u.dst); return; }
void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, struct net_device *dev, u32 pmtu) { struct rt6_info *rt, *nrt; if (pmtu < IPV6_MIN_MTU) { if (net_ratelimit()) printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n", pmtu); /* According to RFC1981, the PMTU is set to the IPv6 minimum link MTU if the node receives a Packet Too Big message reporting next-hop MTU that is less than the IPv6 minimum MTU. */ pmtu = IPV6_MIN_MTU; } rt = rt6_lookup(daddr, saddr, dev->ifindex, 0); if (rt == NULL) return; if (pmtu >= rt->u.dst.pmtu) goto out; /* New mtu received -> path was valid. They are sent only in response to data packets, so that this nexthop apparently is reachable. --ANK */ dst_confirm(&rt->u.dst); /* Host route. If it is static, it would be better not to override it, but add new one, so that when cache entry will expire old pmtu would return automatically. */ if (rt->rt6i_flags & RTF_CACHE) { rt->u.dst.pmtu = pmtu; dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires); rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES; goto out; } /* Network route. Two cases are possible: 1. It is connected route. Action: COW 2. It is gatewayed route or NONEXTHOP route. Action: clone it. */ if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) { nrt = rt6_cow(rt, daddr, saddr); if (!nrt->u.dst.error) { nrt->u.dst.pmtu = pmtu; /* According to RFC 1981, detecting PMTU increase shouldn't be happened within 5 mins, the recommended timer is 10 mins. Here this route expiration time is set to ip6_rt_mtu_expires which is 10 mins. After 10 mins the decreased pmtu is expired and detecting PMTU increase will be automatically happened. */ dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires); nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES; dst_release(&nrt->u.dst); } } else { nrt = ip6_rt_copy(rt); if (nrt == NULL) goto out; ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr); nrt->rt6i_dst.plen = 128; nrt->u.dst.flags |= DST_HOST; nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop); dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires); nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES; nrt->u.dst.pmtu = pmtu; rt6_ins(nrt, NULL); } out: dst_release(&rt->u.dst); }
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; }
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; }
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 void CVE_2015_2922_linux3_2_25_ndisc_router_discovery(struct sk_buff *skb) { struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); struct neighbour *neigh = NULL; struct inet6_dev *in6_dev; struct rt6_info *rt = NULL; int lifetime; struct ndisc_options ndopts; int optlen; unsigned int pref = 0; __u8 * opt = (__u8 *)(ra_msg + 1); optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg); if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { ND_PRINTK2(KERN_WARNING "ICMPv6 RA: source address is not link-local.\n"); return; } if (optlen < 0) { ND_PRINTK2(KERN_WARNING "ICMPv6 RA: packet too short\n"); return; } #ifdef CONFIG_IPV6_NDISC_NODETYPE if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) { ND_PRINTK2(KERN_WARNING "ICMPv6 RA: from host or unauthorized router\n"); return; } #endif /* * set the RA_RECV flag in the interface */ in6_dev = __in6_dev_get(skb->dev); if (in6_dev == NULL) { ND_PRINTK0(KERN_ERR "ICMPv6 RA: can't find inet6 device for %s.\n", skb->dev->name); return; } if (!ndisc_parse_options(opt, optlen, &ndopts)) { ND_PRINTK2(KERN_WARNING "ICMP6 RA: invalid ND options\n"); return; } if (!accept_ra(in6_dev)) goto skip_linkparms; #ifdef CONFIG_IPV6_NDISC_NODETYPE /* skip link-specific parameters from interior routers */ if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) goto skip_linkparms; #endif if (in6_dev->if_flags & IF_RS_SENT) { /* * flag that an RA was received after an RS was sent * out on this interface. */ in6_dev->if_flags |= IF_RA_RCVD; } /* * Remember the managed/otherconf flags from most recently * received RA message (RFC 2462) -- yoshfuji */ in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED | IF_RA_OTHERCONF)) | (ra_msg->icmph.icmp6_addrconf_managed ? IF_RA_MANAGED : 0) | (ra_msg->icmph.icmp6_addrconf_other ? IF_RA_OTHERCONF : 0); if (!in6_dev->cnf.accept_ra_defrtr) goto skip_defrtr; if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0)) goto skip_defrtr; lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime); #ifdef CONFIG_IPV6_ROUTER_PREF pref = ra_msg->icmph.icmp6_router_pref; /* 10b is handled as if it were 00b (medium) */ if (pref == ICMPV6_ROUTER_PREF_INVALID || !in6_dev->cnf.accept_ra_rtr_pref) pref = ICMPV6_ROUTER_PREF_MEDIUM; #endif rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev); if (rt) neigh = dst_get_neighbour(&rt->dst); if (rt && lifetime == 0) { neigh_clone(neigh); ip6_del_rt(rt); rt = NULL; } if (rt == NULL && lifetime) { ND_PRINTK3(KERN_DEBUG "ICMPv6 RA: adding default router.\n"); rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref); if (rt == NULL) { ND_PRINTK0(KERN_ERR "ICMPv6 RA: %s() failed to add default route.\n", __func__); return; } neigh = dst_get_neighbour(&rt->dst); if (neigh == NULL) { ND_PRINTK0(KERN_ERR "ICMPv6 RA: %s() got default router without neighbour.\n", __func__); dst_release(&rt->dst); return; } neigh->flags |= NTF_ROUTER; } else if (rt) { rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref); } if (rt) rt->rt6i_expires = jiffies + (HZ * lifetime); if (ra_msg->icmph.icmp6_hop_limit) { in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit; if (rt) dst_metric_set(&rt->dst, RTAX_HOPLIMIT, ra_msg->icmph.icmp6_hop_limit); } skip_defrtr: /* * Update Reachable Time and Retrans Timer */ if (in6_dev->nd_parms) { unsigned long rtime = ntohl(ra_msg->retrans_timer); if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) { rtime = (rtime*HZ)/1000; if (rtime < HZ/10) rtime = HZ/10; in6_dev->nd_parms->retrans_time = rtime; in6_dev->tstamp = jiffies; inet6_ifinfo_notify(RTM_NEWLINK, in6_dev); } rtime = ntohl(ra_msg->reachable_time); if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) { rtime = (rtime*HZ)/1000; if (rtime < HZ/10) rtime = HZ/10; if (rtime != in6_dev->nd_parms->base_reachable_time) { in6_dev->nd_parms->base_reachable_time = rtime; in6_dev->nd_parms->gc_staletime = 3 * rtime; in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime); in6_dev->tstamp = jiffies; inet6_ifinfo_notify(RTM_NEWLINK, in6_dev); } } } skip_linkparms: /* * Process options. */ if (!neigh) neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr, skb->dev, 1); if (neigh) { u8 *lladdr = NULL; if (ndopts.nd_opts_src_lladdr) { lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, skb->dev); if (!lladdr) { ND_PRINTK2(KERN_WARNING "ICMPv6 RA: invalid link-layer address length\n"); goto out; } } neigh_update(neigh, lladdr, NUD_STALE, NEIGH_UPDATE_F_WEAK_OVERRIDE| NEIGH_UPDATE_F_OVERRIDE| NEIGH_UPDATE_F_OVERRIDE_ISROUTER| NEIGH_UPDATE_F_ISROUTER); } if (!accept_ra(in6_dev)) goto out; #ifdef CONFIG_IPV6_ROUTE_INFO if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0)) goto skip_routeinfo; if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) { struct nd_opt_hdr *p; for (p = ndopts.nd_opts_ri; p; p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) { struct route_info *ri = (struct route_info *)p; #ifdef CONFIG_IPV6_NDISC_NODETYPE if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT && ri->prefix_len == 0) continue; #endif if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen) continue; rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3, &ipv6_hdr(skb)->saddr); } } skip_routeinfo: #endif #ifdef CONFIG_IPV6_NDISC_NODETYPE /* skip link-specific ndopts from interior routers */ if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) goto out; #endif if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) { struct nd_opt_hdr *p; for (p = ndopts.nd_opts_pi; p; p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) { addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3); } } if (ndopts.nd_opts_mtu) { __be32 n; u32 mtu; memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu)); mtu = ntohl(n); if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) { ND_PRINTK2(KERN_WARNING "ICMPv6 RA: invalid mtu: %d\n", mtu); } else if (in6_dev->cnf.mtu6 != mtu) { in6_dev->cnf.mtu6 = mtu; if (rt) dst_metric_set(&rt->dst, RTAX_MTU, mtu); rt6_mtu_change(skb->dev, mtu); } } if (ndopts.nd_useropts) { struct nd_opt_hdr *p; for (p = ndopts.nd_useropts; p; p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) { ndisc_ra_useropt(skb, p); } } if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) { ND_PRINTK2(KERN_WARNING "ICMPv6 RA: invalid RA options"); } out: if (rt) dst_release(&rt->dst); else if (neigh) neigh_release(neigh); }
/* Create a child dst_entry with locked interface MTU, and attach it to `dst'. This is needed on newer linux kernels and IP_ONLY_INTERCEPTOR builds, where the IP stack fragments packets to path MTU after ssh_interceptor_send. */ static struct dst_entry * interceptor_route_create_child_dst(struct dst_entry *dst, Boolean ipv6) { struct dst_entry *child; #ifdef LINUX_HAS_DST_COPY_METRICS SshUInt32 set; struct rt6_info *rt6; struct rtable *rt; #endif /* LINUX_HAS_DST_COPY_METRICS */ /* Allocate a dst_entry and copy relevant fields from dst. */ child = SSH_DST_ALLOC(dst); if (child == NULL) return NULL; child->input = dst->input; child->output = dst->output; /* Child is not added to dst hash, and linux native IPsec is disabled. */ child->flags |= (DST_NOHASH | DST_NOPOLICY | DST_NOXFRM); /* Copy route metrics and lock MTU to interface MTU. */ #ifdef LINUX_HAS_DST_COPY_METRICS if (ipv6 == TRUE) { rt6 = (struct rt6_info *)child; memset(&rt6->rt6i_table, 0, sizeof(*rt6) - sizeof(struct dst_entry)); } else { rt = (struct rtable *)child; memset(&SSH_RTABLE_FIRST_MEMBER(rt), 0, sizeof(*rt) - sizeof(struct dst_entry)); } dst_copy_metrics(child, dst); set = dst_metric(child, RTAX_LOCK); set |= 1 << RTAX_MTU; dst_metric_set(child, RTAX_LOCK, set); #else /* LINUX_HAS_DST_COPY_METRICS */ memcpy(child->metrics, dst->metrics, sizeof(child->metrics)); child->metrics[RTAX_LOCK-1] |= 1 << RTAX_MTU; #endif /* LINUX_HAS_DST_COPY_METRICS */ #ifdef CONFIG_NET_CLS_ROUTE child->tclassid = dst->tclassid; #endif /* CONFIG_NET_CLS_ROUTE */ #ifdef CONFIG_XFRM child->xfrm = NULL; #endif /* CONFIG_XFRM */ #ifdef LINUX_HAS_HH_CACHE if (dst->hh) { atomic_inc(&dst->hh->hh_refcnt); child->hh = dst->hh; } #endif /* LINUX_HAS_HH_CACHE */ #ifdef LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS if (dst_get_neighbour(dst) != NULL) dst_set_neighbour(child, neigh_clone(dst_get_neighbour(dst))); #else /* LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS */ if (dst->neighbour != NULL) child->neighbour = neigh_clone(dst->neighbour); #endif /* LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS */ if (dst->dev) { dev_hold(dst->dev); child->dev = dst->dev; } SSH_ASSERT(dst->child == NULL); dst->child = dst_clone(child); SSH_DEBUG(SSH_D_MIDOK, ("Allocated child %p dst_entry for dst %p mtu %d", child, dst, dst_mtu(dst))); return child; }