Example #1
0
static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
{
    struct inet6_ifaddr *ifp;
    struct inet6_dev *idev;
    struct device *dev;
    int scope;

    if ((dev = dev_get_by_index(ifindex)) == NULL)
        return -ENODEV;

    if ((idev = ipv6_get_idev(dev)) == NULL)
        return -ENXIO;

    scope = ipv6_addr_scope(pfx);

    for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) {
        if (ifp->scope == scope && ifp->prefix_len == plen &&
                (!memcmp(pfx, &ifp->addr, sizeof(struct in6_addr)))) {
            ipv6_del_addr(ifp);

            /* If the last address is deleted administratively,
               disable IPv6 on this interface.
             */

            if (idev->addr_list == NULL)
                addrconf_ifdown(idev->dev, 1);
            return 0;
        }
    }
    return -EADDRNOTAVAIL;
}
Example #2
0
static struct inet6_dev * ipv6_find_idev(struct device *dev)
{
	struct inet6_dev *idev;

	if ((idev = ipv6_get_idev(dev)) == NULL) {
		idev = ipv6_add_dev(dev);
		if (idev == NULL)
			return NULL;
	}
	return idev;
}
Example #3
0
static struct inet6_dev * ipv6_find_idev(struct device *dev)
{
    struct inet6_dev *idev;

    if ((idev = ipv6_get_idev(dev)) == NULL) {
        idev = ipv6_add_dev(dev);
        if (idev == NULL)
            return NULL;
    }
    if (dev->flags&IFF_UP)
        ipv6_mc_up(idev);
    return idev;
}
Example #4
0
struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
{
	struct inet6_ifaddr *ifp = NULL;
	struct inet6_dev *idev;

	if ((idev = ipv6_get_idev(dev)) != NULL) {
		addrconf_lock();
		for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
			if (ifp->scope == IFA_LINK)
				break;
		}
		addrconf_unlock();
	}
	return ifp;
}
Example #5
0
static int ndisc_constructor(struct neighbour *neigh)
{
	struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
	struct device *dev = neigh->dev;
	struct inet6_dev *in6_dev = ipv6_get_idev(dev);
	int addr_type;

	if (in6_dev == NULL)
		return -EINVAL;

	addr_type = ipv6_addr_type(addr);
	if (in6_dev->nd_parms)
		neigh->parms = in6_dev->nd_parms;

	if (addr_type&IPV6_ADDR_MULTICAST)
		neigh->type = RTN_MULTICAST;
	else
		neigh->type = RTN_UNICAST;
	if (dev->hard_header == NULL) {
		neigh->nud_state = NUD_NOARP;
		neigh->ops = &ndisc_direct_ops;
		neigh->output = neigh->ops->queue_xmit;
	} else {
		if (addr_type&IPV6_ADDR_MULTICAST) {
			neigh->nud_state = NUD_NOARP;
			ndisc_mc_map(addr, neigh->ha, dev, 1);
		} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
			neigh->nud_state = NUD_NOARP;
			memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
			if (dev->flags&IFF_LOOPBACK)
				neigh->type = RTN_LOCAL;
		} else if (dev->flags&IFF_POINTOPOINT) {
			neigh->nud_state = NUD_NOARP;
			memcpy(neigh->ha, dev->broadcast, dev->addr_len);
		}
		if (dev->hard_header_cache)
			neigh->ops = &ndisc_hh_ops;
		else
			neigh->ops = &ndisc_generic_ops;
		if (neigh->nud_state&NUD_VALID)
			neigh->output = neigh->ops->connected_output;
		else
			neigh->output = neigh->ops->output;
	}

	return 0;
}
Example #6
0
/*
 *	device multicast group inc (add if not found)
 */
int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
{
	struct ifmcaddr6 *mc;
	struct inet6_dev    *idev;
	int hash;

	idev = ipv6_get_idev(dev);

	if (idev == NULL)
		return -EINVAL;

	hash = ipv6_addr_hash(addr);

	for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
		if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0 && mc->dev == dev) {
			atomic_inc(&mc->mca_users);
			return 0;
		}
	}

	/*
	 *	not found: create a new one.
	 */

	mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);

	if (mc == NULL)
		return -ENOMEM;

	memset(mc, 0, sizeof(struct ifmcaddr6));
	mc->mca_timer.function = igmp6_timer_handler;
	mc->mca_timer.data = (unsigned long) mc;

	memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr));
	mc->dev = dev;
	atomic_set(&mc->mca_users, 1);

	mc->next = inet6_mcast_lst[hash];
	inet6_mcast_lst[hash] = mc;

	mc->if_next = idev->mc_list;
	idev->mc_list = mc;

	igmp6_group_added(mc);

	return 0;
}
Example #7
0
static void pndisc_destructor(struct pneigh_entry *n)
{
	struct in6_addr *addr = (struct in6_addr*)&n->key;
	struct in6_addr maddr;
	struct device *dev = n->dev;

	if (dev == NULL || ipv6_get_idev(dev) == NULL)
		return;
#ifndef CONFIG_IPV6_NO_PB
	addrconf_addr_solict_mult_old(addr, &maddr);
	ipv6_dev_mc_dec(dev, &maddr);
#endif
#ifdef CONFIG_IPV6_EUI64
	addrconf_addr_solict_mult_new(addr, &maddr);
	ipv6_dev_mc_dec(dev, &maddr);
#endif
}
Example #8
0
static int igmp6_read_proc(char *buffer, char **start, off_t offset,
			   int length, int *eof, void *data)
{
	off_t pos=0, begin=0;
	struct ifmcaddr6 *im;
	int len=0;
	struct device *dev;
	
	for (dev = dev_base; dev; dev = dev->next) {
		struct inet6_dev *idev;

		if ((idev = ipv6_get_idev(dev)) == NULL)
			continue;

		for (im = idev->mc_list; im; im = im->if_next) {
			int i;

			len += sprintf(buffer+len,"%-4d %-15s ", dev->ifindex, dev->name);

			for (i=0; i<16; i++)
				len += sprintf(buffer+len, "%02x", im->mca_addr.s6_addr[i]);

			len+=sprintf(buffer+len,
				     " %5d %08X %ld\n",
				     atomic_read(&im->mca_users),
				     im->mca_flags,
				     (im->mca_flags&MAF_TIMER_RUNNING) ? im->mca_timer.expires-jiffies : 0);

			pos=begin+len;
			if (pos < offset) {
				len=0;
				begin=pos;
			}
			if (pos > offset+length)
				goto done;
		}
	}
	*eof = 1;

done:
	*start=buffer+(offset-begin);
	len-=(offset-begin);
	if(len>length)
		len=length;
	return len;
}
Example #9
0
static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma)
{
	struct inet6_dev *idev;

	idev = ipv6_get_idev(dev);

	if (idev) {
		struct ifmcaddr6 *iter, **lnk;
		
		for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) {
			if (iter == ma) {
				*lnk = iter->if_next;
				return;
			}
		}
	}
}
Example #10
0
int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
{
	struct ifmcaddr6 *ma;
	struct in6_addr *addrp;
	unsigned long resptime;

	if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr))
		return -EINVAL;

	/* Drop queries with not link local source */
	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
		return -EINVAL;

	resptime = ntohs(hdr->icmp6_maxdelay);
	/* Translate milliseconds to jiffies */
	resptime = (resptime<<10)/(1024000/HZ);

	addrp = (struct in6_addr *) (hdr + 1);

	if (ipv6_addr_any(addrp)) {
		struct inet6_dev *idev;

		idev = ipv6_get_idev(skb->dev);

		if (idev == NULL)
			return 0;

		for (ma = idev->mc_list; ma; ma=ma->if_next)
			igmp6_group_queried(ma, resptime);
	} else {
		int hash = ipv6_addr_hash(addrp);

		for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
			if (ma->dev == skb->dev &&
			    ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
				igmp6_group_queried(ma, resptime);
				break;
			}
		}
	}

	return 0;
}
Example #11
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);
        }
    }
}