Ejemplo n.º 1
0
/* Route the packet according to the routing keys specified in
 * route_info. Keys are :
 *  - ifindex : 
 *      0 if no oif preferred, 
 *      otherwise set to the index of the desired oif
 *  - route_info->gw :
 *      0 if no gateway specified,
 *      otherwise set to the next host to which the pkt must be routed
 * If success, skb->dev is the output device to which the packet must 
 * be sent and skb->dst is not NULL
 *
 * RETURN:  1 if the packet was succesfully routed to the 
 *            destination desired
 *          0 if the kernel routing table could not route the packet
 *            according to the keys specified
 */
static int 
route6(struct sk_buff *skb,
       unsigned int ifindex,
       const struct ip6t_route_target_info *route_info)
{
	struct rt6_info *rt = NULL;
	struct ipv6hdr *ipv6h = skb->nh.ipv6h;
	struct in6_addr *gw = (struct in6_addr*)&route_info->gw;

	DEBUGP("ip6t_ROUTE: called with: ");
	DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr));
	DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw));
	DEBUGP("OUT=%s\n", route_info->oif);
	
	if (ipv6_addr_any(gw))
		rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1);
	else
		rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1);

	if (!rt)
		goto no_route;

	DEBUGP("ip6t_ROUTE: routing gives: ");
	DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr));
	DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway));
	DEBUGP("OUT=%s\n", rt->rt6i_dev->name);

	if (ifindex && rt->rt6i_dev->ifindex!=ifindex)
		goto wrong_route;
	
	if (!rt->rt6i_nexthop) {
		DEBUGP("ip6t_ROUTE: discovering neighbour\n");
		rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr);
	}

	/* Drop old route. */
	dst_release(skb->dst);
	skb->dst = &rt->u.dst;
	skb->dev = rt->rt6i_dev;
	return 1;

 wrong_route:
	dst_release(&rt->u.dst);
 no_route:
	if (!net_ratelimit())
		return 0;

	printk("ip6t_ROUTE: no explicit route found ");
	if (ifindex)
		printk("via interface %s ", route_info->oif);
	if (!ipv6_addr_any(gw))
		printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw));
	printk("\n");
	return 0;
}
Ejemplo n.º 2
0
static bool match_type6(struct net *net, const struct net_device *dev,
				const struct in6_addr *addr, u16 mask)
{
	int addr_type = ipv6_addr_type(addr);

	if ((mask & XT_ADDRTYPE_MULTICAST) &&
	    !(addr_type & IPV6_ADDR_MULTICAST))
		return false;
	if ((mask & XT_ADDRTYPE_UNICAST) && !(addr_type & IPV6_ADDR_UNICAST))
		return false;
	if ((mask & XT_ADDRTYPE_UNSPEC) && addr_type != IPV6_ADDR_ANY)
		return false;

	if ((XT_ADDRTYPE_LOCAL | XT_ADDRTYPE_ANYCAST |
	     XT_ADDRTYPE_UNREACHABLE) & mask) {
		struct rt6_info *rt;
		u32 type;
		int ifindex = dev ? dev->ifindex : 0;

		rt = rt6_lookup(net, addr, NULL, ifindex, !!dev);

		type = xt_addrtype_rt6_to_type(rt);

		dst_release(&rt->dst);
		return !!(mask & type);
	}
	return true;
}
Ejemplo n.º 3
0
int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
{
	struct net_device *dev = NULL;
	struct ipv6_mc_socklist *mc_lst;
	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
	int err;

	if (!(ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST))
		return -EINVAL;

	mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL);

	if (mc_lst == NULL)
		return -ENOMEM;

	mc_lst->next = NULL;
	memcpy(&mc_lst->addr, addr, sizeof(struct in6_addr));
	mc_lst->ifindex = ifindex;

	if (ifindex == 0) {
		struct rt6_info *rt;
		rt = rt6_lookup(addr, NULL, 0, 0);
		if (rt) {
			dev = rt->rt6i_dev;
			dev_hold(dev);
			dst_release(&rt->u.dst);
		}
	} else
		dev = dev_get_by_index(ifindex);

	if (dev == NULL) {
		sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
		return -ENODEV;
	}

	/*
	 *	now add/increase the group membership on the device
	 */

	err = ipv6_dev_mc_inc(dev, addr);

	if (err) {
		sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
		dev_put(dev);
		return err;
	}

	write_lock_bh(&ipv6_sk_mc_lock);
	mc_lst->next = np->ipv6_mc_list;
	np->ipv6_mc_list = mc_lst;
	write_unlock_bh(&ipv6_sk_mc_lock);

	dev_put(dev);

	return 0;
}
Ejemplo n.º 4
0
static void ip6_tnl_link_config(struct ip6_tnl *t)
{
	struct net_device *dev = t->dev;
	struct ip6_tnl_parm *p = &t->parms;
	struct flowi *fl = &t->fl;

	memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr));
	memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr));

	/* Set up flowi template */
	ipv6_addr_copy(&fl->fl6_src, &p->laddr);
	ipv6_addr_copy(&fl->fl6_dst, &p->raddr);
	fl->oif = p->link;
	fl->fl6_flowlabel = 0;

	if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS))
		fl->fl6_flowlabel |= IPV6_TCLASS_MASK & p->flowinfo;
	if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL))
		fl->fl6_flowlabel |= IPV6_FLOWLABEL_MASK & p->flowinfo;

	ip6_tnl_set_cap(t);

	if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV)
		dev->flags |= IFF_POINTOPOINT;
	else
		dev->flags &= ~IFF_POINTOPOINT;

	dev->iflink = p->link;

	if (p->flags & IP6_TNL_F_CAP_XMIT) {
		int strict = (ipv6_addr_type(&p->raddr) &
			      (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));

		struct rt6_info *rt = rt6_lookup(dev_net(dev),
						 &p->raddr, &p->laddr,
						 p->link, strict);

		if (rt == NULL)
			return;

		if (rt->rt6i_dev) {
			dev->hard_header_len = rt->rt6i_dev->hard_header_len +
				sizeof (struct ipv6hdr);

			dev->mtu = rt->rt6i_dev->mtu - sizeof (struct ipv6hdr);

			if (dev->mtu < IPV6_MIN_MTU)
				dev->mtu = IPV6_MIN_MTU;
		}
		dst_release(&rt->u.dst);
	}
}
Ejemplo n.º 5
0
int ip6_rt_addr_del(struct in6_addr *addr, struct net_device *dev)
{
	struct rt6_info *rt;
	int err = -ENOENT;

	rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, 1);
	if (rt) {
		if (rt->rt6i_dst.plen == 128)
			err = ip6_del_rt(rt, NULL);
		else
			dst_release(&rt->u.dst);
	}

	return err;
}
Ejemplo n.º 6
0
static int
ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
	   int type, int code, int offset, __be32 info)
{
	int rel_msg = 0;
	int rel_type = type;
	int rel_code = code;
	__u32 rel_info = ntohl(info);
	int err;

	err = ip6_tnl_err(skb, IPPROTO_IPV6, opt, &rel_type, &rel_code,
			  &rel_msg, &rel_info, offset);
	if (err < 0)
		return err;

	if (rel_msg && pskb_may_pull(skb, offset + sizeof(struct ipv6hdr))) {
		struct rt6_info *rt;
		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);

		if (!skb2)
			return 0;

		dst_release(skb2->dst);
		skb2->dst = NULL;
		skb_pull(skb2, offset);
		skb_reset_network_header(skb2);

		/* Try to guess incoming interface */
		rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb2)->saddr,
				NULL, 0, 0);

		if (rt && rt->rt6i_dev)
			skb2->dev = rt->rt6i_dev;

		icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev);

		if (rt)
			dst_release(&rt->u.dst);

		kfree_skb(skb2);
	}

	return 0;
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
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);
}
Ejemplo n.º 9
0
void kni_net_process_rx_packet(struct sk_buff *skb,
                               struct net_device *dev,
                               struct rw_kni_mbuf_metadata *meta_data)
{
  struct kni_dev *kni = netdev_priv(dev);
  
  skb->dev = dev;
  
  if (kni->no_pci){
    skb_reset_mac_header(skb);
    skb->protocol = htons(RW_KNI_VF_GET_MDATA_PAYLOAD(meta_data));
  } else {
    skb->protocol = eth_type_trans(skb, dev);
  }
  skb->ip_summed = CHECKSUM_UNNECESSARY;
  /*Eth-type trans would have populated the packet-type. Store
    the old packet-type and populate the new packet-type depending
    on the mbuf flags*/
  rw_fpath_kni_set_skb_packet_type(meta_data, skb);

  if (RW_KNI_VF_VALID_MDATA_NH_POLICY(meta_data)){
    int route_lookup;
    BUG_ON(RW_KNI_VF_VALID_MDATA_ENCAP(meta_data) == 0);
    switch(RW_KNI_VF_GET_MDATA_ENCAP(meta_data)){
      default:
        break;
      case 1: //AKKI fix this later
        {
          uint32_t daddr;
          
          memcpy(&daddr, RW_KNI_VF_GET_MDATA_NH_POLICY(meta_data), 4);
          route_lookup = ip_route_input_noref(skb, ntohl(daddr),
                                              ntohl(daddr), 0, dev);
          if (route_lookup){
            kni->rx_drop_noroute++;
          }else{
            struct neighbour *neigh;
            struct dst_entry *dst = dst_clone(skb_dst(skb));
            struct net_device *neighdev;
            skb_dst_drop(skb);
            neighdev = dst->dev;
            
            
            if (likely(neighdev)){
              rcu_read_lock_bh();
              neigh = __neigh_lookup(&arp_tbl, &daddr, neighdev, 1);
              if (likely(!neigh)){
                __neigh_event_send(neigh, NULL);
              }
              rcu_read_unlock_bh();
              neigh_release(neigh);
            }
            dst_release(dst);
          }
        }
        break;
      case 2:
        {
          struct neighbour *neigh = NULL;
          struct dst_entry *dst = NULL;
          int i;
          uint32_t *v6addr;
          struct flowi6 fl6;
          struct rt6_info *rt;
          struct net_device *neighdev;
          
          v6addr = (uint32_t*)RW_KNI_VF_GET_MDATA_NH_POLICY(meta_data);
          for (i = 0; i < 4; i++){
            fl6.daddr.s6_addr32[i] = htonl(v6addr[i]);
          }
          rt = rt6_lookup(dev_net(dev), &fl6.daddr,
                          NULL, 0, 0);
          if (!rt){
            kni->rx_drop_noroute++;
          }else{
            dst = &rt->dst;
            neighdev = dst->dev;
            if (likely(neighdev)){
              rcu_read_lock_bh();
              neigh = __neigh_lookup(ipv6_stub->nd_tbl,
                                     &fl6.daddr.s6_addr32[0],
                                     neighdev, 1);
              if (likely(!neigh)){
                __neigh_event_send(neigh, NULL);
              }
              rcu_read_unlock_bh();
              neigh_release(neigh);
            }
            dst_release(dst);
          }
        }
        break;
    }
  }

  /* Call netif interface */
  netif_rx(skb);
  
  /* Update statistics */
  kni->stats.rx_packets++;
}
Ejemplo n.º 10
0
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
{
	struct prefix_info *pinfo;
	struct rt6_info *rt;
	__u32 valid_lft;
	__u32 prefered_lft;
	int addr_type;
	unsigned long rt_expires;
	struct inet6_dev *in6_dev;

	pinfo = (struct prefix_info *) opt;
	
	if (len < sizeof(struct prefix_info)) {
		ADBG(("addrconf: prefix option too short\n"));
		return;
	}
	
	/*
	 *	Validation checks ([ADDRCONF], page 19)
	 */

	addr_type = ipv6_addr_type(&pinfo->prefix);

	if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL))
		return;

	valid_lft = ntohl(pinfo->valid);
	prefered_lft = ntohl(pinfo->prefered);

	if (prefered_lft > valid_lft) {
		if (net_ratelimit())
			printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n");
		return;
	}

	in6_dev = in6_dev_get(dev);

	if (in6_dev == NULL) {
		if (net_ratelimit())
			printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
		return;
	}

	/*
	 *	Two things going on here:
	 *	1) Add routes for on-link prefixes
	 *	2) Configure prefixes with the auto flag set
	 */

	/* Avoid arithemtic overflow. Really, we could
	   save rt_expires in seconds, likely valid_lft,
	   but it would require division in fib gc, that it
	   not good.
	 */
	if (valid_lft >= 0x7FFFFFFF/HZ)
		rt_expires = 0;
	else
		rt_expires = jiffies + valid_lft * HZ;

	rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1);

	if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
		if (rt->rt6i_flags&RTF_EXPIRES) {
			if (pinfo->onlink == 0 || valid_lft == 0) {
				ip6_del_rt(rt);
				rt = NULL;
			} else {
				rt->rt6i_expires = rt_expires;
			}
		}
	} else if (pinfo->onlink && valid_lft) {
		addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
				      dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES);
	}
	if (rt)
		dst_release(&rt->u.dst);

	/* Try to figure out our local address for this prefix */

	if (pinfo->autoconf && in6_dev->cnf.autoconf) {
		struct inet6_ifaddr * ifp;
		struct in6_addr addr;
		int plen;

		plen = pinfo->prefix_len >> 3;

		if (pinfo->prefix_len == 64) {
			memcpy(&addr, &pinfo->prefix, 8);
			if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
			    ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
				in6_dev_put(in6_dev);
				return;
			}
			goto ok;
		}
		if (net_ratelimit())
			printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n",
			       pinfo->prefix_len);
		in6_dev_put(in6_dev);
		return;

ok:

		ifp = ipv6_get_ifaddr(&addr, dev);

		if (ifp == NULL && valid_lft) {
			ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
					    addr_type&IPV6_ADDR_SCOPE_MASK, 0);

			if (ifp == NULL) {
				in6_dev_put(in6_dev);
				return;
			}

			addrconf_dad_start(ifp);
		}

		if (ifp && valid_lft == 0) {
			ipv6_del_addr(ifp);
			ifp = NULL;
		}

		if (ifp) {
			int flags;

			spin_lock(&ifp->lock);
			ifp->valid_lft = valid_lft;
			ifp->prefered_lft = prefered_lft;
			ifp->tstamp = jiffies;
			flags = ifp->flags;
			ifp->flags &= ~IFA_F_DEPRECATED;
			spin_unlock(&ifp->lock);

			if (!(flags&IFA_F_TENTATIVE))
				ipv6_ifa_notify((flags&IFA_F_DEPRECATED) ?
						0 : RTM_NEWADDR, ifp);
			in6_ifa_put(ifp);
		}
	}
	in6_dev_put(in6_dev);
}
static int
ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
	   int type, int code, int offset, __be32 info)
{
	struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data;
	struct ip6_tnl *t;
	int rel_msg = 0;
	int rel_type = ICMPV6_DEST_UNREACH;
	int rel_code = ICMPV6_ADDR_UNREACH;
	__u32 rel_info = 0;
	__u16 len;
	int err = -ENOENT;

	/* If the packet doesn't contain the original IPv6 header we are
	   in trouble since we might need the source address for further
	   processing of the error. */

	read_lock(&ip6ip6_lock);
	if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL)
		goto out;

	err = 0;

	switch (type) {
		__u32 teli;
		struct ipv6_tlv_tnl_enc_lim *tel;
		__u32 mtu;
	case ICMPV6_DEST_UNREACH:
		if (net_ratelimit())
			printk(KERN_WARNING
			       "%s: Path to destination invalid "
			       "or inactive!\n", t->parms.name);
		rel_msg = 1;
		break;
	case ICMPV6_TIME_EXCEED:
		if (code == ICMPV6_EXC_HOPLIMIT) {
			if (net_ratelimit())
				printk(KERN_WARNING
				       "%s: Too small hop limit or "
				       "routing loop in tunnel!\n",
				       t->parms.name);
			rel_msg = 1;
		}
		break;
	case ICMPV6_PARAMPROB:
		teli = 0;
		if (code == ICMPV6_HDR_FIELD)
			teli = parse_tlv_tnl_enc_lim(skb, skb->data);

		if (teli && teli == ntohl(info) - 2) {
			tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli];
			if (tel->encap_limit == 0) {
				if (net_ratelimit())
					printk(KERN_WARNING
					       "%s: Too small encapsulation "
					       "limit or routing loop in "
					       "tunnel!\n", t->parms.name);
				rel_msg = 1;
			}
		} else if (net_ratelimit()) {
			printk(KERN_WARNING
			       "%s: Recipient unable to parse tunneled "
			       "packet!\n ", t->parms.name);
		}
		break;
	case ICMPV6_PKT_TOOBIG:
		mtu = ntohl(info) - offset;
		if (mtu < IPV6_MIN_MTU)
			mtu = IPV6_MIN_MTU;
		t->dev->mtu = mtu;

		if ((len = sizeof (*ipv6h) + ntohs(ipv6h->payload_len)) > mtu) {
			rel_type = ICMPV6_PKT_TOOBIG;
			rel_code = 0;
			rel_info = mtu;
			rel_msg = 1;
		}
		break;
	}
	if (rel_msg &&  pskb_may_pull(skb, offset + sizeof (*ipv6h))) {
		struct rt6_info *rt;
		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);

		if (!skb2)
			goto out;

		dst_release(skb2->dst);
		skb2->dst = NULL;
		skb_pull(skb2, offset);
		skb2->nh.raw = skb2->data;

		/* Try to guess incoming interface */
		rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0);

		if (rt && rt->rt6i_dev)
			skb2->dev = rt->rt6i_dev;

		icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev);

		if (rt)
			dst_release(&rt->u.dst);

		kfree_skb(skb2);
	}
out:
	read_unlock(&ip6ip6_lock);
	return err;
}
Ejemplo n.º 12
0
static int niit_xmit(struct sk_buff *skb, struct net_device *dev) {
	struct niit_tunnel *tunnel = (struct niit_tunnel *) netdev_priv(tunnel4_dev);
	struct ethhdr *ethhead;
	struct iphdr *iph4;
	struct ipv6hdr *iph6;
	struct net_device_stats *stats;
	struct rt6_info *rt6; /* Route to the other host */
	struct net_device *tdev; /* Device to other host */
	__u8 nexthdr; /* IPv6 next header */
	u32 delta; /* calc space inside skb */
	unsigned int max_headroom; /* The extra header space needed */
	struct in6_addr s6addr;
	struct in6_addr d6addr;

	/*
	 * all IPv4 (includes icmp) will be encapsulated.
	 * IPv6 ICMPs for IPv4 encapsulated data should be translated
	 *
	 */
	if (skb->protocol == htons(ETH_P_IP)) {
		stats = &tunnel4_dev->stats;
		PDEBUG("niit: skb->proto = iph4 \n");
		iph4 = ip_hdr(skb);

		s6addr.in6_u.u6_addr32[0] = tunnel->ipv6prefix_1;
		s6addr.in6_u.u6_addr32[1] = tunnel->ipv6prefix_2;
		s6addr.in6_u.u6_addr32[2] = tunnel->ipv6prefix_3;
		s6addr.in6_u.u6_addr32[3] = iph4->saddr;

		d6addr.in6_u.u6_addr32[0] = tunnel->ipv6prefix_1;
		d6addr.in6_u.u6_addr32[1] = tunnel->ipv6prefix_2;
		d6addr.in6_u.u6_addr32[2] = tunnel->ipv6prefix_3;
		d6addr.in6_u.u6_addr32[3] = iph4->daddr;

		PDEBUG("niit: ipv4: saddr: %x%x%x%x \n niit: ipv4: daddr %x%x%x%x \n",
		 s6addr.in6_u.u6_addr32[0], s6addr.in6_u.u6_addr32[1],
		 s6addr.in6_u.u6_addr32[2], s6addr.in6_u.u6_addr32[3],
		 d6addr.in6_u.u6_addr32[0], d6addr.in6_u.u6_addr32[1],
		 d6addr.in6_u.u6_addr32[2], d6addr.in6_u.u6_addr32[3]);

		if ((rt6 = rt6_lookup(dev_net(tunnel4_dev), &d6addr, &s6addr, (tunnel4_dev)->iflink, 0)) == NULL) {
			stats->tx_carrier_errors++;
			goto tx_error_icmp;
		}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
tdev = rt6->u.dst.dev;
dst_release(&rt6->u.dst);
#else
tdev = rt6->dst.dev;
dst_release(&rt6->dst);
#endif

		if (tdev == dev) {
			PDEBUG("niit: recursion detected todev = dev \n");
			stats->collisions++;
			goto tx_error;
		}
		/* old MTU check */

		/*
		 * Resize the buffer to push our ipv6 head into
		 */
		max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct ipv6hdr);

		if (skb_headroom(skb) < max_headroom || skb_shared(skb) || (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
			struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
			if (!new_skb) {
				stats->tx_dropped++;
				dev_kfree_skb(skb);
				tunnel->recursion--;
				return 0;
			}
			if (skb->sk)
				skb_set_owner_w(new_skb, skb->sk);
			dev_kfree_skb(skb);
			skb = new_skb;
			iph4 = ip_hdr(skb);
		}

		delta = skb_network_header(skb) - skb->data;

		/* make our skb space best fit */
		if (delta < sizeof(struct ipv6hdr)) {
			iph6 = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr) - delta);
			PDEBUG("niit: iph6 < 0 skb->len %x \n", skb->len);
		}
		else if (delta > sizeof(struct ipv6hdr)) {
			iph6 = (struct ipv6hdr*) skb_pull(skb, delta - sizeof(struct ipv6hdr));
			PDEBUG("niit: iph6 > 0 skb->len %x \n", skb->len);
		}
		else {
			iph6 = (struct ipv6hdr*) skb->data;
			PDEBUG("niit: iph6 = 0 skb->len %x \n", skb->len);
		}
		/* how the package should look like :
		 * skb->network_header =  iph6
		 * skb->transport_header = iph4; 
                 */
		skb->transport_header = skb->network_header; /* we say skb->transport_header = iph4; */
		skb_reset_network_header(skb); /* now -> we reset the network header to skb->data which is our ipv6 paket */
		skb_reset_mac_header(skb);
		skb->mac_header = skb->network_header - sizeof(struct ethhdr);
		skb->mac_len = sizeof(struct ethhdr);

		/* add a dummy ethhdr to use correct interface linktype */
		ethhead = eth_hdr(skb);
		memcpy(ethhead->h_dest, tunnel4_dev->dev_addr, ETH_ALEN);
		memcpy(ethhead->h_source, tunnel4_dev->dev_addr, ETH_ALEN);
		ethhead->h_proto = htons(ETH_P_IPV6);

		/* prepare to send it again */
		IPCB(skb)->flags = 0;
		skb->protocol = htons(ETH_P_IPV6);
		skb->pkt_type = PACKET_HOST;
		skb->dev = tunnel4_dev;
		skb_dst_drop(skb);

		/* install v6 header */
		memset(iph6, 0, sizeof(struct ipv6hdr));
		iph6->version = 6;
		iph6->payload_len = iph4->tot_len;
		iph6->hop_limit = iph4->ttl;
		iph6->nexthdr = IPPROTO_IPIP;
		memcpy(&(iph6->saddr), &s6addr, sizeof(struct in6_addr));
		memcpy(&(iph6->daddr), &d6addr, sizeof(struct in6_addr));

		nf_reset(skb);
		netif_rx(skb);
		tunnel->recursion--;
	}
	else if (skb->protocol == htons(ETH_P_IPV6)) {
		/* got a ipv6-package and need to translate it back to ipv4 */
		__be32 s4addr;
		__be32 d4addr;
		__u8 hoplimit;
		stats = &tunnel6_dev->stats;
		PDEBUG("niit: skb->proto = iph6 \n");

		iph6 = ipv6_hdr(skb);
		if (!iph6) {
			PDEBUG("niit: cant find iph6 \n");
			goto tx_error;
		}

		/* IPv6 to IPv4 */
		hoplimit = iph6->hop_limit;
		/* check against our prefix which all packages must have */
		if (iph6->daddr.s6_addr32[0] != tunnel->ipv6prefix_1 || iph6->daddr.s6_addr32[1] != tunnel->ipv6prefix_2
				|| iph6->daddr.s6_addr32[2] != tunnel->ipv6prefix_3) {
			PDEBUG("niit: xmit ipv6(): Dst addr haven't our previx addr: %x%x%x%x, packet dropped.\n",
					iph6->daddr.s6_addr32[0], iph6->daddr.s6_addr32[1],
					iph6->daddr.s6_addr32[2], iph6->daddr.s6_addr32[3]);
			goto tx_error;
		}

		s4addr = iph6->saddr.s6_addr32[3];
		d4addr = iph6->daddr.s6_addr32[3];
		nexthdr = iph6->nexthdr;
		/* TODO nexthdr handle */
		/*
		 while(nexthdr != IPPROTO_IPIP) {

		 }
		 */
		if(nexthdr != IPPROTO_IPIP) {
			PDEBUG("niit: cant handle hdrtype : %x.\n", nexthdr);
			goto tx_error;
		}

		iph4 = ipip_hdr(skb);

		/* TODO: fix the check for a valid route */
		/*	   {
		 struct flowi fl = { .nl_u = { .ip4_u =
		 { .daddr = d4addr,
		 .saddr = s4addr,
		 .tos = RT_TOS(iph4->tos) } },
		 .oif = tunnel_dev->iflink,
		 .proto = iph4->protocol };

		 if (ip_route_output_key(dev_net(dev), &rt, &fl)) {
		 PDEBUG("niit : ip route not found \n");
		 stats->tx_carrier_errors++;
		 goto tx_error_icmp;
		 }
		 }
		 tdev = rt->u.dst.dev;
		 if (tdev == tunnel_dev) {
		 PDEBUG("niit : tdev == tunnel_dev \n");
		 ip_rt_put(rt);
		 stats->collisions++;
		 goto tx_error;
		 }

		 if (iph4->frag_off)
		 mtu = dst_mtu(&rt->u.dst) - sizeof(struct iphdr);
		 else
		 mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu;

		 if (mtu < 68) {
		 PDEBUG("niit : mtu < 68 \n");
		 stats->collisions++;
		 ip_rt_put(rt);
		 goto tx_error;
		 }
		 if (iph4->daddr && skb_dst(skb))
		 skb_dst(skb)->ops->update_pmtu(skb_dst(skb), mtu);
		 */
		/*
		 if (skb->len > mtu) {
		 icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
		 ip_rt_put(rt);
		 goto tx_error;
		 }
		 */

		/*
		 *  check if we can reuse our skb_buff
		 */

		if (skb_shared(skb) || (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
			struct sk_buff *new_skb = skb_realloc_headroom(skb, skb_headroom(skb));
			if (!new_skb) {
				stats->tx_dropped++;
				dev_kfree_skb(skb);
				tunnel->recursion--;
				return 0;
			}
			if (skb->sk)
				skb_set_owner_w(new_skb, skb->sk);
			dev_kfree_skb(skb);
			skb = new_skb;
			iph6 = ipv6_hdr(skb);
			iph4 = ipip_hdr(skb);
		}

		delta = skb_transport_header(skb) - skb->data;
		skb_pull(skb, delta);

		/* our paket come with ... */
		/* skb->network_header iph6; */
		/* skb->transport_header iph4; */
		skb->network_header = skb->transport_header; /* we say skb->network_header = iph4; */
		skb_set_transport_header(skb, sizeof(struct iphdr));
		skb->mac_header = skb->network_header - sizeof(struct ethhdr);
		skb->mac_len = sizeof(struct ethhdr);

		/* add a dummy ethhdr to use correct interface linktype */
		ethhead = eth_hdr(skb);
		memcpy(ethhead->h_dest, tunnel6_dev->dev_addr, ETH_ALEN);
		memcpy(ethhead->h_source, tunnel6_dev->dev_addr, ETH_ALEN);
		ethhead->h_proto = htons(ETH_P_IP);

		/* prepare to send it again */
		IPCB(skb)->flags = 0;
		skb->protocol = htons(ETH_P_IP);
		skb->pkt_type = PACKET_HOST;
		skb->dev = tunnel6_dev;
		skb_dst_drop(skb);

		/* TODO: set iph4->ttl = hoplimit and recalc the checksum ! */

		/* sending */
		nf_reset(skb);
		netif_rx(skb);
		tunnel->recursion--;
	}
	else {
		stats = &tunnel6_dev->stats;
		PDEBUG("niit: unknown direction %x \n", skb->protocol);
		goto tx_error;
		/* drop */
	}
	return 0;

  tx_error_icmp: 
	dst_link_failure(skb);
	PDEBUG("niit: tx_error_icmp\n");
  tx_error:
	PDEBUG("niit: tx_error\n");
	stats->tx_errors++;
	dev_kfree_skb(skb);
	tunnel->recursion--;
	return 0;
}
Ejemplo n.º 13
0
int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct net_device *dev = NULL;
	struct inet6_dev *idev;
	struct ipv6_ac_socklist *pac;
	struct net *net = sock_net(sk);
	int	ishost = !net->ipv6.devconf_all->forwarding;
	int	err = 0;

	ASSERT_RTNL();

	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
		return -EPERM;
	if (ipv6_addr_is_multicast(addr))
		return -EINVAL;
	if (ipv6_chk_addr(net, addr, NULL, 0))
		return -EINVAL;

	pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
	if (!pac)
		return -ENOMEM;
	pac->acl_next = NULL;
	pac->acl_addr = *addr;

	if (ifindex == 0) {
		struct rt6_info *rt;

		rt = rt6_lookup(net, addr, NULL, 0, 0);
		if (rt) {
			dev = rt->dst.dev;
			ip6_rt_put(rt);
		} else if (ishost) {
			err = -EADDRNOTAVAIL;
			goto error;
		} else {
			/* router, no matching interface: just pick one */
			dev = __dev_get_by_flags(net, IFF_UP,
						 IFF_UP | IFF_LOOPBACK);
		}
	} else
		dev = __dev_get_by_index(net, ifindex);

	if (!dev) {
		err = -ENODEV;
		goto error;
	}

	idev = __in6_dev_get(dev);
	if (!idev) {
		if (ifindex)
			err = -ENODEV;
		else
			err = -EADDRNOTAVAIL;
		goto error;
	}
	/* reset ishost, now that we have a specific device */
	ishost = !idev->cnf.forwarding;

	pac->acl_ifindex = dev->ifindex;

	/* XXX
	 * For hosts, allow link-local or matching prefix anycasts.
	 * This obviates the need for propagating anycast routes while
	 * still allowing some non-router anycast participation.
	 */
	if (!ipv6_chk_prefix(addr, dev)) {
		if (ishost)
			err = -EADDRNOTAVAIL;
		if (err)
			goto error;
	}

	err = __ipv6_dev_ac_inc(idev, addr);
	if (!err) {
		pac->acl_next = np->ipv6_ac_list;
		np->ipv6_ac_list = pac;
		pac = NULL;
	}

error:
	if (pac)
		sock_kfree_s(sk, pac, sizeof(*pac));
	return err;
}
Ejemplo n.º 14
0
void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
{
    struct prefix_info *pinfo;
    struct rt6_info *rt;
    __u32 valid_lft;
    __u32 prefered_lft;
    int addr_type;
    unsigned long rt_expires;
    struct inet6_dev *in6_dev = ipv6_get_idev(dev);

    if (in6_dev == NULL) {
        printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
        return;
    }

    pinfo = (struct prefix_info *) opt;

    if (len < sizeof(struct prefix_info)) {
        ADBG(("addrconf: prefix option too short\n"));
        return;
    }

    /*
     *	Validation checks ([ADDRCONF], page 19)
     */

    addr_type = ipv6_addr_type(&pinfo->prefix);

    if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL))
        return;

    valid_lft = ntohl(pinfo->valid);
    prefered_lft = ntohl(pinfo->prefered);

    if (prefered_lft > valid_lft) {
        printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n");
        return;
    }

    /*
     *	If we where using an "all destinations on link" route
     *	delete it
     */

    rt6_purge_dflt_routers(RTF_ALLONLINK);

    /*
     *	Two things going on here:
     *	1) Add routes for on-link prefixes
     *	2) Configure prefixes with the auto flag set
     */

    /* Avoid arithemtic overflow. Really, we could
       save rt_expires in seconds, likely valid_lft,
       but it would require division in fib gc, that it
       not good.
     */
    if (valid_lft >= 0x7FFFFFFF/HZ)
        rt_expires = 0;
    else
        rt_expires = jiffies + valid_lft * HZ;

    rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1);

    if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
        if (rt->rt6i_flags&RTF_EXPIRES) {
            if (pinfo->onlink == 0 || valid_lft == 0) {
                ip6_del_rt(rt);
            } else {
                rt->rt6i_expires = rt_expires;
            }
        }
    } else if (pinfo->onlink && valid_lft) {
        addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
                              dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES);
    }
    if (rt)
        dst_release(&rt->u.dst);

    /* Try to figure out our local address for this prefix */

    if (pinfo->autoconf && in6_dev->cnf.autoconf) {
        struct inet6_ifaddr * ifp;
        struct in6_addr addr;
        int plen;

        plen = pinfo->prefix_len >> 3;

#ifdef CONFIG_IPV6_EUI64
        if (pinfo->prefix_len == 64) {
            memcpy(&addr, &pinfo->prefix, 8);
            if (ipv6_generate_eui64(addr.s6_addr + 8, dev))
                return;
            goto ok;
        }
#endif
#ifndef CONFIG_IPV6_NO_PB
        if (pinfo->prefix_len == ((sizeof(struct in6_addr) - dev->addr_len)<<3)) {
            memcpy(&addr, &pinfo->prefix, plen);
            memcpy(addr.s6_addr + plen, dev->dev_addr,
                   dev->addr_len);
            goto ok;
        }
#endif
        printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n", pinfo->prefix_len);
        return;

ok:
        ifp = ipv6_chk_addr(&addr, dev, 1);

        if ((ifp == NULL || (ifp->flags&ADDR_INVALID)) && valid_lft) {

            if (ifp == NULL)
                ifp = ipv6_add_addr(in6_dev, &addr, addr_type & IPV6_ADDR_SCOPE_MASK);

            if (ifp == NULL)
                return;

            ifp->prefix_len = pinfo->prefix_len;

            addrconf_dad_start(ifp);
        }

        if (ifp && valid_lft == 0) {
            ipv6_del_addr(ifp);
            ifp = NULL;
        }

        if (ifp) {
            int event = 0;
            ifp->valid_lft = valid_lft;
            ifp->prefered_lft = prefered_lft;
            ifp->tstamp = jiffies;
            if (ifp->flags & ADDR_INVALID)
                event = RTM_NEWADDR;
            ifp->flags &= ~(ADDR_DEPRECATED|ADDR_INVALID);
            ipv6_ifa_notify(event, ifp);
        }
    }
}
Ejemplo n.º 15
0
/*
 *	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;
}
Ejemplo n.º 16
0
void ipip6_err(struct sk_buff *skb, u32 info)
{
#ifndef I_WISH_WORLD_WERE_PERFECT

    /* It is not :-( All the routers (except for Linux) return only
       8 bytes of packet payload. It means, that precise relaying of
       ICMP in the real Internet is absolutely infeasible.
     */
    struct iphdr *iph = (struct iphdr*)skb->data;
    int type = skb->h.icmph->type;
    int code = skb->h.icmph->code;
    struct ip_tunnel *t;

    switch (type) {
    default:
    case ICMP_PARAMETERPROB:
        return;

    case ICMP_DEST_UNREACH:
        switch (code) {
        case ICMP_SR_FAILED:
        case ICMP_PORT_UNREACH:
            /* Impossible event. */
            return;
        case ICMP_FRAG_NEEDED:
            /* Soft state for pmtu is maintained by IP core. */
            return;
        default:
            /* All others are translated to HOST_UNREACH.
               rfc2003 contains "deep thoughts" about NET_UNREACH,
               I believe they are just ether pollution. --ANK
             */
            break;
        }
        break;
    case ICMP_TIME_EXCEEDED:
        if (code != ICMP_EXC_TTL)
            return;
        break;
    }

    read_lock(&ipip6_lock);
    t = ipip6_tunnel_lookup(iph->daddr, iph->saddr);
    if (t == NULL || t->parms.iph.daddr == 0)
        goto out;
    if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
        goto out;

    if (jiffies - t->err_time < IPTUNNEL_ERR_TIMEO)
        t->err_count++;
    else
        t->err_count = 1;
    t->err_time = jiffies;
out:
    read_unlock(&ipip6_lock);
    return;
#else
    struct iphdr *iph = (struct iphdr*)dp;
    int hlen = iph->ihl<<2;
    struct ipv6hdr *iph6;
    int type = skb->h.icmph->type;
    int code = skb->h.icmph->code;
    int rel_type = 0;
    int rel_code = 0;
    int rel_info = 0;
    struct sk_buff *skb2;
    struct rt6_info *rt6i;

    if (len < hlen + sizeof(struct ipv6hdr))
        return;
    iph6 = (struct ipv6hdr*)(dp + hlen);

    switch (type) {
    default:
        return;
    case ICMP_PARAMETERPROB:
        if (skb->h.icmph->un.gateway < hlen)
            return;

        /* So... This guy found something strange INSIDE encapsulated
           packet. Well, he is fool, but what can we do ?
         */
        rel_type = ICMPV6_PARAMPROB;
        rel_info = skb->h.icmph->un.gateway - hlen;
        break;

    case ICMP_DEST_UNREACH:
        switch (code) {
        case ICMP_SR_FAILED:
        case ICMP_PORT_UNREACH:
            /* Impossible event. */
            return;
        case ICMP_FRAG_NEEDED:
            /* Too complicated case ... */
            return;
        default:
            /* All others are translated to HOST_UNREACH.
               rfc2003 contains "deep thoughts" about NET_UNREACH,
               I believe, it is just ether pollution. --ANK
             */
            rel_type = ICMPV6_DEST_UNREACH;
            rel_code = ICMPV6_ADDR_UNREACH;
            break;
        }
        break;
    case ICMP_TIME_EXCEEDED:
        if (code != ICMP_EXC_TTL)
            return;
        rel_type = ICMPV6_TIME_EXCEED;
        rel_code = ICMPV6_EXC_HOPLIMIT;
        break;
    }

    /* Prepare fake skb to feed it to icmpv6_send */
    skb2 = skb_clone(skb, GFP_ATOMIC);
    if (skb2 == NULL)
        return;
    dst_release(skb2->dst);
    skb2->dst = NULL;
    skb_pull(skb2, skb->data - (u8*)iph6);
    skb2->nh.raw = skb2->data;

    /* Try to guess incoming interface */
    rt6i = rt6_lookup(&iph6->saddr, NULL, NULL, 0);
    if (rt6i && rt6i->rt6i_dev) {
        skb2->dev = rt6i->rt6i_dev;

        rt6i = rt6_lookup(&iph6->daddr, &iph6->saddr, NULL, 0);

        if (rt6i && rt6i->rt6i_dev && rt6i->rt6i_dev->type == ARPHRD_SIT) {
            struct ip_tunnel * t = (struct ip_tunnel*)rt6i->rt6i_dev->priv;
            if (rel_type == ICMPV6_TIME_EXCEED && t->parms.iph.ttl) {
                rel_type = ICMPV6_DEST_UNREACH;
                rel_code = ICMPV6_ADDR_UNREACH;
            }
            icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev);
        }
    }
    kfree_skb(skb2);
    return;
#endif
}
Ejemplo n.º 17
0
int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
{
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct net_device *dev = NULL;
	struct inet6_dev *idev;
	struct ipv6_ac_socklist *pac;
	int	ishost = !ipv6_devconf.forwarding;
	int	err = 0;

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;
	if (ipv6_addr_is_multicast(addr))
		return -EINVAL;
	if (ipv6_chk_addr(addr, NULL, 0))
		return -EINVAL;

	pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
	if (pac == NULL)
		return -ENOMEM;
	pac->acl_next = NULL;
	ipv6_addr_copy(&pac->acl_addr, addr);

	if (ifindex == 0) {
		struct rt6_info *rt;

		rt = rt6_lookup(addr, NULL, 0, 0);
		if (rt) {
			dev = rt->rt6i_dev;
			dev_hold(dev);
			dst_release(&rt->u.dst);
		} else if (ishost) {
			err = -EADDRNOTAVAIL;
			goto out_free_pac;
		} else {
			/* router, no matching interface: just pick one */

			dev = dev_get_by_flags(&init_net, IFF_UP, IFF_UP|IFF_LOOPBACK);
		}
	} else
		dev = dev_get_by_index(&init_net, ifindex);

	if (dev == NULL) {
		err = -ENODEV;
		goto out_free_pac;
	}

	idev = in6_dev_get(dev);
	if (!idev) {
		if (ifindex)
			err = -ENODEV;
		else
			err = -EADDRNOTAVAIL;
		goto out_dev_put;
	}
	/* reset ishost, now that we have a specific device */
	ishost = !idev->cnf.forwarding;
	in6_dev_put(idev);

	pac->acl_ifindex = dev->ifindex;

	/* XXX
	 * For hosts, allow link-local or matching prefix anycasts.
	 * This obviates the need for propagating anycast routes while
	 * still allowing some non-router anycast participation.
	 */
	if (!ip6_onlink(addr, dev)) {
		if (ishost)
			err = -EADDRNOTAVAIL;
		if (err)
			goto out_dev_put;
	}

	err = ipv6_dev_ac_inc(dev, addr);
	if (err)
		goto out_dev_put;

	write_lock_bh(&ipv6_sk_ac_lock);
	pac->acl_next = np->ipv6_ac_list;
	np->ipv6_ac_list = pac;
	write_unlock_bh(&ipv6_sk_ac_lock);

	dev_put(dev);

	return 0;

out_dev_put:
	dev_put(dev);
out_free_pac:
	sock_kfree_s(sk, pac, sizeof(*pac));
	return err;
}