Пример #1
0
static int mipv6_mn_tunnel_rcv_send_bu_hook(
	struct ipv6_tunnel *t, struct sk_buff *skb, __u32 flags)
{
	struct ipv6hdr *inner = (struct ipv6hdr *)skb->h.raw;
	struct ipv6hdr *outer = skb->nh.ipv6h; 
	struct mn_info *minfo = NULL;
	__u32 lifetime;

	DEBUG_FUNC();
	if (!(flags & IPV6_T_F_MIPV6_DEV))
		return IPV6_TUNNEL_ACCEPT;

	read_lock(&mn_info_lock);
	minfo = mipv6_mn_get_info(&inner->daddr);

	if (!minfo) {
		DEBUG((DBG_WARNING, "MN info missing"));
		read_unlock(&mn_info_lock);
		return IPV6_TUNNEL_ACCEPT;
	}
	/* We don't send bus in response to all tunneled packets */

        if (!ipv6_addr_cmp(&minfo->ha, &inner->saddr)) {
                DEBUG((DBG_ERROR, "HA BUG: Received a tunneled packet "
		       "originally sent by home agent, not sending BU"));
		read_unlock(&mn_info_lock);
		return IPV6_TUNNEL_ACCEPT;
        }
	if (ipv6_addr_cmp(&minfo->ha, &outer->saddr)) {
		DEBUG((DBG_WARNING, "MIPV6 MN: Received a tunneled IPv6 packet"
		       " that was not tunneled by HA %x:%x:%x:%x:%x:%x:%x:%x,"
		       " but by %x:%x:%x:%x:%x:%x:%x:%x", 
		       NIPV6ADDR(&minfo->ha), NIPV6ADDR(&outer->saddr)));
		read_unlock(&mn_info_lock);
		return IPV6_TUNNEL_ACCEPT;
        }
	read_unlock(&mn_info_lock);

	DEBUG((DBG_INFO, "Sending BU to correspondent node"));

	if (inner->nexthdr != IPPROTO_DSTOPTS) {
		DEBUG((DBG_INFO, "Received tunneled packet without dst_opts"));
		lifetime = mipv6_mn_get_bulifetime(&inner->daddr,
						   &outer->daddr, 0); 
		if(lifetime)
		/* max wait 1000 ms  before sending an empty packet with BU */
			mipv6_send_upd_option(&inner->daddr,&inner->saddr,
					      CN_BU_DELAY, 
					      INITIAL_BINDACK_TIMEOUT,
					      MAX_BINDACK_TIMEOUT , 1, 
#ifdef CN_REQ_ACK
					      MIPV6_BU_F_ACK, /* ack */
#else
					      0, /* no ack */
#endif
					      0, lifetime, NULL);
	}
	/* (Mis)use ipsec tunnel flag  */
	DEBUG((DBG_INFO, "setting rcv_tunnel flag in skb"));
	skb->security = skb->security | RCV_TUNNEL;
	return IPV6_TUNNEL_ACCEPT;
}
Пример #2
0
/**
 * mipv6_mobile_node_moved - Sends BUs to all HAs and CNs
 * @ho: handoff structure contains the new and previous routers
 **/  
int mipv6_mobile_node_moved(struct handoff *ho)
{
	struct mn_info *hinfo = NULL;
	unsigned long lifetime = 1234;
	int bu_to_prev_router = 1;
	int dummy;

	DEBUG_FUNC();
	if (!mipv6_is_initialized)
		return 0;

	ma_ctl_upd_iface(ho->rtr_new.ifindex, 
			 MA_IFACE_CURRENT | MA_IFACE_HAS_ROUTER, &dummy);

	/* First send BUs to all nodes which are on BU list */
	bul_iterate(mn_handoff, (void *)ho);

	/* Then go through the home infos to see if there's a new one
	 * which has not been registered
	 */
	/* TODO: Should the previous router acting as a HA and the
	 * previous CoA be also added to the mn_info list
	 */
	read_lock_bh(&mn_info_lock);
	for (hinfo = mn_info_base; hinfo; hinfo = hinfo->next) {
		if (mipv6_prefix_compare(&hinfo->home_addr, 
					&ho->rtr_new.raddr, 
					 hinfo->home_plen)) {
			if (!hinfo->is_at_home) 
				hinfo->is_at_home = 1;
		}
		else {
			hinfo->is_at_home = 0;
			if (!hinfo->has_home_reg) {
				init_home_registration(hinfo, 
						       &ho->rtr_new.CoA);
			}
		}
		/* Check here whether the previous CoA is in fact a regular 
		 * home address, which is being registered in any case
		 * TODO: Add a mn_info for the previous router and purge it 
		 * or do DHAAD after the lifetime of the HA expires. Remove the
		 * comparison between home agent address and prev. router address 
		 * after bul can handle multiple entries for same CN!
		 */
		if (ho->has_rtr_prev && (!ipv6_addr_cmp(&hinfo->home_addr, 
							&ho->rtr_prev.CoA) || 
					 !ipv6_addr_cmp(&hinfo->ha, &ho->rtr_prev.raddr))) 
			
			bu_to_prev_router = 0;
	}
	read_unlock_bh(&mn_info_lock);		     
	
	
	/* Send BU to previous router if it acts as a HA 
	 * TODO: Should the previous CoA be added to the mn_info list?
	 */
	
	if (ho->has_rtr_prev && ho->rtr_prev.flags & ND_RA_FLAG_HA && 
		    ho->rtr_prev.glob_addr && bu_to_prev_router) {
		lifetime = mipv6_mn_get_bulifetime(&ho->rtr_prev.CoA, 
						   &ho->rtr_new.CoA, 
						   MIPV6_BU_F_HOME | 
						   MIPV6_BU_F_ACK);
		mipv6_send_upd_option(
			&ho->rtr_prev.CoA, &ho->rtr_prev.raddr, HA_BU_DELAY, 
			INITIAL_BINDACK_DAD_TIMEOUT, MAX_BINDACK_TIMEOUT, 1,
			MIPV6_BU_F_HOME | MIPV6_BU_F_ACK, 0, lifetime, NULL);
		}
	
	return 0;		
}
Пример #3
0
/* Grrr, addr_type already calculated by caller, but I don't want
 * to add some silly "cookie" argument to this method just for that.
 */
static int udp_v6_get_port(struct sock *sk, unsigned short snum)
{
    write_lock_bh(&udp_hash_lock);
    if (snum == 0) {
        int best_size_so_far, best, result, i;

        if (udp_port_rover > sysctl_local_port_range[1] ||
                udp_port_rover < sysctl_local_port_range[0])
            udp_port_rover = sysctl_local_port_range[0];
        best_size_so_far = 32767;
        best = result = udp_port_rover;
        for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
            struct sock *sk;
            int size;

            sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
            if (!sk) {
                if (result > sysctl_local_port_range[1])
                    result = sysctl_local_port_range[0] +
                             ((result - sysctl_local_port_range[0]) &
                              (UDP_HTABLE_SIZE - 1));
                goto gotit;
            }
            size = 0;
            do {
                if (++size >= best_size_so_far)
                    goto next;
            } while ((sk = sk->next) != NULL);
            best_size_so_far = size;
            best = result;
next:
            ;
        }
        result = best;
        for(;; result += UDP_HTABLE_SIZE) {
            if (result > sysctl_local_port_range[1])
                result = sysctl_local_port_range[0]
                         + ((result - sysctl_local_port_range[0]) &
                            (UDP_HTABLE_SIZE - 1));
            if (!udp_lport_inuse(result))
                break;
        }
gotit:
        udp_port_rover = snum = result;
    } else {
        struct sock *sk2;
        int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);

        for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
                sk2 != NULL;
                sk2 = sk2->next) {
            if (sk2->num == snum &&
                    sk2 != sk &&
                    sk2->bound_dev_if == sk->bound_dev_if &&
                    (!sk2->rcv_saddr ||
                     addr_type == IPV6_ADDR_ANY ||
                     !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
                                    &sk2->net_pinfo.af_inet6.rcv_saddr) ||
                     (addr_type == IPV6_ADDR_MAPPED &&
                      sk2->family == AF_INET &&
                      sk->rcv_saddr == sk2->rcv_saddr)) &&
                    (!sk2->reuse || !sk->reuse))
                goto fail;
        }
    }

    sk->num = snum;
    if (sk->pprev == NULL) {
        struct sock **skp = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
        if ((sk->next = *skp) != NULL)
            (*skp)->pprev = &sk->next;
        *skp = sk;
        sk->pprev = skp;
        sock_prot_inc_use(sk->prot);
        sock_hold(sk);
    }
    write_unlock_bh(&udp_hash_lock);
    return 0;

fail:
    write_unlock_bh(&udp_hash_lock);
    return 1;
}
Пример #4
0
static int
hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
	       const struct xt_action_param *par,
	       enum ipset_adt adt, const struct ip_set_adt_opt *opt)
{
	const struct ip_set_hash *h = set->data;
	ipset_adtfn adtfn = set->variant->adt[adt];
	struct hash_net4_elem data = {
		.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
	};

	if (data.cidr == 0)
		return -EINVAL;
	if (adt == IPSET_TEST)
		data.cidr = HOST_MASK;

	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip);
	data.ip &= ip_set_netmask(data.cidr);

	return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags);
}

static int
hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
	       enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
	const struct ip_set_hash *h = set->data;
	ipset_adtfn adtfn = set->variant->adt[adt];
	struct hash_net4_elem data = { .cidr = HOST_MASK };
	u32 timeout = h->timeout;
	u32 ip = 0, ip_to, last;
	int ret;

	if (unlikely(!tb[IPSET_ATTR_IP] ||
		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
		return -IPSET_ERR_PROTOCOL;

	if (tb[IPSET_ATTR_LINENO])
		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);

	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
	if (ret)
		return ret;

	if (tb[IPSET_ATTR_CIDR]) {
		data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
		if (!data.cidr || data.cidr > HOST_MASK)
			return -IPSET_ERR_INVALID_CIDR;
	}

	if (tb[IPSET_ATTR_TIMEOUT]) {
		if (!with_timeout(h->timeout))
			return -IPSET_ERR_TIMEOUT;
		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
	}

	if (tb[IPSET_ATTR_CADT_FLAGS] && adt == IPSET_ADD) {
		u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
		if (cadt_flags & IPSET_FLAG_NOMATCH)
			flags |= (cadt_flags << 16);
	}

	if (adt == IPSET_TEST || !tb[IPSET_ATTR_IP_TO]) {
		data.ip = htonl(ip & ip_set_hostmask(data.cidr));
		ret = adtfn(set, &data, timeout, flags);
		return ip_set_eexist(ret, flags) ? 0 : ret;
	}

	ip_to = ip;
	if (tb[IPSET_ATTR_IP_TO]) {
		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
		if (ret)
			return ret;
		if (ip_to < ip)
			swap(ip, ip_to);
		if (ip + UINT_MAX == ip_to)
			return -IPSET_ERR_HASH_RANGE;
	}
	if (retried)
		ip = ntohl(h->next.ip);
	while (!after(ip, ip_to)) {
		data.ip = htonl(ip);
		last = ip_set_range_to_cidr(ip, ip_to, &data.cidr);
		ret = adtfn(set, &data, timeout, flags);
		if (ret && !ip_set_eexist(ret, flags))
			return ret;
		else
			ret = 0;
		ip = last + 1;
	}
	return ret;
}

static bool
hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
{
	const struct ip_set_hash *x = a->data;
	const struct ip_set_hash *y = b->data;

	/* Resizing changes htable_bits, so we ignore it */
	return x->maxelem == y->maxelem &&
	       x->timeout == y->timeout;
}

/* The type variant functions: IPv6 */

struct hash_net6_elem {
	union nf_inet_addr ip;
	u16 padding0;
	u8 nomatch;
	u8 cidr;
};

struct hash_net6_telem {
	union nf_inet_addr ip;
	u16 padding0;
	u8 nomatch;
	u8 cidr;
	unsigned long timeout;
};

static inline bool
hash_net6_data_equal(const struct hash_net6_elem *ip1,
		     const struct hash_net6_elem *ip2,
		     u32 *multi)
{
	return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
	       ip1->cidr == ip2->cidr;
}

static inline bool
hash_net6_data_isnull(const struct hash_net6_elem *elem)
{
	return elem->cidr == 0;
}

static inline void
hash_net6_data_copy(struct hash_net6_elem *dst,
		    const struct hash_net6_elem *src)
{
	dst->ip.in6 = src->ip.in6;
	dst->cidr = src->cidr;
	dst->nomatch = src->nomatch;
}
Пример #5
0
static int
__xfrm6_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
		      struct flowi *fl, struct dst_entry **dst_p)
{
	struct dst_entry *dst, *dst_prev;
	struct rt6_info *rt0 = (struct rt6_info*)(*dst_p);
	struct rt6_info *rt  = rt0;
	struct in6_addr *remote = &fl->fl6_dst;
	struct in6_addr *local  = &fl->fl6_src;
	int i;
	int err = 0;
	int header_len = 0;
	int trailer_len = 0;

	dst = dst_prev = NULL;

	for (i = 0; i < nx; i++) {
		struct dst_entry *dst1 = dst_alloc(&xfrm6_dst_ops);

		if (unlikely(dst1 == NULL)) {
			err = -ENOBUFS;
			goto error;
		}

		if (!dst)
			dst = dst1;
		else {
			dst_prev->child = dst1;
			dst1->flags |= DST_NOHASH;
			dst_clone(dst1);
		}
		dst_prev = dst1;
		if (xfrm[i]->props.mode) {
			remote = (struct in6_addr*)&xfrm[i]->id.daddr;
			local  = (struct in6_addr*)&xfrm[i]->props.saddr;
		}
		header_len += xfrm[i]->props.header_len;
		trailer_len += xfrm[i]->props.trailer_len;
	}

	if (ipv6_addr_cmp(remote, &fl->fl6_dst)) {
		struct flowi fl_tunnel;

		memset(&fl_tunnel, 0, sizeof(fl_tunnel));
		ipv6_addr_copy(&fl_tunnel.fl6_dst, remote);
		ipv6_addr_copy(&fl_tunnel.fl6_src, local);

		err = xfrm_dst_lookup((struct xfrm_dst **) &rt,
				      &fl_tunnel, AF_INET6);
		if (err)
			goto error;
	} else {
		dst_hold(&rt->u.dst);
	}
	dst_prev->child = &rt->u.dst;
	i = 0;
	for (dst_prev = dst; dst_prev != &rt->u.dst; dst_prev = dst_prev->child) {
		struct xfrm_dst *x = (struct xfrm_dst*)dst_prev;

		dst_prev->xfrm = xfrm[i++];
		dst_prev->dev = rt->u.dst.dev;
		if (rt->u.dst.dev)
			dev_hold(rt->u.dst.dev);
		dst_prev->obsolete	= -1;
		dst_prev->flags	       |= DST_HOST;
		dst_prev->lastuse	= jiffies;
		dst_prev->header_len	= header_len;
		dst_prev->trailer_len	= trailer_len;
		memcpy(&dst_prev->metrics, &rt->u.dst.metrics, sizeof(dst_prev->metrics));
		dst_prev->path		= &rt->u.dst;

		/* Copy neighbour for reachability confirmation */
		dst_prev->neighbour	= neigh_clone(rt->u.dst.neighbour);
		dst_prev->input		= rt->u.dst.input;
		dst_prev->output	= dst_prev->xfrm->type->output;
		/* Sheit... I remember I did this right. Apparently,
		 * it was magically lost, so this code needs audit */
		x->u.rt6.rt6i_flags    = rt0->rt6i_flags&(RTCF_BROADCAST|RTCF_MULTICAST|RTCF_LOCAL);
		x->u.rt6.rt6i_metric   = rt0->rt6i_metric;
		x->u.rt6.rt6i_node     = rt0->rt6i_node;
		x->u.rt6.rt6i_gateway  = rt0->rt6i_gateway;
		memcpy(&x->u.rt6.rt6i_gateway, &rt0->rt6i_gateway, sizeof(x->u.rt6.rt6i_gateway)); 
		x->u.rt6.rt6i_dst      = rt0->rt6i_dst;
		x->u.rt6.rt6i_src      = rt0->rt6i_src;	
		header_len -= x->u.dst.xfrm->props.header_len;
		trailer_len -= x->u.dst.xfrm->props.trailer_len;
	}
	*dst_p = dst;
	return 0;

error:
	if (dst)
		dst_free(dst);
	return err;
}
Пример #6
0
int ipv6_flowlabel_opt(struct sock *sk, char *optval, int optlen)
{
	int err;
	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
	struct in6_flowlabel_req freq;
	struct ipv6_fl_socklist *sfl1=NULL;
	struct ipv6_fl_socklist *sfl, **sflp;
	struct ip6_flowlabel *fl;

	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(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(&freq, optval, optlen, &err);
		if (fl == NULL)
			return err;
		sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);

		if (freq.flr_label) {
			struct ip6_flowlabel *fl1 = NULL;

			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(&fl->users);
					break;
				}
			}
			read_unlock_bh(&ip6_sk_fl_lock);

			if (fl1 == NULL)
				fl1 = fl_lookup(freq.flr_label);
			if (fl1) {
				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_cmp(&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;
				write_lock_bh(&ip6_sk_fl_lock);
				sfl1->fl = fl1;
				sfl1->next = np->ipv6_fl_list;
				np->ipv6_fl_list = sfl1;
				write_unlock_bh(&ip6_sk_fl_lock);
				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;

		err = fl_intern(fl, freq.flr_label);
		if (err)
			goto done;

		/* Do not check for fault */
		if (!freq.flr_label)
			copy_to_user(optval + ((u8*)&freq.flr_label - (u8*)&freq), &fl->label, sizeof(fl->label));

		sfl1->fl = fl;
		sfl1->next = np->ipv6_fl_list;
		np->ipv6_fl_list = sfl1;
		return 0;

	default:
		return -EINVAL;
	}

done:
	if (fl)
		fl_free(fl);
	if (sfl1)
		kfree(sfl1);
	return err;
}
Пример #7
0
static bool
ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
	const struct ebt_ip6_info *info = par->matchinfo;
	const struct ipv6hdr *ih6;
	struct ipv6hdr _ip6h;
	const struct tcpudphdr *pptr;
	struct tcpudphdr _ports;	
	struct in6_addr tmp_addr;
	int i;

#if 1
	if(skb->protocol == htons(ETH_P_IPV6))
		ih6 = (struct ipv6hdr *)skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h);
	else if((skb->protocol == htons(ETH_P_8021Q)) && (vlan_proto(skb) == htons(ETH_P_IPV6)))
		ih6 = (struct ipv6hdr *)(skb_mac_header(skb) + VLAN_ETH_HLEN);
	else if((skb->protocol == htons(ETH_P_PPP_SES)) && (pppoe_proto(skb) == htons(0x0021)))
		ih6 = (struct ipv6hdr *)(skb_mac_header(skb) + ETH_HLEN +PPPOE_SES_HLEN);
	else
		ih6 = (struct ipv6hdr *)skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h);
#else
	ih6 = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h);
#endif
	
	if (ih6 == NULL)
		return false;
	
#if 1
	if (info->bitmask & EBT_IP6_TCLASS) {
		__u8 tc = ipv6_get_dsfield((struct ipv6hdr *)ih6);
	   if (FWINV(tc < info->tclass[0] || 
	   		 tc > info->tclass[1], EBT_IP6_TCLASS))
		return false;
	}
#else
	if (info->bitmask & EBT_IP6_TCLASS &&
	   FWINV(info->tclass != ipv6_get_dsfield((struct ipv6hdr *)ih6), EBT_IP6_TCLASS))
		//return false;
		return false;

#endif

#if 1
	for (i = 0; i < 4; i++)
		tmp_addr.in6_u.u6_addr32[i] = ih6->saddr.in6_u.u6_addr32[i] &
			info->smsk.in6_u.u6_addr32[i];
	if (info->bitmask & EBT_IP6_SOURCE &&
		FWINV((ipv6_addr_cmp(&tmp_addr, &info->saddr) != 0),
			EBT_IP6_SOURCE))
		return false;

	for (i = 0; i < 4; i++)
		tmp_addr.in6_u.u6_addr32[i] = ih6->daddr.in6_u.u6_addr32[i] &
			info->dmsk.in6_u.u6_addr32[i];
	if (info->bitmask & EBT_IP6_DEST &&
	   FWINV((ipv6_addr_cmp(&tmp_addr, &info->daddr) != 0), EBT_IP6_DEST))
		return false;
#else
	if (FWINV(ipv6_masked_addr_cmp(&ih6->saddr, &info->smsk,
				       &info->saddr), EBT_IP6_SOURCE) ||
	    FWINV(ipv6_masked_addr_cmp(&ih6->daddr, &info->dmsk,
				       &info->daddr), EBT_IP6_DEST))
		return false;	
#endif

	if (info->bitmask & EBT_IP6_PROTO) {
		uint8_t nexthdr = ih6->nexthdr;
		int offset_ph;

		offset_ph = ipv6_skip_exthdr(skb, sizeof(_ip6h), &nexthdr);
		if (offset_ph == -1)
			return false;
#if 1
		if ( FWINV((info->protocol[0] != nexthdr) && (info->protocol[1] != nexthdr), EBT_IP6_PROTO) )
			return false;
#else
		if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO))
			return false;
	
#endif
		if (!(info->bitmask & EBT_IP6_DPORT) &&
		    !(info->bitmask & EBT_IP6_SPORT)){		    
			return true;
		}
#if 1
		if(skb->protocol == htons(ETH_P_IP))
			pptr = (struct tcpudphdr *)skb_header_pointer(skb, offset_ph, sizeof(_ports), &_ports);
		else if((skb->protocol == htons(ETH_P_8021Q)) && (vlan_proto(skb) == htons(ETH_P_IP)))
			pptr = (struct tcpudphdr *)(skb_mac_header(skb) + VLAN_ETH_HLEN + offset_ph);
		else if((skb->protocol == htons(ETH_P_PPP_SES)) && (pppoe_proto(skb) == htons(0x0021)))
			pptr = (struct tcpudphdr *)(skb_mac_header(skb) + ETH_HLEN + PPPOE_SES_HLEN + offset_ph);
		else
			pptr = (struct tcpudphdr *)skb_header_pointer(skb, offset_ph, sizeof(_ports), &_ports);
#else
		pptr = skb_header_pointer(skb, offset_ph, sizeof(_ports),
					  &_ports);
#endif
		if (pptr == NULL)
			return false;
		if (info->bitmask & EBT_IP6_DPORT) {
			u32 dst = ntohs(pptr->dst);
			if (FWINV(dst < info->dport[0] ||
				  dst > info->dport[1], EBT_IP6_DPORT))
				return false;
		}

		if (info->bitmask & EBT_IP6_SPORT) {
			u32 src = ntohs(pptr->src);
			if (FWINV(src < info->sport[0] ||
				  src > info->sport[1], EBT_IP6_SPORT))
			return false;
		}
		return true;
	}
	return true;
}
Пример #8
0
int igmp6_event_report(struct sk_buff *skb)
{
	struct ifmcaddr6 *ma;
	struct in6_addr *addrp;
	struct inet6_dev *idev;
	struct icmp6hdr *hdr;
#ifdef CONFIG_IPV6_MLD6_DEBUG
	char abuf1[128], abuf2[128];
	unsigned long resptime;

	in6_ntop(&skb->nh.ipv6h->saddr, abuf1);
	in6_ntop(&skb->nh.ipv6h->daddr, abuf2);
	MDBG((KERN_DEBUG
		"igmp6_event_report(skb=%p): saddr=%s, daddr=%s\n",
		skb, abuf1, abuf2));
#endif

	/* Our own report looped back. Ignore it. */
	if (skb->pkt_type == PACKET_LOOPBACK)
		return 0;

	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
		return -EINVAL;

	hdr = (struct icmp6hdr*) skb->h.raw;

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

#ifdef CONFIG_IPV6_MLD6_DEBUG
	resptime = ntohs(hdr->icmp6_maxdelay);
#endif
	addrp = (struct in6_addr *) (hdr + 1);

#ifdef CONFIG_IPV6_MLD6_DEBUG
	in6_ntop(addrp, abuf1);
	MDBG3((KERN_DEBUG
		"igmp6_event_report(): maxdelay=%lu, addr=%s\n",
		resptime, abuf1));
#endif

	if (!ipv6_addr_is_multicast(addrp))
		goto drop;

	idev = in6_dev_get(skb->dev);
	if (idev == NULL)
		return -ENODEV;

	/*
	 *	Cancel the timer for this group
	 */

	read_lock(&idev->lock);
	for (ma = idev->mc_list; ma; ma=ma->next) {
		if (ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) {
			spin_lock(&ma->mca_lock);
			if (del_timer(&ma->mca_timer))
				atomic_dec(&ma->mca_refcnt);
#ifndef CONFIG_IPV6_MLD6_ALL_DONE
			ma->mca_flags &= ~(MAF_LAST_REPORTER|MAF_TIMER_RUNNING);
#else
			ma->mca_flags &= ~MAF_TIMER_RUNNING;
#endif
			spin_unlock(&ma->mca_lock);
			break;
		}
	}
	read_unlock(&idev->lock);
	in6_dev_put(idev);
  drop:
	return 0;
}
Пример #9
0
int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
	struct sockaddr_in6	*usin = (struct sockaddr_in6 *) uaddr;
	struct ipv6_pinfo      	*np = &sk->net_pinfo.af_inet6;
	struct in6_addr		*daddr;
	struct in6_addr		saddr;
	struct dst_entry	*dst;
	struct flowi		fl;
	struct ip6_flowlabel	*flowlabel = NULL;
	int			addr_type;
	int			err;
	int			reroute = 0;

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

	if (addr_len < SIN6_LEN_RFC2133)
	  	return -EINVAL;

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

	fl.fl6_flowlabel = 0;
	if (np->sndflow) {
		fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
		if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
			flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
			if (flowlabel == NULL)
				return -EINVAL;
			ipv6_addr_copy(&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))
			return -ENETUNREACH;

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

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

ipv4_connected:
		if (err < 0)
			return err;
		
		ipv6_addr_set(&np->daddr, 0, 0, 
			      htonl(0x0000ffff),
			      sk->daddr);

		if(ipv6_addr_any(&np->saddr)) {
			ipv6_addr_set(&np->saddr, 0, 0, 
				      htonl(0x0000ffff),
				      sk->saddr);
		}

		if(ipv6_addr_any(&np->rcv_saddr)) {
			ipv6_addr_set(&np->rcv_saddr, 0, 0, 
				      htonl(0x0000ffff),
				      sk->rcv_saddr);
		}
		return 0;
	}

	if (addr_type&IPV6_ADDR_LINKLOCAL) {
		if (addr_len >= sizeof(struct sockaddr_in6) &&
		    usin->sin6_scope_id) {
			if (sk->bound_dev_if && sk->bound_dev_if != usin->sin6_scope_id) {
				fl6_sock_release(flowlabel);
				return -EINVAL;
			}
			sk->bound_dev_if = usin->sin6_scope_id;
			if (!sk->bound_dev_if && (addr_type&IPV6_ADDR_MULTICAST))
				fl.oif = np->mcast_oif;
		}

#ifndef CONFIG_IPV6_LOOSE_SCOPE_ID
		/* Connect to link-local address requires an interface */
		if (sk->bound_dev_if == 0){
			fl6_sock_release(flowlabel);
			return -EINVAL;
		}
#endif
	}

	ipv6_addr_copy(&np->daddr, daddr);
	np->flow_label = fl.fl6_flowlabel;

	sk->dport = usin->sin6_port;

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

	fl.proto = IPPROTO_UDP;
	fl.fl6_dst = &np->daddr;
	fl.fl6_src = &saddr;
	fl.oif = sk->bound_dev_if;
	fl.uli_u.ports.dport = sk->dport;
	fl.uli_u.ports.sport = sk->sport;

	if (!fl.oif && (addr_type&IPV6_ADDR_MULTICAST))
		fl.oif = np->mcast_oif;

	if (flowlabel) {
		if (flowlabel->opt && flowlabel->opt->srcrt) {
			struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
			fl.fl6_dst = rt0->addr;
		}
	} else if (np->opt && np->opt->srcrt) {
		struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
		fl.fl6_dst = rt0->addr;
	}

	dst = ip6_route_output(sk, &fl);

	if ((err = dst->error) != 0) {
		dst_release(dst);
		fl6_sock_release(flowlabel);
		return err;
	}

	/* get the source adddress used in the apropriate device */

	err = ipv6_get_saddr(dst, daddr, &saddr, np->use_tempaddr);

	if (reroute)
		dst_release(dst);

	if (err == 0) {
#ifdef CONFIG_IPV6_SUBTREES
		if (reroute) {
			dst = ip6_route_output(sk, &fl);
			if ((err = dst->error) != 0) {
				dst_release(dst);
				fl6_sock_release(flowlabel);
				return err; 
			} 
		}
#endif
		if(ipv6_addr_any(&np->saddr))
			ipv6_addr_copy(&np->saddr, &saddr);

		if(ipv6_addr_any(&np->rcv_saddr)) {
			ipv6_addr_copy(&np->rcv_saddr, &saddr);
			sk->rcv_saddr = LOOPBACK4_IPV6;
		}
		ip6_dst_store(sk, dst, &np->daddr, 
			      !ipv6_addr_cmp(fl.fl6_src, &np->saddr) ?
			      &np->saddr : NULL);
		sk->state = TCP_ESTABLISHED;
	}
	fl6_sock_release(flowlabel);

	return err;
}
Пример #10
0
/* Grrr, addr_type already calculated by caller, but I don't want
 * to add some silly "cookie" argument to this method just for that.
 */
static int udp_v6_get_port(struct sock *sk, unsigned short snum)
{
	write_lock_bh(&udp_hash_lock);
	if (snum == 0) {
		int best_size_so_far, best, result, i;

		if (udp_port_rover > sysctl_local_port_range[1] ||
		    udp_port_rover < sysctl_local_port_range[0])
			udp_port_rover = sysctl_local_port_range[0];
		best_size_so_far = 32767;
		best = result = udp_port_rover;
		for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
			struct sock *sk;
			int size;

			sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
			if (!sk) {
				if (result > sysctl_local_port_range[1])
					result = sysctl_local_port_range[0] +
						((result - sysctl_local_port_range[0]) &
						 (UDP_HTABLE_SIZE - 1));
				goto gotit;
			}
			size = 0;
			do {
				if (++size >= best_size_so_far)
					goto next;
			} while ((sk = sk->next) != NULL);
			best_size_so_far = size;
			best = result;
		next:;
		}
		result = best;
		for(;; result += UDP_HTABLE_SIZE) {
			if (result > sysctl_local_port_range[1])
				result = sysctl_local_port_range[0]
					+ ((result - sysctl_local_port_range[0]) &
					   (UDP_HTABLE_SIZE - 1));
			if (!udp_lport_inuse(result))
				break;
		}
gotit:
		udp_port_rover = snum = result;
	} else {
		struct sock *sk2;
		int sk_reuse, sk2_reuse;
		int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr),
		    addr_type2;
#if defined(CONFIG_NET_RESTRICTED_REUSE) || defined(CONFIG_IPV6_RESTRICTED_DOUBLE_BIND)
		uid_t sk_uid = sock_i_uid_t(sk),
		      sk2_uid;
#endif

		sk_reuse = 0;
		if (sk->reuse)
			sk_reuse |= 1;
#ifdef SO_REUSEPORT
		if (sk->reuseport)
			sk_reuse |= 2;
#endif
		if (sk_reuse &&
		    (addr_type != IPV6_ADDR_MAPPED ? (addr_type & IPV6_ADDR_MULTICAST) : MULTICAST(sk->rcv_saddr)))
			sk_reuse |= 4;

		for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
		     sk2 != NULL;
		     sk2 = sk2->next) {
#if 1	/* XXX: should be recoded like 2.4.21 */
#if defined(CONFIG_NET_RESTRICTED_REUSE) || defined(CONFIG_IPV6_RESTRICTED_DOUBLE_BIND)
			int uid_ok;
#endif
			int both_specified = 0;

			if (sk2->num != snum ||
			    sk2 == sk ||
			    (sk2->bound_dev_if && sk->bound_dev_if &&
			     sk2->bound_dev_if != sk->bound_dev_if))
				continue;
#if 0
			if (sk2->family != AF_INET6 && sk2->family != AF_INET)
				continue;
#endif

			addr_type2 = sk2->family == AF_INET6 ? ipv6_addr_type(&sk2->net_pinfo.af_inet6.rcv_saddr) : IPV6_ADDR_MAPPED;
#if defined(CONFIG_NET_RESTRICTED_REUSE) || defined(CONFIG_IPV6_RESTRICTED_DOUBLE_BIND)
			sk2_uid = sock_i_uid_t(sk2);
#endif

			if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) &&
			    (addr_type != IPV6_ADDR_MAPPED ? addr_type != IPV6_ADDR_ANY : sk->rcv_saddr)) {
				if (addr_type2 == IPV6_ADDR_MAPPED || addr_type == IPV6_ADDR_MAPPED) {
					if (addr_type2 != addr_type ||
					    sk2->rcv_saddr != sk->rcv_saddr)
						continue;
				} else {
					if (ipv6_addr_cmp(&sk2->net_pinfo.af_inet6.rcv_saddr,
							  &sk->net_pinfo.af_inet6.rcv_saddr))
						continue;
				}
				both_specified = 1;
			}

#if defined(CONFIG_NET_RESTRICTED_REUSE) || defined(CONFIG_IPV6_RESTRICTED_DOUBLE_BIND)
			uid_ok = sk2_uid == (uid_t) -1 || sk_uid == sk2_uid;
#endif

			if ((addr_type2 == IPV6_ADDR_MAPPED && 
			     addr_type != IPV6_ADDR_MAPPED && sk->net_pinfo.af_inet6.ipv6only) ||
			    (addr_type == IPV6_ADDR_MAPPED &&
			     addr_type2 != IPV6_ADDR_MAPPED && sk2->net_pinfo.af_inet6.ipv6only)) {
#ifdef CONFIG_IPV6_RESTRICTED_DOUBLE_BIND
				if (sysctl_ipv6_bindv6only_restriction == 0 || uid_ok)
					continue;
#else
				continue;
#endif
			}

			sk2_reuse = 0;
			if (sk2->reuse)
				sk2_reuse |= 1;
#ifdef SO_REUSEPORT
			if (sk2->reuseport)
				sk2_reuse |= 2;
#endif
			if (sk2_reuse &&
			    (addr_type2 != IPV6_ADDR_MAPPED ? (addr_type2 & IPV6_ADDR_MULTICAST) : MULTICAST(sk2->rcv_saddr)))
				sk2_reuse |= 4;

			if (sk2_reuse & sk_reuse & 3) {	/* NOT && */
				if (sk2_reuse & sk_reuse & 4)
					continue;
#ifdef CONFIG_NET_RESTRICTED_REUSE
				if (!uid_ok)
					goto fail;
#endif
#ifdef SO_REUSEPORT
				if (sk2_reuse & sk_reuse & 2)
					continue;
#endif
				if (both_specified) {
					int addr_type2d = sk2->family == AF_INET6 ? ipv6_addr_type(&sk2->net_pinfo.af_inet6.daddr) : IPV6_ADDR_MAPPED;
					if (addr_type2d != IPV6_ADDR_MAPPED ? addr_type2d != IPV6_ADDR_ANY : sk2->daddr)
						continue;
				} else {
					if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) || 
					    (addr_type != IPV6_ADDR_MAPPED ? addr_type != IPV6_ADDR_ANY : sk->rcv_saddr))
						continue;
				}
			}
			goto fail;
#else	/* XXX: should be recoded like 2.4.21 */
			if (sk2->num == snum &&
			    sk2 != sk &&
			    (!sk2->bound_dev_if ||
			     !sk->bound_dev_if ||
			     sk2->bound_dev_if == sk->bound_dev_if) &&
			    ((!sk2->rcv_saddr && !ipv6_only_sock(sk)) ||
			     (sk2->family == AF_INET6 && 
			      ipv6_addr_any(&sk2->net_pinfo.af_inet6.rcv_saddr) &&
			      !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED)) ||
			     (addr_type == IPV6_ADDR_ANY && 
			      (!ipv6_only_sock(sk) || 
			       !(sk2->family == AF_INET6 ? (ipv6_addr_type(&sk2->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_MAPPED) : 1))) ||
			     (sk2->family == AF_INET6 && 
			      !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
					     &sk2->net_pinfo.af_inet6.rcv_saddr)) ||
			     (addr_type == IPV6_ADDR_MAPPED &&
			      !ipv6_only_sock(sk2) &&
			      (!sk2->rcv_saddr || 
			       !sk->rcv_saddr ||
			       sk->rcv_saddr == sk2->rcv_saddr))) &&
			    (!sk2->reuse || !sk->reuse))
				goto fail;
#endif	/* XXX: should be recoded like 2.4.21 */
		}
	}

	sk->num = snum;
	if (sk->pprev == NULL) {
		struct sock **skp = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
		if ((sk->next = *skp) != NULL)
			(*skp)->pprev = &sk->next;
		*skp = sk;
		sk->pprev = skp;
		sock_prot_inc_use(sk->prot);
		sock_hold(sk);
	}
	write_unlock_bh(&udp_hash_lock);
	return 0;

fail:
	write_unlock_bh(&udp_hash_lock);
	return 1;
}
Пример #11
0
Файл: cache.c Проект: surki/hipl
/**
 * firewall_cache_db_match:
 * Search in the cache database the given peers of hits, lsis or ips
 */
int firewall_cache_db_match(    struct in6_addr *hit_our,
				struct in6_addr *hit_peer,
				hip_lsi_t       *lsi_our,
				hip_lsi_t       *lsi_peer,
				struct in6_addr *ip_our,
				struct in6_addr *ip_peer,
				int *state){
	int i, err = 0, entry_in_cache = 0;
	firewall_cache_hl_t *this;
	hip_list_t *item, *tmp;
	struct in6_addr all_zero_v6 = {0};
	struct in_addr  all_zero_v4 = {0};
	struct hip_common *msg = NULL;
	firewall_cache_hl_t *ha_curr = NULL;
	firewall_cache_hl_t *ha_match = NULL;
	struct hip_tlv_common *current_param = NULL;

	HIP_ASSERT( (hit_our && hit_peer) ||
		    (lsi_our && lsi_peer)    );

	if(hit_peer){
		ha_match = (firewall_cache_hl_t *)hip_ht_find(
						firewall_cache_db,
						(void *)hit_peer);
		if(ha_match){
			HIP_DEBUG("Matched using hash\n");
			entry_in_cache = 1;
			goto out_err;
		}
	}

	HIP_DEBUG("Check firewall cache db\n");

	HIP_LOCK_HT(&firewall_cache_db);

	list_for_each_safe(item, tmp, firewall_cache_db, i){
		this = list_entry(item);

		if( lsi_our && lsi_peer) {
		  HIP_DEBUG_INADDR("this->our", &this->lsi_our.s_addr);
		  HIP_DEBUG_INADDR("this->peer", &this->lsi_peer.s_addr);
		  HIP_DEBUG_INADDR("our", lsi_our);
		  HIP_DEBUG_INADDR("peer", lsi_peer);
		}

		if( hit_our && hit_peer &&
		    (ipv6_addr_cmp(hit_peer, &this->hit_peer) == 0 ) &&
		    (ipv6_addr_cmp(hit_our,  &this->hit_our)  == 0 )    ){
			ha_match = this;
			break;
		}
		if( lsi_our && lsi_peer &&
		    lsi_peer->s_addr == this->lsi_peer.s_addr &&
		    lsi_our->s_addr  == this->lsi_our.s_addr     ){
			ha_match = this;
			break;
		}
		if( ip_our && ip_peer &&
		    ip_peer->s6_addr == this->ip_peer.s6_addr &&
		    ip_our->s6_addr  == this->ip_our.s6_addr     ) {
			ha_match = this;
			break;
		}
	}
/*
 *	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, RT6_LOOKUP_FLAG_STRICT);

	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())
			RT6_TRACE2(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;
}
Пример #13
0
int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl)
{
	int err = 0;

	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_cmp(&fl->fl6_dst, &rt->rt6i_dst.addr))
			     && (np->daddr_cache == NULL ||
				 ipv6_addr_cmp(&fl->fl6_dst, np->daddr_cache)))
			    || (fl->oif && fl->oif != (*dst)->dev->ifindex)) {
				*dst = NULL;
			} else
				dst_hold(*dst);
		}
	}

	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 availiable source address\n");
#endif
			goto out_err_release;
		}
	}
	if ((err = xfrm_lookup(dst, fl, sk, 0)) < 0) {
		err = -ENETUNREACH;
		goto out_err_release;
        }

	return 0;

out_err_release:
	dst_release(*dst);
	*dst = NULL;
	return err;
}
Пример #14
0
/*
 *	device multicast group inc (add if not found)
 */
int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr)
{
	struct ifmcaddr6 *mc;
	struct inet6_dev *idev;

#ifdef CONFIG_IPV6_MLD6_DEBUG
	char abuf[128];
	in6_ntop(addr, abuf);

	MDBG3((KERN_DEBUG
		"ipv6_dev_mc_inc(dev=%p(%s), addr=%s)\n",
		dev, dev->name ? dev->name :"<null>", abuf));
#endif

	idev = in6_dev_get(dev);

	if (idev == NULL)
		return -EINVAL;

	write_lock_bh(&idev->lock);
	if (idev->dead) {
		write_unlock_bh(&idev->lock);
		in6_dev_put(idev);
		return -ENODEV;
	}

	for (mc = idev->mc_list; mc; mc = mc->next) {
		if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) {
			mc->mca_users++;
			write_unlock_bh(&idev->lock);
			in6_dev_put(idev);
			return 0;
		}
	}

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

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

	if (mc == NULL) {
		write_unlock_bh(&idev->lock);
		in6_dev_put(idev);
		return -ENOMEM;
	}

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

	ipv6_addr_copy(&mc->mca_addr, addr);
	mc->idev = idev;
	mc->mca_users = 1;
	atomic_set(&mc->mca_refcnt, 2);
	mc->mca_lock = SPIN_LOCK_UNLOCKED;

	mc->next = idev->mc_list;
	idev->mc_list = mc;
	write_unlock_bh(&idev->lock);

	igmp6_group_added(mc);
	ma_put(mc);
	return 0;
}
Пример #15
0
static int
hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb,
	       enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
{
	const struct ip_set_hash *h = set->data;
	ipset_adtfn adtfn = set->variant->adt[adt];
	struct hash_net4_elem data = {
		.cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK
	};

	if (data.cidr == 0)
		return -EINVAL;
	if (adt == IPSET_TEST)
		data.cidr = HOST_MASK;

	ip4addrptr(skb, flags & IPSET_DIM_ONE_SRC, &data.ip);
	data.ip &= ip_set_netmask(data.cidr);

	return adtfn(set, &data, h->timeout);
}

static int
hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
	       enum ipset_adt adt, u32 *lineno, u32 flags)
{
	const struct ip_set_hash *h = set->data;
	ipset_adtfn adtfn = set->variant->adt[adt];
	struct hash_net4_elem data = { .cidr = HOST_MASK };
	u32 timeout = h->timeout;
	int ret;

	if (unlikely(!tb[IPSET_ATTR_IP] ||
		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
		return -IPSET_ERR_PROTOCOL;

	if (tb[IPSET_ATTR_LINENO])
		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);

	ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip);
	if (ret)
		return ret;

	if (tb[IPSET_ATTR_CIDR])
		data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);

	if (!data.cidr)
		return -IPSET_ERR_INVALID_CIDR;

	data.ip &= ip_set_netmask(data.cidr);

	if (tb[IPSET_ATTR_TIMEOUT]) {
		if (!with_timeout(h->timeout))
			return -IPSET_ERR_TIMEOUT;
		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
	}

	ret = adtfn(set, &data, timeout);

	return ip_set_eexist(ret, flags) ? 0 : ret;
}

static bool
hash_net_same_set(const struct ip_set *a, const struct ip_set *b)
{
	const struct ip_set_hash *x = a->data;
	const struct ip_set_hash *y = b->data;

	/* Resizing changes htable_bits, so we ignore it */
	return x->maxelem == y->maxelem &&
	       x->timeout == y->timeout;
}

/* The type variant functions: IPv6 */

struct hash_net6_elem {
	union nf_inet_addr ip;
	u16 padding0;
	u8 padding1;
	u8 cidr;
};

struct hash_net6_telem {
	union nf_inet_addr ip;
	u16 padding0;
	u8 padding1;
	u8 cidr;
	unsigned long timeout;
};

static inline bool
hash_net6_data_equal(const struct hash_net6_elem *ip1,
		     const struct hash_net6_elem *ip2)
{
	return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 &&
	       ip1->cidr == ip2->cidr;
}

static inline bool
hash_net6_data_isnull(const struct hash_net6_elem *elem)
{
	return elem->cidr == 0;
}

static inline void
hash_net6_data_copy(struct hash_net6_elem *dst,
		    const struct hash_net6_elem *src)
{
	ipv6_addr_copy(&dst->ip.in6, &src->ip.in6);
	dst->cidr = src->cidr;
}
Пример #16
0
int igmp6_event_query(struct sk_buff *skb)
{
	struct ifmcaddr6 *ma;
	struct in6_addr *addrp;
	unsigned long resptime;
	struct inet6_dev *idev;
	struct icmp6hdr *hdr;
	int addr_type;
#ifdef CONFIG_IPV6_MLD6_DEBUG
	char abuf1[128], abuf2[128];

	in6_ntop(&skb->nh.ipv6h->saddr, abuf1);
	in6_ntop(&skb->nh.ipv6h->daddr, abuf2);
	MDBG3((KERN_DEBUG
		"igmp6_event_query(skb=%p): saddr=%s, daddr=%s\n",
		skb, abuf1, abuf2));
#endif

	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
		return -EINVAL;

	hdr = (struct icmp6hdr*) skb->h.raw;

	/* 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);
	addrp = (struct in6_addr *) (hdr + 1);

#ifdef CONFIG_IPV6_MLD6_DEBUG
	in6_ntop(addrp, abuf1);
	MDBG3((KERN_DEBUG
		"igmp6_event_query(): maxdelay=%lu, addr=%s\n",
		resptime, abuf1));
#endif

	/* Translate milliseconds to jiffies */
	resptime = (resptime<<10)/(1024000/HZ);

	addr_type = ipv6_addr_type(addrp);

	if (addr_type == IPV6_ADDR_ANY ||
	    (addr_type & IPV6_ADDR_MULTICAST) == 0)
		goto drop;

	idev = in6_dev_get(skb->dev);

	if (idev == NULL)
		return 0;

	read_lock(&idev->lock);
	if (addr_type == IPV6_ADDR_ANY) {
		for (ma = idev->mc_list; ma; ma=ma->next)
			igmp6_group_queried(ma, resptime);
	} else {
		for (ma = idev->mc_list; ma; ma=ma->next) {
			if (ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
				igmp6_group_queried(ma, resptime);
				break;
			}
		}
	}
	read_unlock(&idev->lock);
	in6_dev_put(idev);
  drop:
	return 0;
}
Пример #17
0
static bool nf_nat_ipv6_in_range(const struct nf_conntrack_tuple *t,
				 const struct nf_nat_range *range)
{
	return ipv6_addr_cmp(&t->src.u3.in6, &range->min_addr.in6) >= 0 &&
	       ipv6_addr_cmp(&t->src.u3.in6, &range->max_addr.in6) <= 0;
}
int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
		   struct flowi *fl, unsigned length,
		   struct ipv6_txoptions *opt, int hlimit, int flags)
{
	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
	struct in6_addr *final_dst = NULL;
	struct dst_entry *dst;
	int err = 0;
	unsigned int pktlength, jumbolen, mtu;
	struct in6_addr saddr;

	if (opt && opt->srcrt) {
		struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
		final_dst = fl->fl6_dst;
		fl->fl6_dst = rt0->addr;
	}

	if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr))
		fl->oif = np->mcast_oif;

	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_cmp(fl->fl6_dst, &rt->rt6i_dst.addr))
		     && (np->daddr_cache == NULL ||
			 ipv6_addr_cmp(fl->fl6_dst, np->daddr_cache)))
		    || (fl->oif && fl->oif != dst->dev->ifindex)) {
			dst = NULL;
		} else
			dst_hold(dst);
	}

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

	if (dst->error) {
		IP6_INC_STATS(Ip6OutNoRoutes);
		dst_release(dst);
		return -ENETUNREACH;
	}

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

		if (err) {
#if IP6_DEBUG >= 2
			printk(KERN_DEBUG "ip6_build_xmit: "
			       "no available source address\n");
#endif
			goto out;
		}
		fl->fl6_src = &saddr;
	}
	pktlength = length;

	if (hlimit < 0) {
		if (ipv6_addr_is_multicast(fl->fl6_dst))
			hlimit = np->mcast_hops;
		else
			hlimit = np->hop_limit;
		if (hlimit < 0)
			hlimit = ((struct rt6_info*)dst)->rt6i_hoplimit;
	}

	jumbolen = 0;

	if (!sk->protinfo.af_inet.hdrincl) {
		pktlength += sizeof(struct ipv6hdr);
		if (opt)
			pktlength += opt->opt_flen + opt->opt_nflen;

		if (pktlength > 0xFFFF + sizeof(struct ipv6hdr)) {
			/* Jumbo datagram.
			   It is assumed, that in the case of hdrincl
			   jumbo option is supplied by user.
			 */
			pktlength += 8;
			jumbolen = pktlength - sizeof(struct ipv6hdr);
		}
	}

	mtu = dst->pmtu;
	if (np->frag_size < mtu) {
		if (np->frag_size)
			mtu = np->frag_size;
		else if (np->pmtudisc == IPV6_PMTUDISC_DONT)
			mtu = IPV6_MIN_MTU;
	}

	/* Critical arithmetic overflow check.
	   FIXME: may gcc optimize it out? --ANK (980726)
	 */
	if (pktlength < length) {
		ipv6_local_error(sk, EMSGSIZE, fl, mtu);
		err = -EMSGSIZE;
		goto out;
	}

	if (flags&MSG_CONFIRM)
		dst_confirm(dst);

	if (pktlength <= mtu) {
		struct sk_buff *skb;
		struct ipv6hdr *hdr;
		struct net_device *dev = dst->dev;

		err = 0;
		if (flags&MSG_PROBE)
			goto out;

		skb = sock_alloc_send_skb(sk, pktlength + 15 +
					  dev->hard_header_len,
					  flags & MSG_DONTWAIT, &err);

		if (skb == NULL) {
			IP6_INC_STATS(Ip6OutDiscards);
			goto out;
		}

		skb->dst = dst_clone(dst);

		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);

		hdr = (struct ipv6hdr *) skb->tail;
		skb->nh.ipv6h = hdr;

		if (!sk->protinfo.af_inet.hdrincl) {
			ip6_bld_1(sk, skb, fl, hlimit,
				  jumbolen ? sizeof(struct ipv6hdr) : pktlength);

			if (opt || jumbolen) {
				u8 *prev_hdr = &hdr->nexthdr;
				prev_hdr = ipv6_build_nfrag_opts(skb, prev_hdr, opt, final_dst, jumbolen);
				if (opt && opt->opt_flen)
					ipv6_build_frag_opts(skb, prev_hdr, opt);
			}
		}

		skb_put(skb, length);
		err = getfrag(data, &hdr->saddr,
			      ((char *) hdr) + (pktlength - length),
			      0, length);

		if (!err) {
			IP6_INC_STATS(Ip6OutRequests);
			err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
		} else {
			err = -EFAULT;
			kfree_skb(skb);
		}
	} else {
		if (sk->protinfo.af_inet.hdrincl || jumbolen ||
		    np->pmtudisc == IPV6_PMTUDISC_DO) {
			ipv6_local_error(sk, EMSGSIZE, fl, mtu);
			err = -EMSGSIZE;
			goto out;
		}

		err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, final_dst, hlimit,
				    flags, length, mtu);
	}

	/*
	 *	cleanup
	 */
out:
	ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL);
	if (err > 0)
		err = np->recverr ? net_xmit_errno(err) : 0;
	return err;
}
Пример #19
0
int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
			       struct ieee80211_vif *vif,
			       bool disable_offloading,
			       u32 cmd_flags)
{
	union {
		struct iwl_proto_offload_cmd_v1 v1;
		struct iwl_proto_offload_cmd_v2 v2;
		struct iwl_proto_offload_cmd_v3_small v3s;
		struct iwl_proto_offload_cmd_v3_large v3l;
	} cmd = {};
	struct iwl_host_cmd hcmd = {
		.id = PROT_OFFLOAD_CONFIG_CMD,
		.flags = cmd_flags,
		.data[0] = &cmd,
		.dataflags[0] = IWL_HCMD_DFL_DUP,
	};
	struct iwl_proto_offload_cmd_common *common;
	u32 enabled = 0, size;
	u32 capa_flags = mvm->fw->ucode_capa.flags;
#if IS_ENABLED(CONFIG_IPV6)
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	int i;

	if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
	    capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
		struct iwl_ns_config *nsc;
		struct iwl_targ_addr *addrs;
		int n_nsc, n_addrs;
		int c;

		if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
			nsc = cmd.v3s.ns_config;
			n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
			addrs = cmd.v3s.targ_addrs;
			n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
		} else {
			nsc = cmd.v3l.ns_config;
			n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
			addrs = cmd.v3l.targ_addrs;
			n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
		}

		if (mvmvif->num_target_ipv6_addrs)
			enabled |= IWL_D3_PROTO_OFFLOAD_NS;

		/*
		 * For each address we have (and that will fit) fill a target
		 * address struct and combine for NS offload structs with the
		 * solicited node addresses.
		 */
		for (i = 0, c = 0;
		     i < mvmvif->num_target_ipv6_addrs &&
		     i < n_addrs && c < n_nsc; i++) {
			struct in6_addr solicited_addr;
			int j;

			addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
						  &solicited_addr);
			for (j = 0; j < c; j++)
				if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
						  &solicited_addr) == 0)
					break;
			if (j == c)
				c++;
			addrs[i].addr = mvmvif->target_ipv6_addrs[i];
			addrs[i].config_num = cpu_to_le32(j);
			nsc[j].dest_ipv6_addr = solicited_addr;
			memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
		}

		if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
			cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
		else
			cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
		if (mvmvif->num_target_ipv6_addrs) {
			enabled |= IWL_D3_PROTO_OFFLOAD_NS;
			memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
		}

		BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
			     sizeof(mvmvif->target_ipv6_addrs[0]));

		for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
				    IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++)
			memcpy(cmd.v2.target_ipv6_addr[i],
			       &mvmvif->target_ipv6_addrs[i],
			       sizeof(cmd.v2.target_ipv6_addr[i]));
	} else {
		if (mvmvif->num_target_ipv6_addrs) {
			enabled |= IWL_D3_PROTO_OFFLOAD_NS;
			memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
		}

		BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
			     sizeof(mvmvif->target_ipv6_addrs[0]));

		for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
				    IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++)
			memcpy(cmd.v1.target_ipv6_addr[i],
			       &mvmvif->target_ipv6_addrs[i],
			       sizeof(cmd.v1.target_ipv6_addr[i]));
	}
#endif

	if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
		common = &cmd.v3s.common;
		size = sizeof(cmd.v3s);
	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
		common = &cmd.v3l.common;
		size = sizeof(cmd.v3l);
	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
		common = &cmd.v2.common;
		size = sizeof(cmd.v2);
	} else {
		common = &cmd.v1.common;
		size = sizeof(cmd.v1);
	}

	if (vif->bss_conf.arp_addr_cnt) {
		enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
		common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
		memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
	}

	if (!disable_offloading)
		common->enabled = cpu_to_le32(enabled);

	hcmd.len[0] = size;
	return iwl_mvm_send_cmd(mvm, &hcmd);
}
static bool
ebt_ip6_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
	const struct ebt_ip6_info *info = par->matchinfo;
	const struct ipv6hdr *ih6;
	struct ipv6hdr _ip6h;
	const struct tcpudphdr *pptr;
	struct tcpudphdr _ports;
	struct in6_addr tmp_addr;
	int i;

	ih6 = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h);
	if (ih6 == NULL)
		return false;
	if (info->bitmask & EBT_IP6_TCLASS &&
	   FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS))
		return false;
	for (i = 0; i < 4; i++)
		tmp_addr.in6_u.u6_addr32[i] = ih6->saddr.in6_u.u6_addr32[i] &
			info->smsk.in6_u.u6_addr32[i];
	if (info->bitmask & EBT_IP6_SOURCE &&
		FWINV((ipv6_addr_cmp(&tmp_addr, &info->saddr) != 0),
			EBT_IP6_SOURCE))
		return false;
	for (i = 0; i < 4; i++)
		tmp_addr.in6_u.u6_addr32[i] = ih6->daddr.in6_u.u6_addr32[i] &
			info->dmsk.in6_u.u6_addr32[i];
	if (info->bitmask & EBT_IP6_DEST &&
	   FWINV((ipv6_addr_cmp(&tmp_addr, &info->daddr) != 0), EBT_IP6_DEST))
		return false;
	if (info->bitmask & EBT_IP6_PROTO) {
		uint8_t nexthdr = ih6->nexthdr;
		int offset_ph;

		offset_ph = ipv6_skip_exthdr(skb, sizeof(_ip6h), &nexthdr);
		if (offset_ph == -1)
			return false;
		if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO))
			return false;
		if (!(info->bitmask & EBT_IP6_DPORT) &&
		    !(info->bitmask & EBT_IP6_SPORT))
			return true;
		pptr = skb_header_pointer(skb, offset_ph, sizeof(_ports),
					  &_ports);
		if (pptr == NULL)
			return false;
		if (info->bitmask & EBT_IP6_DPORT) {
			u32 dst = ntohs(pptr->dst);
			if (FWINV(dst < info->dport[0] ||
				  dst > info->dport[1], EBT_IP6_DPORT))
				return false;
		}
		if (info->bitmask & EBT_IP6_SPORT) {
			u32 src = ntohs(pptr->src);
			if (FWINV(src < info->sport[0] ||
				  src > info->sport[1], EBT_IP6_SPORT))
			return false;
		}
		return true;
	}
	return true;
}
Пример #21
0
/**
 * mipv6_bcache_add - add Binding Cache entry
 * @ifindex: interface index
 * @our_addr: own address
 * @home_addr: MN's home address
 * @coa: MN's care-of address
 * @lifetime: lifetime for this binding
 * @prefix: prefix length
 * @seq: sequence number
 * @single: single address bit
 * @type: type of entry
 *
 * Adds an entry for this @home_addr in the Binding Cache.  If entry
 * already exists, old entry is updated.  @type may be %CACHE_ENTRY or
 * %HOME_REGISTRATION.
 **/
int mipv6_bcache_add(
	int ifindex,
	struct in6_addr *our_addr,
	struct in6_addr *home_addr,
	struct in6_addr *coa,
	__u32 lifetime,
	__u8 prefix,
	__u8 seq,
	__u8 single,
	__u8 type) 
{
	unsigned long flags;
	struct mipv6_bcache_entry *entry;
	int update = 0;
	int create_tunnel = 0;
	unsigned long now = jiffies;
	struct cache_entry_iterator_args args;

	write_lock_irqsave(&bcache->lock, flags);

	if ((entry = (struct mipv6_bcache_entry *)
	     hashlist_get(bcache->entries, home_addr)) != NULL) {
                /* if an entry for this home_addr exists (with smaller
		 * seq than the new seq), update it by removing it
		 * first
		 */
		if (SEQMOD(seq, entry->seq)) {
			DEBUG((DBG_INFO, "mipv6_bcache_add: updating an "
			       "existing entry"));
			update = 1;

			if (entry->type == HOME_REGISTRATION) {
				create_tunnel = ipv6_addr_cmp(&entry->our_addr,
							      our_addr) ||
					ipv6_addr_cmp(&entry->coa, coa) ||
					entry->ifindex != ifindex || 
					entry->prefix != prefix || 
					entry->single != single;
				if (create_tunnel || 
				    type != HOME_REGISTRATION) {
					mipv6_tunnel_route_del(
						&entry->home_addr, 
						&entry->coa,
						&entry->our_addr);
					mipv6_tunnel_del(&entry->coa, 
							 &entry->our_addr);
				}				
				if (type != HOME_REGISTRATION) {
					bcache_proxy_nd_rem(entry);
				}
			}
		} else {
			DEBUG((DBG_INFO, "mipv6_bcache_add: smaller seq "
			       "than existing, not updating"));
/*
			write_unlock_irqrestore(&bcache->lock, flags);
			return 0;
*/
		}
	} else {
		/* no entry for this home_addr, try to create a new entry */
		DEBUG((DBG_INFO, "mipv6_bcache_add: creating a new entry"));
		entry = mipv6_bcache_get_entry(type);
		
		if (entry == NULL) {
			/* delete next expiring entry of type CACHE_ENTRY */
			args.entry = &entry;
			hashlist_iterate(bcache->entries, &args,
			                 find_first_cache_entry_iterator);

			if (entry == NULL) {
				DEBUG((DBG_INFO, "mipv6_bcache_add: cache full"));
				write_unlock_irqrestore(&bcache->lock, flags);
				return -1;
			}
			hashlist_delete(bcache->entries, &entry->home_addr);
		}
		create_tunnel = (type == HOME_REGISTRATION);
	}
	
	ipv6_addr_copy(&(entry->our_addr), our_addr);
	ipv6_addr_copy(&(entry->home_addr), home_addr);
	ipv6_addr_copy(&(entry->coa), coa);
	entry->ifindex = ifindex;
	entry->prefix = prefix;
	entry->seq = seq;
	entry->type = type;
	entry->last_used = 0;

	if (type == HOME_REGISTRATION) {
		entry->router = 0;
		entry->single = single;
		if (create_tunnel) {
			if (mipv6_tunnel_add(coa, our_addr, 0)) {
				DEBUG((DBG_INFO, "mipv6_bcache_add: no free tunnel devices!"));
				bcache_proxy_nd_rem(entry);
				if (update) 
					hashlist_delete(bcache->entries, 
							&entry->home_addr);
				mipv6_bcache_entry_free(entry);

				write_unlock_irqrestore(&bcache->lock, flags);
				return -1;
			}
			/* Todo: set the prefix length correctly */
			if (mipv6_tunnel_route_add(home_addr, 
						   coa, 
						   our_addr)) {
				DEBUG((DBG_INFO, "mipv6_bcache_add: invalid route to home address!"));
				mipv6_tunnel_del(coa, our_addr);
				bcache_proxy_nd_rem(entry);

				if (update) 
					hashlist_delete(bcache->entries, 
							&entry->home_addr);
				mipv6_bcache_entry_free(entry);

				write_unlock_irqrestore(&bcache->lock, flags);
				return -1;
			}
		}
	}
	entry->last_br = 0;
	if (lifetime == EXPIRE_INFINITE) {
		entry->callback_time = EXPIRE_INFINITE; 
	} else {
		entry->callback_time = now + lifetime * HZ;
		if (entry->type & (HOME_REGISTRATION | TEMPORARY_ENTRY))
			entry->br_callback_time = 0;
		else
			entry->br_callback_time = now + 
				(lifetime - BCACHE_BR_SEND_LEAD) * HZ;
	}

	if (update) {
		DEBUG((DBG_INFO, "updating entry : %x", entry));
		hashlist_reschedule(bcache->entries,
		                    home_addr,
		                    entry->callback_time);
	} else {
		DEBUG((DBG_INFO, "adding entry: %x", entry));
		if ((hashlist_add(bcache->entries,
				  home_addr,
				  entry->callback_time,
				  entry)) < 0) {
			if (create_tunnel) {
				mipv6_tunnel_route_del(home_addr, 
						       coa, 
						       our_addr);
				mipv6_tunnel_del(coa, our_addr);
				bcache_proxy_nd_rem(entry);
			}
			mipv6_bcache_entry_free(entry);
			DEBUG((DBG_ERROR, "Hash add failed"));
			write_unlock_irqrestore(&bcache->lock, flags);
			return -1;
		}
	}

	set_timer();

	write_unlock_irqrestore(&bcache->lock, flags);
	return 0;
}