int ndp_send(uint8_t type, uint8_t code, const void *body, size_t body_sz, struct net_device *dev) { struct sk_buff *skb; struct in_device *in_dev; struct in6_addr dst_ip6; assert(dev != NULL); skb = skb_alloc(dev->hdr_len + IP6_HEADER_SIZE + ICMP6_MIN_HEADER_SIZE + body_sz); if (skb == NULL) { return -ENOMEM; } skb->dev = dev; skb->nh.raw = skb->mac.raw + dev->hdr_len; skb->h.raw = skb->nh.raw + IP6_HEADER_SIZE; in_dev = inetdev_get_by_dev(dev); assert(in_dev != NULL); inet_pton(AF_INET6, "ff02::1:ff02:10", &dst_ip6); ip6_build(skb->nh.ip6h, ICMP6_MIN_HEADER_SIZE + body_sz, IPPROTO_ICMPV6, 255, &in_dev->ifa6_address, &dst_ip6); icmp6_build(skb->h.icmp6h, type, code, body, body_sz); icmp6_set_check_field(skb->h.icmp6h, skb->nh.ip6h); return ndp_xmit(skb, dev); }
static inline ip6_addr get_ip6_ll(const void *p) { return ip6_build(0xfe800000, 0, get_u32(p+0), get_u32(p+4)); }
static int ip6_make(const struct sock *sk, const struct sockaddr *to, size_t *data_size, struct sk_buff **out_skb) { size_t hdr_size, max_size; struct sk_buff *skb; struct net_device *dev; const struct inet6_sock *in6_sk; const struct sockaddr_in6 *to_in6; uint8_t nexthdr; const struct in6_addr *src_ip6; struct in6_addr dst_ip6; assert((to == NULL) || (to->sa_family == AF_INET6)); assert(data_size != NULL); assert(out_skb != NULL); assert((sk != NULL) || (*out_skb != NULL)); in6_sk = to_const_inet6_sock(sk); to_in6 = (const struct sockaddr_in6 *)to; assert((to_in6 == NULL) || (to_in6->sin6_family == AF_INET6)); memcpy(&dst_ip6, to_in6 != NULL ? &to_in6->sin6_addr : in6_sk != NULL ? &in6_sk->dst_in6.sin6_addr : &(*out_skb)->nh.ip6h->saddr, /* make a reply */ sizeof dst_ip6); /* FIXME use route */ if (0 == memcmp(&dst_ip6, &in6addr_loopback, sizeof dst_ip6)) { dev = netdev_get_by_name("lo"); } else { dev = netdev_get_by_name("eth0"); } assert(dev != NULL); assert(inetdev_get_by_dev(dev) != NULL); src_ip6 = &inetdev_get_by_dev(dev)->ifa6_address; nexthdr = in6_sk != NULL ? in6_sk->sk.opt.so_protocol : (*out_skb)->nh.ip6h->nexthdr; hdr_size = dev->hdr_len + IP6_HEADER_SIZE; max_size = min(dev->mtu, skb_max_size()); if (hdr_size > max_size) { return -EMSGSIZE; } *data_size = min(*data_size, max_size - hdr_size); skb = skb_realloc(hdr_size + *data_size, *out_skb); if (skb == NULL) { return -ENOMEM; } skb->dev = dev; skb->nh.raw = skb->mac.raw + dev->hdr_len; skb->h.raw = skb->nh.raw + IP6_HEADER_SIZE; ip6_build(skb->nh.ip6h, *data_size, nexthdr, 64, src_ip6, &dst_ip6); *out_skb = skb; return 0; }