Exemple #1
0
struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
					  struct ndisc_options *ndopts)
{
	struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;

	if (!nd_opt || opt_len < 0 || !ndopts)
		return NULL;
	memset(ndopts, 0, sizeof(*ndopts));
	while (opt_len) {
		int l;
		if (opt_len < sizeof(struct nd_opt_hdr))
			return NULL;
		l = nd_opt->nd_opt_len << 3;
		if (opt_len < l || l == 0)
			return NULL;
		switch (nd_opt->nd_opt_type) {
		case ND_OPT_SOURCE_LL_ADDR:
		case ND_OPT_TARGET_LL_ADDR:
		case ND_OPT_MTU:
		case ND_OPT_REDIRECT_HDR:
			if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
				ND_PRINTK(2, warn,
					  "%s: duplicated ND6 option found: type=%d\n",
					  __func__, nd_opt->nd_opt_type);
			} else {
				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
			}
			break;
		case ND_OPT_PREFIX_INFO:
			ndopts->nd_opts_pi_end = nd_opt;
			if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
			break;
#ifdef CONFIG_IPV6_ROUTE_INFO
		case ND_OPT_ROUTE_INFO:
			ndopts->nd_opts_ri_end = nd_opt;
			if (!ndopts->nd_opts_ri)
				ndopts->nd_opts_ri = nd_opt;
			break;
#endif
		default:
			if (ndisc_is_useropt(nd_opt)) {
				ndopts->nd_useropts_end = nd_opt;
				if (!ndopts->nd_useropts)
					ndopts->nd_useropts = nd_opt;
			} else {
				ND_PRINTK(2, notice,
					  "%s: ignored unsupported option; type=%d, len=%d\n",
					  __func__,
					  nd_opt->nd_opt_type,
					  nd_opt->nd_opt_len);
			}
		}
		opt_len -= l;
		nd_opt = ((void *)nd_opt) + l;
	}
	return ndopts;
}
Exemple #2
0
static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
				       int len)
{
	int hlen = LL_RESERVED_SPACE(dev);
	int tlen = dev->needed_tailroom;
	struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
	struct sk_buff *skb;

	skb = alloc_skb(hlen + sizeof(struct ipv6hdr) + len + tlen, GFP_ATOMIC);
	if (!skb) {
		ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb\n",
			  __func__);
		return NULL;
	}

	skb->protocol = htons(ETH_P_IPV6);
	skb->dev = dev;

	skb_reserve(skb, hlen + sizeof(struct ipv6hdr));
	skb_reset_transport_header(skb);

	/* Manually assign socket ownership as we avoid calling
	 * sock_alloc_send_pskb() to bypass wmem buffer limits
	 */
	skb_set_owner_w(skb, sk);

	return skb;
}
static void lowpan_ndisc_prefix_rcv_add_addr(struct net *net,
        struct net_device *dev,
        const struct prefix_info *pinfo,
        struct inet6_dev *in6_dev,
        struct in6_addr *addr,
        int addr_type, u32 addr_flags,
        bool sllao, bool tokenized,
        __u32 valid_lft,
        u32 prefered_lft,
        bool dev_addr_generated)
{
    int err;

    /* generates short based address for RA PIO's */
    if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) && dev_addr_generated &&
            !addrconf_ifid_802154_6lowpan(addr->s6_addr + 8, dev)) {
        err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
                                           addr, addr_type, addr_flags,
                                           sllao, tokenized, valid_lft,
                                           prefered_lft);
        if (err)
            ND_PRINTK(2, warn,
                      "RA: could not add a short address based address for prefix: %pI6c\n",
                      &pinfo->prefix);
    }
}
Exemple #4
0
static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
				       int len)
{
	int hlen = LL_RESERVED_SPACE(dev);
	int tlen = dev->needed_tailroom;
	struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
	struct sk_buff *skb;
	int err;

	skb = sock_alloc_send_skb(sk,
				  hlen + sizeof(struct ipv6hdr) + len + tlen,
				  1, &err);
	if (!skb) {
		ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n",
			  __func__, err);
		return NULL;
	}

	skb->protocol = htons(ETH_P_IPV6);
	skb->dev = dev;

	skb_reserve(skb, hlen + sizeof(struct ipv6hdr));
	skb_reset_transport_header(skb);

	return skb;
}
Exemple #5
0
struct sk_buff *ndisc_build_skb(struct net_device *dev,
				const struct in6_addr *daddr,
				const struct in6_addr *saddr,
				struct icmp6hdr *icmp6h,
				const struct in6_addr *target,
				int llinfo)
{
	struct net *net = dev_net(dev);
	struct sock *sk = net->ipv6.ndisc_sk;
	struct sk_buff *skb;
	struct icmp6hdr *hdr;
	int hlen = LL_RESERVED_SPACE(dev);
	int tlen = dev->needed_tailroom;
	int len;
	int err;
	u8 *opt;

	if (!dev->addr_len)
		llinfo = 0;

	len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
	if (llinfo)
		len += ndisc_opt_addr_space(dev);

	skb = sock_alloc_send_skb(sk,
				  (MAX_HEADER + sizeof(struct ipv6hdr) +
				   len + hlen + tlen),
				  1, &err);
	if (!skb) {
		ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n",
			  __func__, err);
		return NULL;
	}

	skb_reserve(skb, hlen);
	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);

	skb->transport_header = skb->tail;
	skb_put(skb, len);

	hdr = (struct icmp6hdr *)skb_transport_header(skb);
	memcpy(hdr, icmp6h, sizeof(*hdr));

	opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
	if (target) {
		*(struct in6_addr *)opt = *target;
		opt += sizeof(*target);
	}

	if (llinfo)
		ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
				       dev->addr_len, dev->type);

	hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
					   IPPROTO_ICMPV6,
					   csum_partial(hdr,
							len, 0));

	return skb;
}
static void lowpan_ndisc_802154_update(struct neighbour *n, u32 flags,
                                       u8 icmp6_type,
                                       const struct ndisc_options *ndopts)
{
    struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
    u8 *lladdr_short = NULL;

    switch (icmp6_type) {
    case NDISC_ROUTER_SOLICITATION:
    case NDISC_ROUTER_ADVERTISEMENT:
    case NDISC_NEIGHBOUR_SOLICITATION:
        if (ndopts->nd_802154_opts_src_lladdr) {
            lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_src_lladdr,
                                                 IEEE802154_SHORT_ADDR_LEN, 0);
            if (!lladdr_short) {
                ND_PRINTK(2, warn,
                          "NA: invalid short link-layer address length\n");
                return;
            }
        }
        break;
    case NDISC_REDIRECT:
    case NDISC_NEIGHBOUR_ADVERTISEMENT:
        if (ndopts->nd_802154_opts_tgt_lladdr) {
            lladdr_short = __ndisc_opt_addr_data(ndopts->nd_802154_opts_tgt_lladdr,
                                                 IEEE802154_SHORT_ADDR_LEN, 0);
            if (!lladdr_short) {
                ND_PRINTK(2, warn,
                          "NA: invalid short link-layer address length\n");
                return;
            }
        }
        break;
    default:
        break;
    }

    write_lock_bh(&n->lock);
    if (lladdr_short) {
        ieee802154_be16_to_le16(&neigh->short_addr, lladdr_short);
        if (!lowpan_802154_is_valid_src_short_addr(neigh->short_addr))
            neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
    } else {
        neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
    }
    write_unlock_bh(&n->lock);
}
static int lowpan_ndisc_parse_802154_options(const struct net_device *dev,
        struct nd_opt_hdr *nd_opt,
        struct ndisc_options *ndopts)
{
    switch (nd_opt->nd_opt_len) {
    case NDISC_802154_SHORT_ADDR_LENGTH:
        if (ndopts->nd_802154_opt_array[nd_opt->nd_opt_type])
            ND_PRINTK(2, warn,
                      "%s: duplicated short addr ND6 option found: type=%d\n",
                      __func__, nd_opt->nd_opt_type);
        else
            ndopts->nd_802154_opt_array[nd_opt->nd_opt_type] = nd_opt;
        return 1;
    default:
        /* all others will be handled by ndisc IPv6 option parsing */
        return 0;
    }
}