Esempio n. 1
0
static int _group_idx(const gnrc_netif_t *netif, const ipv6_addr_t *addr)
{
    if (!ipv6_addr_is_unspecified(addr)) {
        for (unsigned i = 0; i < GNRC_NETIF_IPV6_GROUPS_NUMOF; i++) {
            if (ipv6_addr_equal(&netif->ipv6.groups[i], addr)) {
                return i;
            }
        }
    }
    return -1;
}
Esempio n. 2
0
static void test_ipv6_netif_find_best_src_addr__multicast_input(void)
{
    ipv6_addr_t mc_addr = IPV6_ADDR_ALL_ROUTERS_LINK_LOCAL;
    ipv6_addr_t *out = NULL;

    /* Adds DEFAULT_TEST_NETIF as interface and to it fe80::1, fe80::2 and ff02::1 */
    test_ipv6_netif_find_best_src_addr__success();

    TEST_ASSERT_NOT_NULL((out = ng_ipv6_netif_find_best_src_addr(DEFAULT_TEST_NETIF, &mc_addr)));
    TEST_ASSERT_EQUAL_INT(false, ipv6_addr_equal(&mc_addr, out));
    TEST_ASSERT_EQUAL_INT(false, ipv6_addr_is_unspecified(out));
}
Esempio n. 3
0
/*
 *	device anycast group inc (add if not found)
 */
int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr)
{
	struct ifacaddr6 *aca;
	struct rt6_info *rt;
	int err;

	ASSERT_RTNL();

	write_lock_bh(&idev->lock);
	if (idev->dead) {
		err = -ENODEV;
		goto out;
	}

	for (aca = idev->ac_list; aca; aca = aca->aca_next) {
		if (ipv6_addr_equal(&aca->aca_addr, addr)) {
			aca->aca_users++;
			err = 0;
			goto out;
		}
	}

	rt = addrconf_dst_alloc(idev, addr, true);
	if (IS_ERR(rt)) {
		err = PTR_ERR(rt);
		goto out;
	}
	aca = aca_alloc(rt, addr);
	if (!aca) {
		ip6_rt_put(rt);
		err = -ENOMEM;
		goto out;
	}

	aca->aca_next = idev->ac_list;
	idev->ac_list = aca;

	/* Hold this for addrconf_join_solict() below before we unlock,
	 * it is already exposed via idev->ac_list.
	 */
	aca_get(aca);
	write_unlock_bh(&idev->lock);

	ip6_ins_rt(rt);

	addrconf_join_solict(idev->dev, &aca->aca_addr);

	aca_put(aca);
	return 0;
out:
	write_unlock_bh(&idev->lock);
	return err;
}
Esempio n. 4
0
static int ip6mr_mfc_delete(struct net *net, struct mf6cctl *mfc)
{
	int line;
	struct mfc6_cache *c, **cp;

	line = MFC6_HASH(&mfc->mf6cc_mcastgrp.sin6_addr, &mfc->mf6cc_origin.sin6_addr);

	for (cp = &net->ipv6.mfc6_cache_array[line];
	     (c = *cp) != NULL; cp = &c->next) {
		if (ipv6_addr_equal(&c->mf6c_origin, &mfc->mf6cc_origin.sin6_addr) &&
		    ipv6_addr_equal(&c->mf6c_mcastgrp, &mfc->mf6cc_mcastgrp.sin6_addr)) {
			write_lock_bh(&mrt_lock);
			*cp = c->next;
			write_unlock_bh(&mrt_lock);

			ip6mr_cache_free(c);
			return 0;
		}
	}
	return -ENOENT;
}
Esempio n. 5
0
static void test_ipv6_netif_find_by_addr__success(void)
{
    ipv6_addr_t addr = DEFAULT_TEST_IPV6_ADDR;
    ipv6_addr_t *out = NULL;

    test_ipv6_netif_add_addr__success(); /* adds DEFAULT_TEST_IPV6_ADDR to
                                          * DEFAULT_TEST_NETIF */

    TEST_ASSERT_EQUAL_INT(DEFAULT_TEST_NETIF, ng_ipv6_netif_find_by_addr(&out, &addr));
    TEST_ASSERT_NOT_NULL(out);
    TEST_ASSERT(out != &addr);
    TEST_ASSERT_EQUAL_INT(true, ipv6_addr_equal(out, &addr));
}
Esempio n. 6
0
static int
ip6ip6_tnl_locate(struct ip6_tnl_parm *p, struct ip6_tnl **pt, int create)
{
	struct in6_addr *remote = &p->raddr;
	struct in6_addr *local = &p->laddr;
	struct ip6_tnl *t;

	if (p->proto != IPPROTO_IPV6)
		return -EINVAL;

	for (t = *ip6ip6_bucket(p); t; t = t->next) {
		if (ipv6_addr_equal(local, &t->parms.laddr) &&
		    ipv6_addr_equal(remote, &t->parms.raddr)) {
			*pt = t;
			return (create ? -EEXIST : 0);
		}
	}
	if (!create)
		return -ENODEV;
	
	return ip6_tnl_create(p, pt);
}
Esempio n. 7
0
static int ip6t_npt_checkentry(const struct xt_tgchk_param *par)
{
	struct ip6t_npt_tginfo *npt = par->targinfo;
	struct in6_addr pfx;
	__wsum src_sum, dst_sum;

	if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64)
		return -EINVAL;

	/* Ensure that LSB of prefix is zero */
	ipv6_addr_prefix(&pfx, &npt->src_pfx.in6, npt->src_pfx_len);
	if (!ipv6_addr_equal(&pfx, &npt->src_pfx.in6))
		return -EINVAL;
	ipv6_addr_prefix(&pfx, &npt->dst_pfx.in6, npt->dst_pfx_len);
	if (!ipv6_addr_equal(&pfx, &npt->dst_pfx.in6))
		return -EINVAL;

	src_sum = csum_partial(&npt->src_pfx.in6, sizeof(npt->src_pfx.in6), 0);
	dst_sum = csum_partial(&npt->dst_pfx.in6, sizeof(npt->dst_pfx.in6), 0);

	npt->adjustment = ~csum_fold(csum_sub(src_sum, dst_sum));
	return 0;
}
Esempio n. 8
0
static void test_ipv6_nc_get__success_if_global(void)
{
    ipv6_addr_t addr = DEFAULT_TEST_IPV6_ADDR;
    gnrc_ipv6_nc_t *entry = NULL;

    test_ipv6_nc_add__success(); /* adds DEFAULT_TEST_IPV6_ADDR to DEFAULT_TEST_NETIF */

    TEST_ASSERT_NOT_NULL((entry = gnrc_ipv6_nc_get(KERNEL_PID_UNDEF, &addr)));
    TEST_ASSERT_EQUAL_INT(DEFAULT_TEST_NETIF, entry->iface);
    TEST_ASSERT(ipv6_addr_equal(&(entry->ipv6_addr), &addr));
    TEST_ASSERT_EQUAL_STRING(TEST_STRING4, (char *)entry->l2_addr);
    TEST_ASSERT_EQUAL_INT(sizeof(TEST_STRING4), entry->l2_addr_len);
    TEST_ASSERT_EQUAL_INT(0, entry->flags);
}
Esempio n. 9
0
void uhcp_handle_prefix(uint8_t *prefix, uint8_t prefix_len, uint16_t lifetime, uint8_t *src, uhcp_iface_t iface)
{
    (void)prefix_len;
    (void)lifetime;
    (void)src;

    eui64_t iid;
    if (!gnrc_wireless_interface) {
        LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): received prefix, but don't know any wireless interface\n");
        return;
    }

    if ((kernel_pid_t)iface != gnrc_border_interface) {
        LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): received prefix from unexpected interface\n");
        return;
    }

    if (gnrc_netapi_get(gnrc_wireless_interface, NETOPT_IPV6_IID, 0, &iid,
                        sizeof(eui64_t)) >= 0) {
        ipv6_addr_set_aiid((ipv6_addr_t*)prefix, iid.uint8);
    }
    else {
        LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): cannot get IID of wireless interface\n");
        return;
    }

    if (ipv6_addr_equal(&_prefix, (ipv6_addr_t*)prefix)) {
        LOG_WARNING("gnrc_uhcpc: uhcp_handle_prefix(): got same prefix again\n");
        return;
    }

    gnrc_ipv6_netif_add_addr(gnrc_wireless_interface, (ipv6_addr_t*)prefix, 64,
                             GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST |
                             GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_AUTO);

    gnrc_ipv6_netif_remove_addr(gnrc_wireless_interface, &_prefix);
    print_str("gnrc_uhcpc: uhcp_handle_prefix(): configured new prefix ");
    ipv6_addr_print((ipv6_addr_t*)prefix);
    puts("/64");

    if (!ipv6_addr_is_unspecified(&_prefix)) {
        gnrc_ipv6_netif_remove_addr(gnrc_wireless_interface, &_prefix);
        print_str("gnrc_uhcpc: uhcp_handle_prefix(): removed old prefix ");
        ipv6_addr_print(&_prefix);
        puts("/64");
    }

    memcpy(&_prefix, prefix, 16);
}
Esempio n. 10
0
static void test_ipv6_netif_find_best_src_addr__other_subnet(void)
{
    ipv6_addr_t addr1 = DEFAULT_TEST_IPV6_ADDR;
    ipv6_addr_t addr2 = OTHER_TEST_IPV6_ADDR;

    ipv6_addr_t *out = NULL;

    test_ipv6_netif_add__success(); /* adds DEFAULT_TEST_NETIF as interface */
    TEST_ASSERT_NOT_NULL(ng_ipv6_netif_add_addr(DEFAULT_TEST_NETIF, &addr1, DEFAULT_TEST_PREFIX_LEN,
                         0));

    TEST_ASSERT_NOT_NULL((out = ng_ipv6_netif_find_best_src_addr(DEFAULT_TEST_NETIF, &addr2)));
    TEST_ASSERT(out != &addr1);
    TEST_ASSERT_EQUAL_INT(true, ipv6_addr_equal(out, &addr1));
}
Esempio n. 11
0
static bool addr_same(const struct inetpeer_addr *a,
		      const struct inetpeer_addr *b)
{
	const struct in6_addr *a6, *b6;

	if (a->family != b->family)
		return false;
	if (a->family == AF_INET)
		return a->addr.a4 == b->addr.a4;

	a6 = (const struct in6_addr *) &a->addr.a6[0];
	b6 = (const struct in6_addr *) &b->addr.a6[0];

	return ipv6_addr_equal(a6, b6);
}
/*
 *	check if the interface has this anycast address
 *	called with rcu_read_lock()
 */
static int ipv6_chk_acast_dev(struct net_device *dev, const struct in6_addr *addr)
{
	struct inet6_dev *idev;
	struct ifacaddr6 *aca;

	idev = __in6_dev_get(dev);
	if (idev) {
		read_lock_bh(&idev->lock);
		for (aca = idev->ac_list; aca; aca = aca->aca_next)
			if (ipv6_addr_equal(&aca->aca_addr, addr))
				break;
		read_unlock_bh(&idev->lock);
		return aca != NULL;
	}
	return 0;
}
Esempio n. 13
0
static gnrc_sixlowpan_nd_router_abr_t *_get_abr(ipv6_addr_t *addr)
{
    gnrc_sixlowpan_nd_router_abr_t *abr = NULL;

    for (int i = 0; i < GNRC_SIXLOWPAN_ND_ROUTER_ABR_NUMOF; i++) {
        if (ipv6_addr_equal(&_abrs[i].addr, addr)) {
            return &_abrs[i];
        }

        if ((abr == NULL) && ipv6_addr_is_unspecified(&_abrs[i].addr)) {
            abr = &_abrs[i];
        }
    }

    return abr;
}
Esempio n. 14
0
File: core.c Progetto: gxt/linux
static int brcmf_inet6addr_changed(struct notifier_block *nb,
				   unsigned long action, void *data)
{
	struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub,
					      inet6addr_notifier);
	struct inet6_ifaddr *ifa = data;
	struct brcmf_if *ifp;
	int i;
	struct in6_addr *table;

	/* Only handle primary interface */
	ifp = drvr->iflist[0];
	if (!ifp)
		return NOTIFY_DONE;
	if (ifp->ndev != ifa->idev->dev)
		return NOTIFY_DONE;

	table = ifp->ipv6_addr_tbl;
	for (i = 0; i < NDOL_MAX_ENTRIES; i++)
		if (ipv6_addr_equal(&ifa->addr, &table[i]))
			break;

	switch (action) {
	case NETDEV_UP:
		if (i == NDOL_MAX_ENTRIES) {
			if (ifp->ipv6addr_idx < NDOL_MAX_ENTRIES) {
				table[ifp->ipv6addr_idx++] = ifa->addr;
			} else {
				for (i = 0; i < NDOL_MAX_ENTRIES - 1; i++)
					table[i] = table[i + 1];
				table[NDOL_MAX_ENTRIES - 1] = ifa->addr;
			}
		}
		break;
	case NETDEV_DOWN:
		if (i < NDOL_MAX_ENTRIES)
			for (; i < ifp->ipv6addr_idx; i++)
				table[i] = table[i + 1];
		break;
	default:
		break;
	}

	schedule_work(&ifp->ndoffload_work);

	return NOTIFY_OK;
}
Esempio n. 15
0
static void *_ipv6_fwd_eventloop(void *arg)
{
    (void)arg;

    msg_t msg, msg_q[8];
    gnrc_netreg_entry_t me_reg;

    msg_init_queue(msg_q, 8);

    me_reg.demux_ctx = GNRC_NETREG_DEMUX_CTX_ALL;
    me_reg.pid = thread_getpid();

    gnrc_netreg_register(GNRC_NETTYPE_SIXLOWPAN, &me_reg);

    while(1) {
        msg_receive(&msg);
        gnrc_pktsnip_t *pkt = msg.content.ptr;
        if(msg.type == GNRC_NETAPI_MSG_TYPE_SND) {
            gnrc_pktsnip_t *ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
            ipv6 = ipv6->data;

            ipv6_hdr_t *ipv6_hdr =(ipv6_hdr_t *)ipv6;

            /* get the first IPv6 interface and prints its address */
            kernel_pid_t ifs[GNRC_NETIF_NUMOF];
            gnrc_netif_get(ifs);
            gnrc_ipv6_netif_t *entry = gnrc_ipv6_netif_get(ifs[0]);
            for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) {
                if ( (!ipv6_addr_is_link_local(&entry->addrs[i].addr)) && (!ipv6_addr_is_link_local(&ipv6_hdr->src)) 
                    && (!ipv6_addr_is_link_local(&ipv6_hdr->dst)) && !(entry->addrs[i].flags & GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST)
                    && (!ipv6_addr_is_unspecified(&entry->addrs[i].addr)) ) {

                    if(!ipv6_addr_equal(&entry->addrs[i].addr, &(ipv6_hdr->src))){

                        char addr_str[IPV6_ADDR_MAX_STR_LEN];
                        printf("IPv6 ROUTER: forward from src = %s ", ipv6_addr_to_str(addr_str, &(ipv6_hdr->src), sizeof(addr_str)) );
                        printf("to dst = %s\n",ipv6_addr_to_str(addr_str, &(ipv6_hdr->dst), sizeof(addr_str)));
                    }
                }
            }
        }
        gnrc_pktbuf_release(pkt);
    }
    /* never reached */
    return NULL;
}
Esempio n. 16
0
static void _remove_addr_from_entry(gnrc_ipv6_netif_t *entry, ipv6_addr_t *addr)
{
    mutex_lock(&entry->mutex);

    for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) {
        if (ipv6_addr_equal(&(entry->addrs[i].addr), addr)) {
            DEBUG("ipv6 netif: Remove %s to interface %" PRIkernel_pid "\n",
                  ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), entry->pid);
            ipv6_addr_set_unspecified(&(entry->addrs[i].addr));
            entry->addrs[i].flags = 0;

            mutex_unlock(&entry->mutex);
            return;
        }
    }

    mutex_unlock(&entry->mutex);
}
Esempio n. 17
0
int inet6_ac_check(struct sock *sk, struct in6_addr *addr, int ifindex)
{
	struct ipv6_ac_socklist *pac;
	struct ipv6_pinfo *np = inet6_sk(sk);
	int	found;

	found = 0;
	read_lock(&ipv6_sk_ac_lock);
	for (pac=np->ipv6_ac_list; pac; pac=pac->acl_next) {
		if (ifindex && pac->acl_ifindex != ifindex)
			continue;
		found = ipv6_addr_equal(&pac->acl_addr, addr);
		if (found)
			break;
	}
	read_unlock(&ipv6_sk_ac_lock);

	return found;
}
Esempio n. 18
0
static int dnsmq_ctrl_ipv6(struct file *file, const char *buffer, unsigned long length, void *data)
{
	char s[64];
	char *ptr;
	int i, j;

	// "[dnsmq ipv6] [dnsmq name]
	if ((length > 0) && (length < 64)) {
		memcpy(s, buffer, length);
		s[length] = 0;
		for(i=0;i<length;i++) {
			if (s[i] == ' ') break;
		}
		if (i<length) {
			s[i] = 0;
			ptr = s + i + 1;
			in6_pton(s, length, (void *)&dnsmq_ipv6, '\n', NULL);

			// convert to dnsname format
			j=0;
			for(;i<length;i++) {
				if (s[i] == '.') {
					s[i]=0;
					dnsmq_name_ipv6[j]=strlen(ptr);
					memcpy(dnsmq_name_ipv6+j+1, ptr, strlen(ptr));
					j += strlen(ptr) + 1;
					ptr = s + i + 1;
				}
			}
			dnsmq_name_ipv6[j]=strlen(ptr);
			memcpy(dnsmq_name_ipv6+j+1, ptr, strlen(ptr));
			dnsmq_name_ipv6[j+1+strlen(ptr)]=0;
		}
	}
	else memset(&dnsmq_ipv6, 0, sizeof(struct in6_addr));

	printk(KERN_DEBUG "dnsmq ctrl ipv6: %pI6 %s\n", &dnsmq_ipv6, dnsmq_name_ipv6);

	if (ipv6_addr_equal(&dnsmq_ipv6, &in6addr_any)) dnsmq_hit_hook_func (NULL);
	else dnsmq_hit_hook_func(dnsmq_func);

	return length;
}
Esempio n. 19
0
gnrc_ipv6_nc_t *gnrc_ipv6_nc_get(kernel_pid_t iface, const ipv6_addr_t *ipv6_addr)
{
    if ((ipv6_addr == NULL) || (ipv6_addr_is_unspecified(ipv6_addr))) {
        DEBUG("ipv6_nc: address was NULL or ::\n");
        return NULL;
    }

    for (int i = 0; i < GNRC_IPV6_NC_SIZE; i++) {
        if (((ncache[i].iface == KERNEL_PID_UNDEF) || (iface == KERNEL_PID_UNDEF) ||
             (iface == ncache[i].iface)) && ipv6_addr_equal(&(ncache[i].ipv6_addr), ipv6_addr)) {
            DEBUG("ipv6_nc: Found entry for %s on interface %" PRIkernel_pid
                  " (0 = all interfaces) [%p]\n",
                  ipv6_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str)),
                  iface, (void *)(ncache + i));

            return ncache + i;
        }
    }

    return NULL;
}
Esempio n. 20
0
/*
 *	socket leave an anycast group
 */
int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct net_device *dev;
	struct ipv6_ac_socklist *pac, *prev_pac;
	struct net *net = sock_net(sk);

	spin_lock_bh(&ipv6_sk_ac_lock);
	prev_pac = NULL;
	for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) {
		if ((ifindex == 0 || pac->acl_ifindex == ifindex) &&
		     ipv6_addr_equal(&pac->acl_addr, addr))
			break;
		prev_pac = pac;
	}
	if (!pac) {
		spin_unlock_bh(&ipv6_sk_ac_lock);
		return -ENOENT;
	}
	if (prev_pac)
		prev_pac->acl_next = pac->acl_next;
	else
		np->ipv6_ac_list = pac->acl_next;

	spin_unlock_bh(&ipv6_sk_ac_lock);

	rtnl_lock();
	rcu_read_lock();
	dev = dev_get_by_index_rcu(net, pac->acl_ifindex);
	if (dev)
		ipv6_dev_ac_dec(dev, &pac->acl_addr);
	rcu_read_unlock();
	rtnl_unlock();

	sock_kfree_s(sk, pac, sizeof(*pac));
	return 0;
}
Esempio n. 21
0
gnrc_sixlowpan_ctx_t *gnrc_sixlowpan_ctx_update(uint8_t id, const ipv6_addr_t *prefix,
                                                uint8_t prefix_len, uint16_t ltime,
                                                bool comp)
{
    if ((id >= GNRC_SIXLOWPAN_CTX_SIZE) || (prefix_len == 0)) {
        return NULL;
    }

    mutex_lock(&_ctx_mutex);

    _ctxs[id].ltime = ltime;

    if (ltime == 0) {
        comp = false;
    }

    if (prefix_len > IPV6_ADDR_BIT_LEN) {
        _ctxs[id].prefix_len = IPV6_ADDR_BIT_LEN;
    }
    else {
        _ctxs[id].prefix_len = prefix_len;
    }

    _ctxs[id].flags_id = (comp) ? (GNRC_SIXLOWPAN_CTX_FLAGS_COMP | id) : id;

    if (!ipv6_addr_equal(&(_ctxs[id].prefix), prefix)) {
        ipv6_addr_set_unspecified(&(_ctxs[id].prefix));
        ipv6_addr_init_prefix(&(_ctxs[id].prefix), prefix, _ctxs[id].prefix_len);
    }
    DEBUG("6lo ctx: update context (%u, %s/%" PRIu8 "), lifetime: %" PRIu16 " min\n",
          id, ipv6_addr_to_str(ipv6str, &_ctxs[id].prefix, sizeof(ipv6str)),
          _ctxs[id].prefix_len, _ctxs[id].ltime);
    _ctx_inval_times[id] = ltime + _current_minute();

    mutex_unlock(&_ctx_mutex);
    return &(_ctxs[id]);
}
Esempio n. 22
0
ipv6_addr_t *gnrc_ipv6_netif_find_addr(kernel_pid_t pid, const ipv6_addr_t *addr)
{
    gnrc_ipv6_netif_t *entry = gnrc_ipv6_netif_get(pid);

    if (entry == NULL) {
        return NULL;
    }

    mutex_lock(&entry->mutex);

    for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) {
        if (ipv6_addr_equal(&(entry->addrs[i].addr), addr)) {
            mutex_unlock(&entry->mutex);
            DEBUG("ipv6 netif: Found %s on interface %" PRIkernel_pid "\n",
                  ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)),
                  pid);
            return &(entry->addrs[i].addr);
        }
    }

    mutex_unlock(&entry->mutex);

    return NULL;
}
Esempio n. 23
0
static void _remove_addr_from_entry(gnrc_ipv6_netif_t *entry, ipv6_addr_t *addr)
{
    mutex_lock(&entry->mutex);

    for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) {
        if (ipv6_addr_equal(&(entry->addrs[i].addr), addr)) {
            DEBUG("ipv6 netif: Remove %s to interface %" PRIkernel_pid "\n",
                  ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), entry->pid);
            ipv6_addr_set_unspecified(&(entry->addrs[i].addr));
            entry->addrs[i].flags = 0;
#ifdef MODULE_GNRC_NDP_ROUTER
            /* Removal of prefixes MAY allow the router to retransmit up to
             * GNRC_NDP_MAX_INIT_RTR_ADV_NUMOF unsolicited RA
             * (see https://tools.ietf.org/html/rfc4861#section-6.2.4) */
            if ((entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) &&
                (entry->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV) &&
                (!ipv6_addr_is_multicast(addr) &&
                 !ipv6_addr_is_link_local(addr))) {
                entry->rtr_adv_count = GNRC_NDP_MAX_INIT_RTR_ADV_NUMOF;
                mutex_unlock(&entry->mutex);    /* function below relocks the mutex */
                gnrc_ndp_router_retrans_rtr_adv(entry);
                return;
            }
#endif
#ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER
            gnrc_sixlowpan_nd_router_abr_t *abr = gnrc_sixlowpan_nd_router_abr_get();
            gnrc_sixlowpan_nd_router_abr_rem_prf(abr, entry, &entry->addrs[i]);
#endif

            mutex_unlock(&entry->mutex);
            return;
        }
    }

    mutex_unlock(&entry->mutex);
}
Esempio n. 24
0
static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
			  int addr_len)
{
	struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
	struct inet_sock *inet = inet_sk(sk);
	struct inet_connection_sock *icsk = inet_csk(sk);
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct tcp_sock *tp = tcp_sk(sk);
	struct in6_addr *saddr = NULL, *final_p, final;
	struct rt6_info *rt;
	struct flowi6 fl6;
	struct dst_entry *dst;
	int addr_type;
	int err;

	if (addr_len < SIN6_LEN_RFC2133)
		return -EINVAL;

	if (usin->sin6_family != AF_INET6)
		return -EAFNOSUPPORT;

	memset(&fl6, 0, sizeof(fl6));

	if (np->sndflow) {
		fl6.flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
		IP6_ECN_flow_init(fl6.flowlabel);
		if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
			struct ip6_flowlabel *flowlabel;
			flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
			if (flowlabel == NULL)
				return -EINVAL;
			usin->sin6_addr = flowlabel->dst;
			fl6_sock_release(flowlabel);
		}
	}


	if(ipv6_addr_any(&usin->sin6_addr))
		usin->sin6_addr.s6_addr[15] = 0x1;

	addr_type = ipv6_addr_type(&usin->sin6_addr);

	if(addr_type & IPV6_ADDR_MULTICAST)
		return -ENETUNREACH;

	if (addr_type&IPV6_ADDR_LINKLOCAL) {
		if (addr_len >= sizeof(struct sockaddr_in6) &&
		    usin->sin6_scope_id) {
			if (sk->sk_bound_dev_if &&
			    sk->sk_bound_dev_if != usin->sin6_scope_id)
				return -EINVAL;

			sk->sk_bound_dev_if = usin->sin6_scope_id;
		}

		
		if (!sk->sk_bound_dev_if)
			return -EINVAL;
	}

	if (tp->rx_opt.ts_recent_stamp &&
	    !ipv6_addr_equal(&np->daddr, &usin->sin6_addr)) {
		tp->rx_opt.ts_recent = 0;
		tp->rx_opt.ts_recent_stamp = 0;
		tp->write_seq = 0;
	}

	np->daddr = usin->sin6_addr;
	np->flow_label = fl6.flowlabel;


	if (addr_type == IPV6_ADDR_MAPPED) {
		u32 exthdrlen = icsk->icsk_ext_hdr_len;
		struct sockaddr_in sin;

		SOCK_DEBUG(sk, "connect: ipv4 mapped\n");

		if (__ipv6_only_sock(sk))
			return -ENETUNREACH;

		sin.sin_family = AF_INET;
		sin.sin_port = usin->sin6_port;
		sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];

		icsk->icsk_af_ops = &ipv6_mapped;
		sk->sk_backlog_rcv = tcp_v4_do_rcv;
#ifdef CONFIG_TCP_MD5SIG
		tp->af_specific = &tcp_sock_ipv6_mapped_specific;
#endif

		err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));

		if (err) {
			icsk->icsk_ext_hdr_len = exthdrlen;
			icsk->icsk_af_ops = &ipv6_specific;
			sk->sk_backlog_rcv = tcp_v6_do_rcv;
#ifdef CONFIG_TCP_MD5SIG
			tp->af_specific = &tcp_sock_ipv6_specific;
#endif
			goto failure;
		} else {
			ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
			ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
					       &np->rcv_saddr);
		}

		return err;
	}

	if (!ipv6_addr_any(&np->rcv_saddr))
		saddr = &np->rcv_saddr;

	fl6.flowi6_proto = IPPROTO_TCP;
	fl6.daddr = np->daddr;
	fl6.saddr = saddr ? *saddr : np->saddr;
	fl6.flowi6_oif = sk->sk_bound_dev_if;
	fl6.flowi6_mark = sk->sk_mark;
	fl6.fl6_dport = usin->sin6_port;
	fl6.fl6_sport = inet->inet_sport;
	fl6.flowi6_uid = sock_i_uid(sk);

	final_p = fl6_update_dst(&fl6, np->opt, &final);

	security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));

	dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true);
	if (IS_ERR(dst)) {
		err = PTR_ERR(dst);
		goto failure;
	}

	if (saddr == NULL) {
		saddr = &fl6.saddr;
		np->rcv_saddr = *saddr;
	}

	
	np->saddr = *saddr;
	inet->inet_rcv_saddr = LOOPBACK4_IPV6;

	sk->sk_gso_type = SKB_GSO_TCPV6;
	__ip6_dst_store(sk, dst, NULL, NULL);

	rt = (struct rt6_info *) dst;
	if (tcp_death_row.sysctl_tw_recycle &&
	    !tp->rx_opt.ts_recent_stamp &&
	    ipv6_addr_equal(&rt->rt6i_dst.addr, &np->daddr)) {
		struct inet_peer *peer = rt6_get_peer(rt);
		if (peer) {
			inet_peer_refcheck(peer);
			if ((u32)get_seconds() - peer->tcp_ts_stamp <= TCP_PAWS_MSL) {
				tp->rx_opt.ts_recent_stamp = peer->tcp_ts_stamp;
				tp->rx_opt.ts_recent = peer->tcp_ts;
			}
		}
	}

	icsk->icsk_ext_hdr_len = 0;
	if (np->opt)
		icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
					  np->opt->opt_nflen);

	tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);

	inet->inet_dport = usin->sin6_port;

	tcp_set_state(sk, TCP_SYN_SENT);
	err = inet6_hash_connect(&tcp_death_row, sk);
	if (err)
		goto late_failure;

	if (!tp->write_seq)
		tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32,
							     np->daddr.s6_addr32,
							     inet->inet_sport,
							     inet->inet_dport);

	err = tcp_connect(sk);
	if (err)
		goto late_failure;

	return 0;

late_failure:
	tcp_set_state(sk, TCP_CLOSE);
	__sk_dst_reset(sk);
failure:
	inet->inet_dport = 0;
	sk->sk_route_caps = 0;
	return err;
}
int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
	struct sockaddr_in6	*usin = (struct sockaddr_in6 *) uaddr;
	struct inet_sock      	*inet = inet_sk(sk);
	struct ipv6_pinfo      	*np = inet6_sk(sk);
	struct in6_addr		*daddr, *final_p, final;
	struct dst_entry	*dst;
	struct flowi6		fl6;
	struct ip6_flowlabel	*flowlabel = NULL;
	struct ipv6_txoptions   *opt;
	int			addr_type;
	int			err;

	if (usin->sin6_family == AF_INET) {
		if (__ipv6_only_sock(sk))
			return -EAFNOSUPPORT;
		err = ip4_datagram_connect(sk, uaddr, addr_len);
		goto ipv4_connected;
	}

	if (addr_len < SIN6_LEN_RFC2133)
		return -EINVAL;

	if (usin->sin6_family != AF_INET6)
		return -EAFNOSUPPORT;

	memset(&fl6, 0, sizeof(fl6));
	if (np->sndflow) {
		fl6.flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
		if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
			flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
			if (flowlabel == NULL)
				return -EINVAL;
			usin->sin6_addr = flowlabel->dst;
		}
	}

	addr_type = ipv6_addr_type(&usin->sin6_addr);

	if (addr_type == IPV6_ADDR_ANY) {
		/*
		 *	connect to self
		 */
		usin->sin6_addr.s6_addr[15] = 0x01;
	}

	daddr = &usin->sin6_addr;

	if (addr_type == IPV6_ADDR_MAPPED) {
		struct sockaddr_in sin;

		if (__ipv6_only_sock(sk)) {
			err = -ENETUNREACH;
			goto out;
		}
		sin.sin_family = AF_INET;
		sin.sin_addr.s_addr = daddr->s6_addr32[3];
		sin.sin_port = usin->sin6_port;

		err = ip4_datagram_connect(sk,
					   (struct sockaddr *) &sin,
					   sizeof(sin));

ipv4_connected:
		if (err)
			goto out;

		ipv6_addr_set_v4mapped(inet->inet_daddr, &np->daddr);

		if (ipv6_addr_any(&np->saddr) ||
		    ipv6_mapped_addr_any(&np->saddr))
			ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);

		if (ipv6_addr_any(&np->rcv_saddr) ||
		    ipv6_mapped_addr_any(&np->rcv_saddr)) {
			ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
					       &np->rcv_saddr);
			if (sk->sk_prot->rehash)
				sk->sk_prot->rehash(sk);
		}

		goto out;
	}

	if (__ipv6_addr_needs_scope_id(addr_type)) {
		if (addr_len >= sizeof(struct sockaddr_in6) &&
		    usin->sin6_scope_id) {
			if (sk->sk_bound_dev_if &&
			    sk->sk_bound_dev_if != usin->sin6_scope_id) {
				err = -EINVAL;
				goto out;
			}
			sk->sk_bound_dev_if = usin->sin6_scope_id;
		}

		if (!sk->sk_bound_dev_if && (addr_type & IPV6_ADDR_MULTICAST))
			sk->sk_bound_dev_if = np->mcast_oif;

		/* Connect to link-local address requires an interface */
		if (!sk->sk_bound_dev_if) {
			err = -EINVAL;
			goto out;
		}
	}

	np->daddr = *daddr;
	np->flow_label = fl6.flowlabel;

	inet->inet_dport = usin->sin6_port;

	/*
	 *	Check for a route to destination an obtain the
	 *	destination cache for it.
	 */

	fl6.flowi6_proto = sk->sk_protocol;
	fl6.daddr = np->daddr;
	fl6.saddr = np->saddr;
	fl6.flowi6_oif = sk->sk_bound_dev_if;
	fl6.flowi6_mark = sk->sk_mark;
	fl6.fl6_dport = inet->inet_dport;
	fl6.fl6_sport = inet->inet_sport;

	if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST))
		fl6.flowi6_oif = np->mcast_oif;

	security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));

	opt = flowlabel ? flowlabel->opt : np->opt;
	final_p = fl6_update_dst(&fl6, opt, &final);

	dst = ip6_dst_lookup_flow(sk, &fl6, final_p, true);
	err = 0;
	if (IS_ERR(dst)) {
		err = PTR_ERR(dst);
		goto out;
	}

	/* source address lookup done in ip6_dst_lookup */

	if (ipv6_addr_any(&np->saddr))
		np->saddr = fl6.saddr;

	if (ipv6_addr_any(&np->rcv_saddr)) {
		np->rcv_saddr = fl6.saddr;
		inet->inet_rcv_saddr = LOOPBACK4_IPV6;
		if (sk->sk_prot->rehash)
			sk->sk_prot->rehash(sk);
	}

	ip6_dst_store(sk, dst,
		      ipv6_addr_equal(&fl6.daddr, &np->daddr) ?
		      &np->daddr : NULL,
#ifdef CONFIG_IPV6_SUBTREES
		      ipv6_addr_equal(&fl6.saddr, &np->saddr) ?
		      &np->saddr :
#endif
		      NULL);

	sk->sk_state = TCP_ESTABLISHED;
out:
	fl6_sock_release(flowlabel);
	return err;
}
Esempio n. 26
0
static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
			    struct nl_info *info)
{
	struct rt6_info *iter = NULL;
	struct rt6_info **ins;
	int replace = (info->nlh &&
		       (info->nlh->nlmsg_flags & NLM_F_REPLACE));
	int add = (!info->nlh ||
		   (info->nlh->nlmsg_flags & NLM_F_CREATE));
	int found = 0;

	ins = &fn->leaf;

	for (iter = fn->leaf; iter; iter = iter->dst.rt6_next) {
		/*
		 *	Search for duplicates
		 */

		if (iter->rt6i_metric == rt->rt6i_metric) {
			/*
			 *	Same priority level
			 */
			if (info->nlh &&
			    (info->nlh->nlmsg_flags & NLM_F_EXCL))
				return -EEXIST;
			if (replace) {
				found++;
				break;
			}

			if (iter->dst.dev == rt->dst.dev &&
			    iter->rt6i_idev == rt->rt6i_idev &&
			    ipv6_addr_equal(&iter->rt6i_gateway,
					    &rt->rt6i_gateway)) {
				if (!(iter->rt6i_flags & RTF_EXPIRES))
					return -EEXIST;
				if (!(rt->rt6i_flags & RTF_EXPIRES))
					rt6_clean_expires(iter);
				else
					rt6_set_expires(iter, rt->dst.expires);
				return -EEXIST;
			}
		}

		if (iter->rt6i_metric > rt->rt6i_metric)
			break;

		ins = &iter->dst.rt6_next;
	}

	/* Reset round-robin state, if necessary */
	if (ins == &fn->leaf)
		fn->rr_ptr = NULL;

	/*
	 *	insert node
	 */
	if (!replace) {
		if (!add)
			pr_warn("NLM_F_CREATE should be set when creating new route\n");

add:
		rt->dst.rt6_next = iter;
		*ins = rt;
		rt->rt6i_node = fn;
		atomic_inc(&rt->rt6i_ref);
		inet6_rt_notify(RTM_NEWROUTE, rt, info);
		info->nl_net->ipv6.rt6_stats->fib_rt_entries++;

		if (!(fn->fn_flags & RTN_RTINFO)) {
			info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
			fn->fn_flags |= RTN_RTINFO;
		}

	} else {
		if (!found) {
			if (add)
				goto add;
			pr_warn("NLM_F_REPLACE set, but no existing node found!\n");
			return -ENOENT;
		}
		*ins = rt;
		rt->rt6i_node = fn;
		rt->dst.rt6_next = iter->dst.rt6_next;
		atomic_inc(&rt->rt6i_ref);
		inet6_rt_notify(RTM_NEWROUTE, rt, info);
		rt6_release(iter);
		if (!(fn->fn_flags & RTN_RTINFO)) {
			info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
			fn->fn_flags |= RTN_RTINFO;
		}
	}

	return 0;
}
int ip6_route_me_harder(struct sk_buff *skb)
{
    struct net *net = dev_net(skb_dst(skb)->dev);
    struct ipv6hdr *iph = ipv6_hdr(skb);
    struct dst_entry *dst;
    struct flowi fl = {
        .oif = skb->sk ? skb->sk->sk_bound_dev_if : 0,
        .mark = skb->mark,
        .fl6_dst = iph->daddr,
        .fl6_src = iph->saddr,
    };

    dst = ip6_route_output(net, skb->sk, &fl);
    if (dst->error) {
        IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
        LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n");
        dst_release(dst);
        return -EINVAL;
    }

    /* Drop old route. */
    skb_dst_drop(skb);

    skb_dst_set(skb, dst);

#ifdef CONFIG_XFRM
    if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
            xfrm_decode_session(skb, &fl, AF_INET6) == 0) {
        skb_dst_set(skb, NULL);
        if (xfrm_lookup(net, &dst, &fl, skb->sk, 0))
            return -1;
        skb_dst_set(skb, dst);
    }
#endif

    return 0;
}
EXPORT_SYMBOL(ip6_route_me_harder);

/*
 * Extra routing may needed on local out, as the QUEUE target never
 * returns control to the table.
 */

struct ip6_rt_info {
    struct in6_addr daddr;
    struct in6_addr saddr;
    u_int32_t mark;
};

static void nf_ip6_saveroute(const struct sk_buff *skb,
                             struct nf_queue_entry *entry)
{
    struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);

    if (entry->hook == NF_INET_LOCAL_OUT) {
        struct ipv6hdr *iph = ipv6_hdr(skb);

        rt_info->daddr = iph->daddr;
        rt_info->saddr = iph->saddr;
        rt_info->mark = skb->mark;
    }
}

static int nf_ip6_reroute(struct sk_buff *skb,
                          const struct nf_queue_entry *entry)
{
    struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);

    if (entry->hook == NF_INET_LOCAL_OUT) {
        struct ipv6hdr *iph = ipv6_hdr(skb);
        if (!ipv6_addr_equal(&iph->daddr, &rt_info->daddr) ||
                !ipv6_addr_equal(&iph->saddr, &rt_info->saddr) ||
                skb->mark != rt_info->mark)
            return ip6_route_me_harder(skb);
    }
    return 0;
}

static int nf_ip6_route(struct dst_entry **dst, struct flowi *fl)
{
    *dst = ip6_route_output(&init_net, NULL, fl);
    return (*dst)->error;
}
Esempio n. 28
0
int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
{
	int uninitialized_var(err);
	struct net *net = sock_net(sk);
	struct ipv6_pinfo *np = inet6_sk(sk);
	struct in6_flowlabel_req freq;
	struct ipv6_fl_socklist *sfl1=NULL;
	struct ipv6_fl_socklist *sfl, **sflp;
	struct ip6_flowlabel *fl, *fl1 = NULL;


	if (optlen < sizeof(freq))
		return -EINVAL;

	if (copy_from_user(&freq, optval, sizeof(freq)))
		return -EFAULT;

	switch (freq.flr_action) {
	case IPV6_FL_A_PUT:
		write_lock_bh(&ip6_sk_fl_lock);
		for (sflp = &np->ipv6_fl_list; (sfl=*sflp)!=NULL; sflp = &sfl->next) {
			if (sfl->fl->label == freq.flr_label) {
				if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK))
					np->flow_label &= ~IPV6_FLOWLABEL_MASK;
				*sflp = sfl->next;
				write_unlock_bh(&ip6_sk_fl_lock);
				fl_release(sfl->fl);
				kfree(sfl);
				return 0;
			}
		}
		write_unlock_bh(&ip6_sk_fl_lock);
		return -ESRCH;

	case IPV6_FL_A_RENEW:
		read_lock_bh(&ip6_sk_fl_lock);
		for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
			if (sfl->fl->label == freq.flr_label) {
				err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);
				read_unlock_bh(&ip6_sk_fl_lock);
				return err;
			}
		}
		read_unlock_bh(&ip6_sk_fl_lock);

		if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {
			fl = fl_lookup(net, freq.flr_label);
			if (fl) {
				err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
				fl_release(fl);
				return err;
			}
		}
		return -ESRCH;

	case IPV6_FL_A_GET:
		if (freq.flr_label & ~IPV6_FLOWLABEL_MASK)
			return -EINVAL;

		fl = fl_create(net, sk, &freq, optval, optlen, &err);
		if (fl == NULL)
			return err;
		sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);

		if (freq.flr_label) {
			err = -EEXIST;
			read_lock_bh(&ip6_sk_fl_lock);
			for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
				if (sfl->fl->label == freq.flr_label) {
					if (freq.flr_flags&IPV6_FL_F_EXCL) {
						read_unlock_bh(&ip6_sk_fl_lock);
						goto done;
					}
					fl1 = sfl->fl;
					atomic_inc(&fl1->users);
					break;
				}
			}
			read_unlock_bh(&ip6_sk_fl_lock);

			if (fl1 == NULL)
				fl1 = fl_lookup(net, freq.flr_label);
			if (fl1) {
recheck:
				err = -EEXIST;
				if (freq.flr_flags&IPV6_FL_F_EXCL)
					goto release;
				err = -EPERM;
				if (fl1->share == IPV6_FL_S_EXCL ||
				    fl1->share != fl->share ||
				    fl1->owner != fl->owner)
					goto release;

				err = -EINVAL;
				if (!ipv6_addr_equal(&fl1->dst, &fl->dst) ||
				    ipv6_opt_cmp(fl1->opt, fl->opt))
					goto release;

				err = -ENOMEM;
				if (sfl1 == NULL)
					goto release;
				if (fl->linger > fl1->linger)
					fl1->linger = fl->linger;
				if ((long)(fl->expires - fl1->expires) > 0)
					fl1->expires = fl->expires;
				fl_link(np, sfl1, fl1);
				fl_free(fl);
				return 0;

release:
				fl_release(fl1);
				goto done;
			}
		}
		err = -ENOENT;
		if (!(freq.flr_flags&IPV6_FL_F_CREATE))
			goto done;

		err = -ENOMEM;
		if (sfl1 == NULL || (err = mem_check(sk)) != 0)
			goto done;

		fl1 = fl_intern(net, fl, freq.flr_label);
		if (fl1 != NULL)
			goto recheck;

		if (!freq.flr_label) {
			if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label,
					 &fl->label, sizeof(fl->label))) {
				/* Intentionally ignore fault. */
			}
		}

		fl_link(np, sfl1, fl);
		return 0;

	default:
		return -EINVAL;
	}

done:
	fl_free(fl);
	kfree(sfl1);
	return err;
}
Esempio n. 29
0
static int
ip6mr_cache_unresolved(mifi_t mifi, struct sk_buff *skb)
{
	int err;
	struct mfc6_cache *c;

	spin_lock_bh(&mfc_unres_lock);
	for (c = mfc_unres_queue; c; c = c->next) {
		if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) &&
		    ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr))
			break;
	}

	if (c == NULL) {
		/*
		 *	Create a new entry if allowable
		 */

		if (atomic_read(&cache_resolve_queue_len) >= 10 ||
		    (c = ip6mr_cache_alloc_unres()) == NULL) {
			spin_unlock_bh(&mfc_unres_lock);

			kfree_skb(skb);
			return -ENOBUFS;
		}

		/*
		 *	Fill in the new cache entry
		 */
		c->mf6c_parent = -1;
		c->mf6c_origin = ipv6_hdr(skb)->saddr;
		c->mf6c_mcastgrp = ipv6_hdr(skb)->daddr;

		/*
		 *	Reflect first query at pim6sd
		 */
		if ((err = ip6mr_cache_report(skb, mifi, MRT6MSG_NOCACHE)) < 0) {
			/* If the report failed throw the cache entry
			   out - Brad Parker
			 */
			spin_unlock_bh(&mfc_unres_lock);

			kmem_cache_free(mrt_cachep, c);
			kfree_skb(skb);
			return err;
		}

		atomic_inc(&cache_resolve_queue_len);
		c->next = mfc_unres_queue;
		mfc_unres_queue = c;

		ipmr_do_expire_process(1);
	}

	/*
	 *	See if we can append the packet
	 */
	if (c->mfc_un.unres.unresolved.qlen > 3) {
		kfree_skb(skb);
		err = -ENOBUFS;
	} else {
		skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
		err = 0;
	}

	spin_unlock_bh(&mfc_unres_lock);
	return err;
}
Esempio n. 30
0
int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
{
	int err = 0;

	*dst = NULL;
	if (sk) {
		struct ipv6_pinfo *np = inet6_sk(sk);
	
		*dst = sk_dst_check(sk, np->dst_cookie);
		if (*dst) {
			struct rt6_info *rt = (struct rt6_info*)*dst;
	
				/* Yes, checking route validity in not connected
				   case is not very simple. Take into account,
				   that we do not support routing by source, TOS,
				   and MSG_DONTROUTE 		--ANK (980726)
	
				   1. If route was host route, check that
				      cached destination is current.
				      If it is network route, we still may
				      check its validity using saved pointer
				      to the last used address: daddr_cache.
				      We do not want to save whole address now,
				      (because main consumer of this service
				       is tcp, which has not this problem),
				      so that the last trick works only on connected
				      sockets.
				   2. oif also should be the same.
				 */
	
			if (((rt->rt6i_dst.plen != 128 ||
			      !ipv6_addr_equal(&fl->fl6_dst, &rt->rt6i_dst.addr))
			     && (np->daddr_cache == NULL ||
				 !ipv6_addr_equal(&fl->fl6_dst, np->daddr_cache)))
			    || (fl->oif && fl->oif != (*dst)->dev->ifindex)) {
				dst_release(*dst);
				*dst = NULL;
			}
		}
	}

	if (*dst == NULL)
		*dst = ip6_route_output(sk, fl);

	if ((err = (*dst)->error))
		goto out_err_release;

	if (ipv6_addr_any(&fl->fl6_src)) {
		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);

		if (err) {
#if IP6_DEBUG >= 2
			printk(KERN_DEBUG "ip6_dst_lookup: "
			       "no available source address\n");
#endif
			goto out_err_release;
		}
	}

	return 0;

out_err_release:
	dst_release(*dst);
	*dst = NULL;
	return err;
}