static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t) { struct ip6_tnl_parm *p = &t->parms; int ret = 0; struct net *net = dev_net(t->dev); if (p->flags & IP6_TNL_F_CAP_RCV) { struct net_device *ldev = NULL; if (p->link) ldev = dev_get_by_index(net, p->link); if ((ipv6_addr_is_multicast(&p->laddr) || likely(ipv6_chk_addr(net, &p->laddr, ldev, 0))) && likely(!ipv6_chk_addr(net, &p->raddr, NULL, 0))) ret = 1; if (ldev) dev_put(ldev); } return ret; }
ng_pktsnip_t *ng_ndp_nbr_sol_build(ipv6_addr_t *tgt, ng_pktsnip_t *options) { ng_pktsnip_t *pkt; DEBUG("ndp: building neighbor solicitation message\n"); if (ipv6_addr_is_multicast(tgt)) { DEBUG("ndp: tgt must not be multicast\n"); return NULL; } pkt = ng_icmpv6_build(options, ICMPV6_NBR_SOL, 0, sizeof(ng_ndp_nbr_sol_t)); if (pkt != NULL) { ng_ndp_nbr_sol_t *nbr_sol = pkt->data; nbr_sol->resv.u32 = 0; nbr_sol->tgt.u64[0].u64 = tgt->u64[0].u64; nbr_sol->tgt.u64[1].u64 = tgt->u64[1].u64; } return pkt; }
static int ip6_output2(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct net_device *dev = dst->dev; skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) { struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL; struct inet6_dev *idev = ip6_dst_idev(skb->dst); if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) && ((mroute6_socket && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr))) { struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); /* Do not check for IFF_ALLMULTI; multicast routing is not supported in any case. */ if (newskb) NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, newskb, NULL, newskb->dev, ip6_dev_loopback_xmit); if (ipv6_hdr(skb)->hop_limit == 0) { IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); return 0; } } IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS); } return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish); }
static void ip6_datagram_flow_key_init(struct flowi6 *fl6, struct sock *sk) { struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); memset(fl6, 0, sizeof(*fl6)); fl6->flowi6_proto = sk->sk_protocol; fl6->daddr = sk->sk_v6_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; fl6->flowlabel = np->flow_label; if (!fl6->flowi6_oif) fl6->flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; if (!fl6->flowi6_oif && ipv6_addr_is_multicast(&fl6->daddr)) fl6->flowi6_oif = np->mcast_oif; security_sk_classify_flow(sk, flowi6_to_flowi(fl6)); }
static int _netif_del(kernel_pid_t iface, char *addr_str) { #ifdef MODULE_GNRC_IPV6 ipv6_addr_t addr; if (ipv6_addr_from_str(&addr, addr_str) == NULL) { puts("error: unable to parse IPv6 address."); return 1; } if (ipv6_addr_is_multicast(&addr)) { if (gnrc_netapi_set(iface, NETOPT_IPV6_GROUP_LEAVE, 0, &addr, sizeof(addr)) < 0) { printf("error: unable to leave IPv6 multicast group\n"); return 1; } } else { if (gnrc_netapi_set(iface, NETOPT_IPV6_ADDR_REMOVE, 0, &addr, sizeof(addr)) < 0) { printf("error: unable to remove IPv6 address\n"); return 1; } } printf("success: removed %s to interface %" PRIkernel_pid "\n", addr_str, iface); return 0; #else (void)iface; (void)addr_str; puts("error: unable to delete IPv6 address."); return 1; #endif }
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); }
ng_pktsnip_t *ng_ndp_nbr_adv_build(uint8_t flags, ipv6_addr_t *tgt, ng_pktsnip_t *options) { ng_pktsnip_t *pkt; DEBUG("ndp: building neighbor advertisement message\n"); if (ipv6_addr_is_multicast(tgt)) { DEBUG("ndp: tgt must not be multicast\n"); return NULL; } pkt = ng_icmpv6_build(options, ICMPV6_NBR_ADV, 0, sizeof(ng_ndp_nbr_adv_t)); if (pkt != NULL) { ng_ndp_nbr_adv_t *nbr_adv = pkt->data; nbr_adv->flags = (flags & NG_NDP_NBR_ADV_FLAGS_MASK); nbr_adv->resv[0] = nbr_adv->resv[1] = nbr_adv->resv[2] = 0; nbr_adv->tgt.u64[0].u64 = tgt->u64[0].u64; nbr_adv->tgt.u64[1].u64 = tgt->u64[1].u64; } return pkt; }
int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) { struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); struct icmp6hdr user_icmph; int addr_type; struct in6_addr *daddr; int iif = 0; struct flowi6 fl6; int err; int hlimit; struct dst_entry *dst; struct rt6_info *rt; struct pingfakehdr pfh; pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph, sizeof(user_icmph)); if (err) return err; if (msg->msg_name) { struct sockaddr_in6 *u = (struct sockaddr_in6 *) msg->msg_name; if (msg->msg_namelen < sizeof(struct sockaddr_in6) || u->sin6_family != AF_INET6) { return -EINVAL; } if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != u->sin6_scope_id) { return -EINVAL; } daddr = &(u->sin6_addr); iif = u->sin6_scope_id; } else { if (sk->sk_state != TCP_ESTABLISHED) return -EDESTADDRREQ; daddr = &np->daddr; } if (!iif) iif = sk->sk_bound_dev_if; addr_type = ipv6_addr_type(daddr); if (__ipv6_addr_needs_scope_id(addr_type) && !iif) return -EINVAL; if (addr_type & IPV6_ADDR_MAPPED) return -EINVAL; /* TODO: use ip6_datagram_send_ctl to get options from cmsg */ memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_ICMPV6; fl6.saddr = np->saddr; fl6.daddr = *daddr; fl6.flowi6_mark = sk->sk_mark; fl6.fl6_icmp_type = user_icmph.icmp6_type; fl6.fl6_icmp_code = user_icmph.icmp6_code; fl6.flowi6_uid = sock_i_uid(sk); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) fl6.flowi6_oif = np->mcast_oif; else if (!fl6.flowi6_oif) fl6.flowi6_oif = np->ucast_oif; dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, 1); if (IS_ERR(dst)) return PTR_ERR(dst); rt = (struct rt6_info *) dst; np = inet6_sk(sk); if (!np) return -EBADF; if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) fl6.flowi6_oif = np->mcast_oif; else if (!fl6.flowi6_oif) fl6.flowi6_oif = np->ucast_oif; pfh.icmph.type = user_icmph.icmp6_type; pfh.icmph.code = user_icmph.icmp6_code; pfh.icmph.checksum = 0; pfh.icmph.un.echo.id = inet->inet_sport; pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence; pfh.iov = msg->msg_iov; pfh.wcheck = 0; pfh.family = AF_INET6; if (ipv6_addr_is_multicast(&fl6.daddr)) hlimit = np->mcast_hops; else hlimit = np->hop_limit; if (hlimit < 0) hlimit = ip6_dst_hoplimit(dst); lock_sock(sk); err = ip6_append_data(sk, ping_getfrag, &pfh, len, 0, hlimit, np->tclass, NULL, &fl6, rt, MSG_DONTWAIT, np->dontfrag); if (err) { ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev, ICMP6_MIB_OUTERRORS); ip6_flush_pending_frames(sk); } else { err = icmpv6_push_pending_frames(sk, &fl6, (struct icmp6hdr *) &pfh.icmph, len); } release_sock(sk); if (err) return err; return len; }
static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst->dev; struct neighbour *neigh; struct in6_addr *nexthop; int ret; skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) { struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) && ((mroute6_socket(net, skb) && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr))) { struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); /* Do not check for IFF_ALLMULTI; multicast routing is not supported in any case. */ if (newskb) NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, net, sk, newskb, NULL, newskb->dev, dev_loopback_xmit); if (ipv6_hdr(skb)->hop_limit == 0) { IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); return 0; } } IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, skb->len); if (IPV6_ADDR_MC_SCOPE(&ipv6_hdr(skb)->daddr) <= IPV6_ADDR_SCOPE_NODELOCAL && !(dev->flags & IFF_LOOPBACK)) { kfree_skb(skb); return 0; } } rcu_read_lock_bh(); nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr); neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop); if (unlikely(!neigh)) neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false); if (!IS_ERR(neigh)) { ret = dst_neigh_output(dst, neigh, skb); rcu_read_unlock_bh(); return ret; } rcu_read_unlock_bh(); IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); kfree_skb(skb); return -EINVAL; }
int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { const struct ipv6hdr *hdr; u32 pkt_len; struct inet6_dev *idev; struct net *net = dev_net(skb->dev); if (skb->pkt_type == PACKET_OTHERHOST) { kfree_skb(skb); return NET_RX_DROP; } rcu_read_lock(); idev = __in6_dev_get(skb->dev); __IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_IN, skb->len); if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL || !idev || unlikely(idev->cnf.disable_ipv6)) { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS); goto drop; } memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); /* * Store incoming device index. When the packet will * be queued, we cannot refer to skb->dev anymore. * * BTW, when we send a packet for our own local address on a * non-loopback interface (e.g. ethX), it is being delivered * via the loopback interface (lo) here; skb->dev = loopback_dev. * It, however, should be considered as if it is being * arrived via the sending interface (ethX), because of the * nature of scoping architecture. --yoshfuji */ IP6CB(skb)->iif = skb_valid_dst(skb) ? ip6_dst_idev(skb_dst(skb))->dev->ifindex : dev->ifindex; if (unlikely(!pskb_may_pull(skb, sizeof(*hdr)))) goto err; hdr = ipv6_hdr(skb); if (hdr->version != 6) goto err; __IP6_ADD_STATS(net, idev, IPSTATS_MIB_NOECTPKTS + (ipv6_get_dsfield(hdr) & INET_ECN_MASK), max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs)); /* * RFC4291 2.5.3 * A packet received on an interface with a destination address * of loopback must be dropped. */ if (!(dev->flags & IFF_LOOPBACK) && ipv6_addr_loopback(&hdr->daddr)) goto err; /* RFC4291 Errata ID: 3480 * Interface-Local scope spans only a single interface on a * node and is useful only for loopback transmission of * multicast. Packets with interface-local scope received * from another node must be discarded. */ if (!(skb->pkt_type == PACKET_LOOPBACK || dev->flags & IFF_LOOPBACK) && ipv6_addr_is_multicast(&hdr->daddr) && IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 1) goto err; /* If enabled, drop unicast packets that were encapsulated in link-layer * multicast or broadcast to protected against the so-called "hole-196" * attack in 802.11 wireless. */ if (!ipv6_addr_is_multicast(&hdr->daddr) && (skb->pkt_type == PACKET_BROADCAST || skb->pkt_type == PACKET_MULTICAST) && idev->cnf.drop_unicast_in_l2_multicast) goto err; /* RFC4291 2.7 * Nodes must not originate a packet to a multicast address whose scope * field contains the reserved value 0; if such a packet is received, it * must be silently dropped. */ if (ipv6_addr_is_multicast(&hdr->daddr) && IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 0) goto err; /* * RFC4291 2.7 * Multicast addresses must not be used as source addresses in IPv6 * packets or appear in any Routing header. */ if (ipv6_addr_is_multicast(&hdr->saddr)) goto err; skb->transport_header = skb->network_header + sizeof(*hdr); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); pkt_len = ntohs(hdr->payload_len); /* pkt_len may be zero if Jumbo payload option is present */ if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) { if (pkt_len + sizeof(struct ipv6hdr) > skb->len) { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; } if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); goto drop; } hdr = ipv6_hdr(skb); } if (hdr->nexthdr == NEXTHDR_HOP) { if (ipv6_parse_hopopts(skb) < 0) { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); rcu_read_unlock(); return NET_RX_DROP; } } rcu_read_unlock(); /* Must drop socket now because of tproxy. */ skb_orphan(skb); return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, net, NULL, skb, dev, NULL, ip6_rcv_finish); err: __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); drop: rcu_read_unlock(); kfree_skb(skb); return NET_RX_DROP; }
static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { const struct inet6_protocol *ipprot; struct inet6_dev *idev; unsigned int nhoff; int nexthdr; bool raw; bool have_final = false; /* * Parse extension headers */ rcu_read_lock(); resubmit: idev = ip6_dst_idev(skb_dst(skb)); if (!pskb_pull(skb, skb_transport_offset(skb))) goto discard; nhoff = IP6CB(skb)->nhoff; nexthdr = skb_network_header(skb)[nhoff]; resubmit_final: raw = raw6_local_deliver(skb, nexthdr); ipprot = rcu_dereference(inet6_protos[nexthdr]); if (ipprot) { int ret; if (have_final) { if (!(ipprot->flags & INET6_PROTO_FINAL)) { /* Once we've seen a final protocol don't * allow encapsulation on any non-final * ones. This allows foo in UDP encapsulation * to work. */ goto discard; } } else if (ipprot->flags & INET6_PROTO_FINAL) { const struct ipv6hdr *hdr; /* Only do this once for first final protocol */ have_final = true; /* Free reference early: we don't need it any more, and it may hold ip_conntrack module loaded indefinitely. */ nf_reset(skb); skb_postpull_rcsum(skb, skb_network_header(skb), skb_network_header_len(skb)); hdr = ipv6_hdr(skb); if (ipv6_addr_is_multicast(&hdr->daddr) && !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, &hdr->saddr) && !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb))) goto discard; } if (!(ipprot->flags & INET6_PROTO_NOPOLICY) && !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) goto discard; ret = ipprot->handler(skb); if (ret > 0) { if (ipprot->flags & INET6_PROTO_FINAL) { /* Not an extension header, most likely UDP * encapsulation. Use return value as nexthdr * protocol not nhoff (which presumably is * not set by handler). */ nexthdr = ret; goto resubmit_final; } else { goto resubmit; } } else if (ret == 0) { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDELIVERS); } } else { if (!raw) { if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INUNKNOWNPROTOS); icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_NEXTHDR, nhoff); } kfree_skb(skb); } else { __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDELIVERS); consume_skb(skb); } } rcu_read_unlock(); return 0; discard: __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS); rcu_read_unlock(); kfree_skb(skb); return 0; }
int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { const struct ipv6hdr *hdr; u32 pkt_len; struct inet6_dev *idev; struct net *net = dev_net(skb->dev); if (skb->pkt_type == PACKET_OTHERHOST) { kfree_skb(skb); return NET_RX_DROP; } rcu_read_lock(); idev = __in6_dev_get(skb->dev); IP6_UPD_PO_STATS_BH(net, idev, IPSTATS_MIB_IN, skb->len); if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL || !idev || unlikely(idev->cnf.disable_ipv6)) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDISCARDS); goto drop; } memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); IP6CB(skb)->iif = skb_dst(skb) ? ip6_dst_idev(skb_dst(skb))->dev->ifindex : dev->ifindex; if (unlikely(!pskb_may_pull(skb, sizeof(*hdr)))) goto err; hdr = ipv6_hdr(skb); if (hdr->version != 6) goto err; /* * RFC4291 2.5.3 * A packet received on an interface with a destination address * of loopback must be dropped. */ if (!(dev->flags & IFF_LOOPBACK) && ipv6_addr_loopback(&hdr->daddr)) goto err; /* RFC4291 Errata ID: 3480 * Interface-Local scope spans only a single interface on a * node and is useful only for loopback transmission of * multicast. Packets with interface-local scope received * from another node must be discarded. */ if (!(skb->pkt_type == PACKET_LOOPBACK || dev->flags & IFF_LOOPBACK) && ipv6_addr_is_multicast(&hdr->daddr) && IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 1) goto err; /* RFC4291 2.7 * Nodes must not originate a packet to a multicast address whose scope * field contains the reserved value 0; if such a packet is received, it * must be silently dropped. */ if (ipv6_addr_is_multicast(&hdr->daddr) && IPV6_ADDR_MC_SCOPE(&hdr->daddr) == 0) goto err; /* * RFC4291 2.7 * Multicast addresses must not be used as source addresses in IPv6 * packets or appear in any Routing header. */ if (ipv6_addr_is_multicast(&hdr->saddr)) goto err; skb->transport_header = skb->network_header + sizeof(*hdr); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); pkt_len = ntohs(hdr->payload_len); if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) { if (pkt_len + sizeof(struct ipv6hdr) > skb->len) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; } if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); goto drop; } hdr = ipv6_hdr(skb); } if (hdr->nexthdr == NEXTHDR_HOP) { if (ipv6_parse_hopopts(skb) < 0) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); rcu_read_unlock(); return NET_RX_DROP; } } rcu_read_unlock(); skb_orphan(skb); return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish); err: IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); drop: rcu_read_unlock(); #ifdef CONFIG_HTC_NETWORK_MODIFY if (!IS_ERR(skb) && (skb)) kfree_skb(skb); #else kfree_skb(skb); #endif return NET_RX_DROP; }
int gnrc_netif_ipv6_addr_add_internal(gnrc_netif_t *netif, const ipv6_addr_t *addr, unsigned pfx_len, uint8_t flags) { unsigned idx = UINT_MAX; assert((netif != NULL) && (addr != NULL)); assert(!(ipv6_addr_is_multicast(addr) || ipv6_addr_is_unspecified(addr) || ipv6_addr_is_loopback(addr))); assert((pfx_len > 0) && (pfx_len <= 128)); gnrc_netif_acquire(netif); if ((flags & GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_MASK) == GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_TENTATIVE) { /* set to first retransmission */ flags &= ~GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_TENTATIVE; flags |= 0x1; } for (unsigned i = 0; i < GNRC_NETIF_IPV6_ADDRS_NUMOF; i++) { if (ipv6_addr_equal(&netif->ipv6.addrs[i], addr)) { gnrc_netif_release(netif); return i; } if ((idx == UINT_MAX) && (netif->ipv6.addrs_flags[i] == 0)) { idx = i; } } if (idx == UINT_MAX) { gnrc_netif_release(netif); return -ENOMEM; } netif->ipv6.addrs_flags[idx] = flags; memcpy(&netif->ipv6.addrs[idx], addr, sizeof(netif->ipv6.addrs[idx])); #ifdef MODULE_GNRC_IPV6_NIB #if GNRC_IPV6_NIB_CONF_ARSM ipv6_addr_t sol_nodes; int res; /* TODO: SHOULD delay join between 0 and MAX_RTR_SOLICITATION_DELAY * for SLAAC */ ipv6_addr_set_solicited_nodes(&sol_nodes, addr); res = gnrc_netif_ipv6_group_join_internal(netif, &sol_nodes); if (res < 0) { DEBUG("nib: Can't join solicited-nodes of %s on interface %u\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), netif->pid); } #endif /* GNRC_IPV6_NIB_CONF_ARSM */ if (_get_state(netif, idx) == GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID) { void *state = NULL; gnrc_ipv6_nib_pl_t ple; bool in_pl = false; while (gnrc_ipv6_nib_pl_iter(netif->pid, &state, &ple)) { if (ipv6_addr_match_prefix(&ple.pfx, addr) >= pfx_len) { in_pl = true; } } if (!in_pl) { gnrc_ipv6_nib_pl_set(netif->pid, addr, pfx_len, UINT32_MAX, UINT32_MAX); } } #if GNRC_IPV6_NIB_CONF_SLAAC else { /* TODO: send out NS to solicited nodes for DAD probing */ } #endif #else (void)pfx_len; #endif gnrc_netif_release(netif); return idx; }
void ng_icmpv6_echo_req_handle(kernel_pid_t iface, ng_ipv6_hdr_t *ipv6_hdr, ng_icmpv6_echo_t *echo, uint16_t len) { uint8_t *payload = ((uint8_t *)echo) + sizeof(ng_icmpv6_echo_t); ng_pktsnip_t *hdr, *pkt; ng_netreg_entry_t *sendto = NULL; if ((echo == NULL) || (len < sizeof(ng_icmpv6_echo_t))) { DEBUG("icmpv6_echo: echo was NULL or len (%" PRIu16 ") was < sizeof(ng_icmpv6_echo_t)\n", len); return; } pkt = ng_icmpv6_echo_build(NG_ICMPV6_ECHO_REP, byteorder_ntohs(echo->id), byteorder_ntohs(echo->seq), payload, len - sizeof(ng_icmpv6_echo_t)); if (pkt == NULL) { DEBUG("icmpv6_echo: no space left in packet buffer\n"); return; } if (ipv6_addr_is_multicast(&ipv6_hdr->dst)) { hdr = ng_ipv6_hdr_build(pkt, NULL, 0, (uint8_t *)&ipv6_hdr->src, sizeof(ipv6_addr_t)); } else { hdr = ng_ipv6_hdr_build(pkt, (uint8_t *)&ipv6_hdr->dst, sizeof(ipv6_addr_t), (uint8_t *)&ipv6_hdr->src, sizeof(ipv6_addr_t)); } if (hdr == NULL) { DEBUG("icmpv6_echo: no space left in packet buffer\n"); ng_pktbuf_release(pkt); return; } pkt = hdr; hdr = ng_netif_hdr_build(NULL, 0, NULL, 0); ((ng_netif_hdr_t *)hdr->data)->if_pid = iface; LL_PREPEND(pkt, hdr); sendto = ng_netreg_lookup(NG_NETTYPE_IPV6, NG_NETREG_DEMUX_CTX_ALL); if (sendto == NULL) { DEBUG("icmpv6_echo: no receivers for IPv6 packets\n"); ng_pktbuf_release(pkt); return; } /* ICMPv6 is not interested anymore so `- 1` */ ng_pktbuf_hold(pkt, ng_netreg_num(NG_NETTYPE_IPV6, NG_NETREG_DEMUX_CTX_ALL) - 1); while (sendto != NULL) { ng_netapi_send(sendto->pid, pkt); sendto = ng_netreg_getnext(sendto); } }
static int _netif_add(char *cmd_name, kernel_pid_t dev, int argc, char **argv) { #ifdef MODULE_GNRC_IPV6_NETIF enum { _UNICAST = 0, _MULTICAST, /* multicast value just to check if given addr is mc */ _ANYCAST } type = _UNICAST; char *addr_str = argv[0]; ipv6_addr_t addr; uint8_t prefix_len; if (argc > 1) { if (strcmp(argv[0], "anycast") == 0) { type = _ANYCAST; addr_str = argv[1]; } else if (strcmp(argv[0], "multicast") == 0) { type = _MULTICAST; addr_str = argv[1]; } else if (strcmp(argv[0], "unicast") == 0) { /* type already set to unicast */ addr_str = argv[1]; } else { _add_usage(cmd_name); return 1; } } prefix_len = _get_prefix_len(addr_str); if (ipv6_addr_from_str(&addr, addr_str) == NULL) { puts("error: unable to parse IPv6 address."); return 1; } if ((argc > 1) && (ipv6_addr_is_multicast(&addr)) && (type != _MULTICAST)) { puts("error: address was not a multicast address."); return 1; } if (gnrc_ipv6_netif_add_addr(dev, &addr, prefix_len, (type == _ANYCAST) ? GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST : GNRC_IPV6_NETIF_ADDR_FLAGS_UNICAST) == NULL) { printf("error: unable to add IPv6 address\n"); return 1; } printf("success: added %s/%d to interface %" PRIkernel_pid "\n", addr_str, prefix_len, dev); return 0; #else (void)cmd_name; (void)dev; (void)argc; (void)argv; puts("error: unable to add IPv6 address."); return 1; #endif }
int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh) { int err; struct rtmsg *r; struct rt6_info *rt; struct net_device *dev = NULL; int addr_type; if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128) return -EINVAL; #ifndef CONFIG_IPV6_SUBTREES if (rtmsg->rtmsg_src_len) return -EINVAL; #endif if (rtmsg->rtmsg_metric == 0) rtmsg->rtmsg_metric = IP6_RT_PRIO_USER; rt = dst_alloc(&ip6_dst_ops); if (rt == NULL) return -ENOMEM; rt->u.dst.obsolete = -1; rt->rt6i_expires = rtmsg->rtmsg_info; if (nlh && (r = NLMSG_DATA(nlh))) { rt->rt6i_protocol = r->rtm_protocol; } else { rt->rt6i_protocol = RTPROT_BOOT; } addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst); if (addr_type & IPV6_ADDR_MULTICAST) rt->u.dst.input = ip6_mc_input; else rt->u.dst.input = ip6_forward; rt->u.dst.output = ip6_output; if (rtmsg->rtmsg_ifindex) { dev = dev_get_by_index(rtmsg->rtmsg_ifindex); err = -ENODEV; if (dev == NULL) goto out; } ipv6_addr_prefix(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst, rtmsg->rtmsg_dst_len); rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len; if (rt->rt6i_dst.plen == 128) rt->u.dst.flags = DST_HOST; #ifdef CONFIG_IPV6_SUBTREES ipv6_addr_prefix(&rt->rt6i_src.addr, &rtmsg->rtmsg_src, rtmsg->rtmsg_src_len); rt->rt6i_src.plen = rtmsg->rtmsg_src_len; #endif rt->rt6i_metric = rtmsg->rtmsg_metric; /* We cannot add true routes via loopback here, they would result in kernel looping; promote them to reject routes */ if ((rtmsg->rtmsg_flags&RTF_REJECT) || (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { if (dev) dev_put(dev); dev = &loopback_dev; dev_hold(dev); rt->u.dst.output = ip6_pkt_discard; rt->u.dst.input = ip6_pkt_discard; rt->u.dst.error = -ENETUNREACH; rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; goto install_route; } if (rtmsg->rtmsg_flags & RTF_GATEWAY) { struct in6_addr *gw_addr; int gwa_type; gw_addr = &rtmsg->rtmsg_gateway; ipv6_addr_copy(&rt->rt6i_gateway, &rtmsg->rtmsg_gateway); gwa_type = ipv6_addr_type(gw_addr); if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { struct rt6_info *grt; /* IPv6 strictly inhibits using not link-local addresses as nexthop address. Otherwise, router will not able to send redirects. It is very good, but in some (rare!) curcumstances (SIT, PtP, NBMA NOARP links) it is handy to allow some exceptions. --ANK */ err = -EINVAL; if (!(gwa_type&IPV6_ADDR_UNICAST)) goto out; grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1); err = -EHOSTUNREACH; if (grt == NULL) goto out; if (dev) { if (dev != grt->rt6i_dev) { dst_release(&grt->u.dst); goto out; } } else { dev = grt->rt6i_dev; dev_hold(dev); } if (!(grt->rt6i_flags&RTF_GATEWAY)) err = 0; dst_release(&grt->u.dst); if (err) goto out; } err = -EINVAL; if (dev == NULL || (dev->flags&IFF_LOOPBACK)) goto out; } err = -ENODEV; if (dev == NULL) goto out; if (rtmsg->rtmsg_flags & (RTF_GATEWAY|RTF_NONEXTHOP)) { rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev); if (IS_ERR(rt->rt6i_nexthop)) { err = PTR_ERR(rt->rt6i_nexthop); rt->rt6i_nexthop = NULL; goto out; } } if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS; else rt->rt6i_hoplimit = ipv6_get_hoplimit(dev); rt->rt6i_flags = rtmsg->rtmsg_flags; install_route: rt->u.dst.pmtu = ipv6_get_mtu(dev); rt->u.dst.advmss = max_t(unsigned int, rt->u.dst.pmtu - 60, ip6_rt_min_advmss); /* Maximal non-jumbo IPv6 payload is 65535 and corresponding MSS is 65535 - tcp_header_size. 65535 is also valid and means: "any MSS, rely only on pmtu discovery" */ if (rt->u.dst.advmss > 65535-20) rt->u.dst.advmss = 65535; rt->u.dst.dev = dev; return rt6_ins(rt, nlh); out: if (dev) dev_put(dev); dst_free((struct dst_entry *) rt); return err; }
void gnrc_ndp_nbr_sol_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, ndp_nbr_sol_t *nbr_sol, size_t icmpv6_size) { uint16_t opt_offset = 0; uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX]; uint8_t *buf = ((uint8_t *)nbr_sol) + sizeof(ndp_nbr_sol_t); ipv6_addr_t *tgt, nbr_adv_dst; gnrc_pktsnip_t *nbr_adv_opts = NULL; #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER ndp_opt_t *sl2a_opt = NULL; sixlowpan_nd_opt_ar_t *ar_opt = NULL; #endif int sicmpv6_size = (int)icmpv6_size, l2src_len = 0; DEBUG("ndp: received neighbor solicitation (src: %s, ", ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str))); DEBUG("dst: %s, ", ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str))); DEBUG("tgt: %s)\n", ipv6_addr_to_str(addr_str, &nbr_sol->tgt, sizeof(addr_str))); /* check validity */ if ((ipv6->hl != 255) || (nbr_sol->code != 0) || (icmpv6_size < sizeof(ndp_nbr_sol_t)) || ipv6_addr_is_multicast(&nbr_sol->tgt) || (ipv6_addr_is_unspecified(&ipv6->src) && ipv6_addr_is_solicited_node(&ipv6->dst))) { DEBUG("ndp: neighbor solicitation was invalid.\n"); /* ipv6 releases */ return; } if ((tgt = gnrc_ipv6_netif_find_addr(iface, &nbr_sol->tgt)) == NULL) { DEBUG("ndp: Target address is not to interface %" PRIkernel_pid "\n", iface); /* ipv6 releases */ return; } sicmpv6_size -= sizeof(ndp_nbr_sol_t); while (sicmpv6_size > 0) { ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset); switch (opt->type) { case NDP_OPT_SL2A: if ((l2src_len = gnrc_ndp_internal_sl2a_opt_handle(pkt, ipv6, nbr_sol->type, opt, l2src)) < 0) { /* -ENOTSUP can not happen, since the function only returns this for invalid * message types containing the SL2A. Neighbor solicitations are not an * invalid message type for SL2A. According to that, we don't need to watch * out for that here, but regardless, the source link-layer address option * is invalid. */ return; } #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER sl2a_opt = opt; break; case NDP_OPT_AR: /* actually handling at the end of the function (see below) */ ar_opt = (sixlowpan_nd_opt_ar_t *)opt; #endif break; default: /* silently discard all other options */ break; } opt_offset += (opt->len * 8); sicmpv6_size -= (opt->len * 8); #if ENABLE_DEBUG if (sicmpv6_size < 0) { DEBUG("ndp: Option parsing out of sync.\n"); } #endif } #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER gnrc_ipv6_netif_t *ipv6_iface = gnrc_ipv6_netif_get(iface); assert(ipv6_iface != NULL); if ((sl2a_opt != NULL) && (ar_opt != NULL) && (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && (ipv6_iface->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { uint8_t status = gnrc_sixlowpan_nd_opt_ar_handle(iface, ipv6, nbr_sol->type, &ipv6->src, ar_opt, l2src, l2src_len); /* check for multihop DAD return */ nbr_adv_opts = gnrc_sixlowpan_nd_opt_ar_build(status, GNRC_SIXLOWPAN_ND_AR_LTIME, &ar_opt->eui64, NULL); if (status == 0) { memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t)); } else { /* see https://tools.ietf.org/html/rfc6775#section-6.5.2 */ eui64_t iid; ieee802154_get_iid(&iid, ar_opt->eui64.uint8, sizeof(eui64_t)); ipv6_addr_set_iid(&nbr_adv_dst, iid.uint64.u64); ipv6_addr_set_link_local_prefix(&nbr_adv_dst); } } else { /* gnrc_sixlowpan_nd_opt_ar_handle updates neighbor cache */ _stale_nc(iface, &ipv6->src, l2src, l2src_len); memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t)); } #else _stale_nc(iface, &ipv6->src, l2src, l2src_len); memcpy(&nbr_adv_dst, &ipv6->src, sizeof(ipv6_addr_t)); #endif gnrc_ndp_internal_send_nbr_adv(iface, tgt, &nbr_adv_dst, ipv6_addr_is_multicast(&ipv6->dst), nbr_adv_opts); }
void gnrc_ndp_rtr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, ndp_rtr_adv_t *rtr_adv, size_t icmpv6_size) { uint8_t *buf = (uint8_t *)(rtr_adv + 1); gnrc_ipv6_nc_t *nc_entry = NULL; gnrc_ipv6_netif_t *if_entry = gnrc_ipv6_netif_get(iface); uint8_t l2src[GNRC_IPV6_NC_L2_ADDR_MAX]; #ifdef MODULE_GNRC_SIXLOWPAN_ND uint32_t next_rtr_sol = 0; #endif int sicmpv6_size = (int)icmpv6_size, l2src_len = 0; uint16_t opt_offset = 0; if (!ipv6_addr_is_link_local(&ipv6->src) || ipv6_addr_is_multicast(&ipv6->src) || (ipv6->hl != 255) || (rtr_adv->code != 0) || (icmpv6_size < sizeof(ndp_rtr_adv_t))) { DEBUG("ndp: router advertisement was invalid\n"); /* ipv6 releases */ return; } /* get source from default router list */ nc_entry = gnrc_ipv6_nc_get(iface, &ipv6->src); if (nc_entry == NULL) { /* not in default router list */ /* create default router list entry */ nc_entry = gnrc_ipv6_nc_add(iface, &ipv6->src, NULL, 0, GNRC_IPV6_NC_IS_ROUTER); if (nc_entry == NULL) { DEBUG("ndp: error on default router list entry creation\n"); return; } } else if ((nc_entry->flags & GNRC_IPV6_NC_IS_ROUTER) && (byteorder_ntohs(rtr_adv->ltime) == 0)) { nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER; } else { nc_entry->flags |= GNRC_IPV6_NC_IS_ROUTER; } /* set router life timer */ if (rtr_adv->ltime.u16 != 0) { uint16_t ltime = byteorder_ntohs(rtr_adv->ltime); #ifdef MODULE_GNRC_SIXLOWPAN_ND next_rtr_sol = ltime; #endif xtimer_set_msg(&nc_entry->rtr_timeout, (ltime * SEC_IN_USEC), &nc_entry->rtr_timeout_msg, thread_getpid()); } /* set current hop limit from message if available */ if (rtr_adv->cur_hl != 0) { if_entry->cur_hl = rtr_adv->cur_hl; } /* set flags from message */ if_entry->flags &= ~GNRC_IPV6_NETIF_FLAGS_RTR_ADV_MASK; if_entry->flags |= (rtr_adv->flags << GNRC_IPV6_NETIF_FLAGS_RTR_ADV_POS) & GNRC_IPV6_NETIF_FLAGS_RTR_ADV_MASK; /* set reachable time from message if it is not the same as the random base * value */ if ((rtr_adv->reach_time.u32 != 0) && (if_entry->reach_time_base != byteorder_ntohl(rtr_adv->reach_time))) { _set_reach_time(if_entry, byteorder_ntohl(rtr_adv->reach_time)); } /* set retransmission timer from message */ if (rtr_adv->retrans_timer.u32 != 0) { if_entry->retrans_timer = timex_set(0, byteorder_ntohl(rtr_adv->retrans_timer)); timex_normalize(&if_entry->retrans_timer); } mutex_unlock(&if_entry->mutex); sicmpv6_size -= sizeof(ndp_rtr_adv_t); /* parse options */ while (sicmpv6_size > 0) { ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset); switch (opt->type) { case NDP_OPT_SL2A: if ((l2src_len = gnrc_ndp_internal_sl2a_opt_handle(pkt, ipv6, rtr_adv->type, opt, l2src)) < 0) { /* -ENOTSUP can not happen */ /* invalid source link-layer address option */ return; } break; case NDP_OPT_MTU: if (!gnrc_ndp_internal_mtu_opt_handle(iface, rtr_adv->type, (ndp_opt_mtu_t *)opt)) { /* invalid MTU option */ return; } break; case NDP_OPT_PI: if (!gnrc_ndp_internal_pi_opt_handle(iface, rtr_adv->type, (ndp_opt_pi_t *)opt)) { /* invalid prefix information option */ return; } #ifdef MODULE_GNRC_SIXLOWPAN_ND uint32_t valid_ltime = byteorder_ntohl(((ndp_opt_pi_t *)opt)->valid_ltime); if ((valid_ltime != 0) && (valid_ltime < next_rtr_sol)) { next_rtr_sol = valid_ltime; } #endif break; #ifdef MODULE_GNRC_SIXLOWPAN_ND case NDP_OPT_6CTX: if (!gnrc_sixlowpan_nd_opt_6ctx_handle(rtr_adv->type, (sixlowpan_nd_opt_6ctx_t *)opt)) { /* invalid 6LoWPAN context option */ return; } uint16_t ltime = byteorder_ntohs(((sixlowpan_nd_opt_6ctx_t *)opt)->ltime); if ((ltime != 0) && (ltime < (next_rtr_sol / 60))) { next_rtr_sol = ltime * 60; } break; #endif #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER case NDP_OPT_ABR: gnrc_sixlowpan_nd_opt_abr_handle(iface, rtr_adv, icmpv6_size, (sixlowpan_nd_opt_abr_t *)opt); break; #endif } opt_offset += (opt->len * 8); sicmpv6_size -= (opt->len * 8); #if ENABLE_DEBUG if (sicmpv6_size < 0) { DEBUG("ndp: Option parsing out of sync.\n"); } #endif } #if ENABLE_DEBUG && defined(MODULE_GNRC_SIXLOWPAN_ND) if ((if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) && (l2src_len <= 0)) { DEBUG("ndp: Router advertisement did not contain any source address information\n"); } #endif _stale_nc(iface, &ipv6->src, l2src, l2src_len); /* stop multicast router solicitation retransmission timer */ xtimer_remove(&if_entry->rtr_sol_timer); #ifdef MODULE_GNRC_SIXLOWPAN_ND if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) { /* 3/4 of the time should be "well before" enough the respective timeout * not to run out; see https://tools.ietf.org/html/rfc6775#section-5.4.3 */ next_rtr_sol *= 3; next_rtr_sol = (next_rtr_sol > 4) ? (next_rtr_sol >> 2) : 1; /* according to https://tools.ietf.org/html/rfc6775#section-5.3: * "In all cases, the RS retransmissions are terminated when an RA is * received." * Hence, reset router solicitation counter and reset timer. */ if_entry->rtr_sol_count = 0; gnrc_sixlowpan_nd_rtr_sol_reschedule(nc_entry, next_rtr_sol); gnrc_ndp_internal_send_nbr_sol(nc_entry->iface, NULL, &nc_entry->ipv6_addr, &nc_entry->ipv6_addr); if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) { gnrc_ipv6_netif_set_rtr_adv(if_entry, true); } }
void gnrc_ndp_nbr_adv_handle(kernel_pid_t iface, gnrc_pktsnip_t *pkt, ipv6_hdr_t *ipv6, ndp_nbr_adv_t *nbr_adv, size_t icmpv6_size) { uint16_t opt_offset = 0; uint8_t *buf = ((uint8_t *)nbr_adv) + sizeof(ndp_nbr_adv_t); int l2tgt_len = 0; uint8_t l2tgt[GNRC_IPV6_NC_L2_ADDR_MAX]; int sicmpv6_size = (int)icmpv6_size; gnrc_ipv6_nc_t *nc_entry = gnrc_ipv6_nc_get(iface, &nbr_adv->tgt); gnrc_pktsnip_t *netif; gnrc_netif_hdr_t *netif_hdr = NULL; DEBUG("ndp: received neighbor advertisement (src: %s, ", ipv6_addr_to_str(addr_str, &ipv6->src, sizeof(addr_str))); DEBUG("dst: %s, ", ipv6_addr_to_str(addr_str, &ipv6->dst, sizeof(addr_str))); DEBUG("tgt: %s)\n", ipv6_addr_to_str(addr_str, &nbr_adv->tgt, sizeof(addr_str))); /* check validity */ if ((ipv6->hl != 255) || (nbr_adv->code != 0) || (icmpv6_size < sizeof(ndp_nbr_adv_t)) || ipv6_addr_is_multicast(&nbr_adv->tgt)) { DEBUG("ndp: neighbor advertisement was invalid.\n"); /* ipv6 releases */ return; } if (nc_entry == NULL) { /* see https://tools.ietf.org/html/rfc4861#section-7.2.5 */ DEBUG("ndp: no neighbor cache entry found for advertisement's target\n"); /* ipv6 releases */ return; } sicmpv6_size -= sizeof(ndp_nbr_adv_t); while (sicmpv6_size > 0) { ndp_opt_t *opt = (ndp_opt_t *)(buf + opt_offset); switch (opt->type) { case NDP_OPT_TL2A: if ((l2tgt_len = gnrc_ndp_internal_tl2a_opt_handle(pkt, ipv6, nbr_adv->type, opt, l2tgt)) < 0) { /* invalid target link-layer address option */ return; } break; #ifdef MODULE_GNRC_SIXLOWPAN_ND case NDP_OPT_AR: /* address registration option is always ignored when invalid */ gnrc_sixlowpan_nd_opt_ar_handle(iface, ipv6, nbr_adv->type, &nbr_adv->tgt, (sixlowpan_nd_opt_ar_t *)opt, NULL, 0); break; #endif default: /* silently discard all other options */ break; } opt_offset += (opt->len * 8); sicmpv6_size -= (opt->len * 8); #if ENABLE_DEBUG if (sicmpv6_size < 0) { DEBUG("ndp: Option parsing out of sync.\n"); } #endif } LL_SEARCH_SCALAR(pkt, netif, type, GNRC_NETTYPE_NETIF); if (netif != NULL) { netif_hdr = netif->data; } if (l2tgt_len != -ENOTSUP) { #ifdef MODULE_GNRC_SIXLOWPAN_ND /* check if entry wasn't removed by ARO, ideally there should not be any TL2A in here */ nc_entry = gnrc_ipv6_nc_get(iface, &nbr_adv->tgt); if (nc_entry == NULL) { return; } #endif if (gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_INCOMPLETE) { if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len == 0)) { /* link-layer has addresses, but no TLLAO supplied: discard silently * (see https://tools.ietf.org/html/rfc4861#section-7.2.5) */ return; } nc_entry->iface = iface; nc_entry->l2_addr_len = l2tgt_len; memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len); if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_REACHABLE); } else { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); } if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_R) { nc_entry->flags |= GNRC_IPV6_NC_IS_ROUTER; } else { nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER; /* TODO: update state of neighbor as router in FIB? */ } #ifdef MODULE_GNRC_NDP_NODE gnrc_pktqueue_t *queued_pkt; while ((queued_pkt = gnrc_pktqueue_remove_head(&nc_entry->pkts)) != NULL) { if (gnrc_netapi_send(gnrc_ipv6_pid, queued_pkt->pkt) < 1) { DEBUG("ndp: unable to send queued packet\n"); gnrc_pktbuf_release(queued_pkt->pkt); } queued_pkt->pkt = NULL; } #endif } else { /* first or-term: no link-layer, but nc_entry has l2addr, * second or-term: different l2addr cached */ bool l2tgt_changed = false; if ((!_pkt_has_l2addr(netif_hdr)) && (l2tgt_len == 0)) { /* there was previously a L2 address registered */ l2tgt_changed = (nc_entry->l2_addr_len != 0); } /* link-layer has addresses and TLLAO with different address */ else if (_pkt_has_l2addr(netif_hdr) && (l2tgt_len != 0)) { l2tgt_changed = (!(l2tgt_len == nc_entry->l2_addr_len)) && (memcmp(nc_entry->l2_addr, l2tgt, l2tgt_len) == 0); } if ((nbr_adv->flags & NDP_NBR_ADV_FLAGS_O) || !l2tgt_changed || (l2tgt_len == 0)) { if (l2tgt_len != 0) { nc_entry->iface = iface; nc_entry->l2_addr_len = l2tgt_len; memcpy(nc_entry->l2_addr, l2tgt, l2tgt_len); } if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_S) { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_REACHABLE); } else if (l2tgt_changed) { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); } if (nbr_adv->flags & NDP_NBR_ADV_FLAGS_R) { nc_entry->flags |= GNRC_IPV6_NC_IS_ROUTER; } else { nc_entry->flags &= ~GNRC_IPV6_NC_IS_ROUTER; /* TODO: update state of neighbor as router in FIB? */ } } else if (l2tgt_changed && gnrc_ipv6_nc_get_state(nc_entry) == GNRC_IPV6_NC_STATE_REACHABLE) { gnrc_ndp_internal_set_state(nc_entry, GNRC_IPV6_NC_STATE_STALE); } } } return; }
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; }
int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr) { struct ipv6_pinfo *np = inet6_sk(sk); struct net_device *dev = NULL; struct inet6_dev *idev; struct ipv6_ac_socklist *pac; int ishost = !ipv6_devconf.forwarding; int err = 0; if (!capable(CAP_NET_ADMIN)) return -EPERM; if (ipv6_addr_is_multicast(addr)) return -EINVAL; if (ipv6_chk_addr(addr, NULL, 0)) return -EINVAL; pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL); if (pac == NULL) return -ENOMEM; pac->acl_next = NULL; ipv6_addr_copy(&pac->acl_addr, addr); if (ifindex == 0) { struct rt6_info *rt; rt = rt6_lookup(addr, NULL, 0, 0); if (rt) { dev = rt->rt6i_dev; dev_hold(dev); dst_release(&rt->u.dst); } else if (ishost) { err = -EADDRNOTAVAIL; goto out_free_pac; } else { /* router, no matching interface: just pick one */ dev = dev_get_by_flags(&init_net, IFF_UP, IFF_UP|IFF_LOOPBACK); } } else dev = dev_get_by_index(&init_net, ifindex); if (dev == NULL) { err = -ENODEV; goto out_free_pac; } idev = in6_dev_get(dev); if (!idev) { if (ifindex) err = -ENODEV; else err = -EADDRNOTAVAIL; goto out_dev_put; } /* reset ishost, now that we have a specific device */ ishost = !idev->cnf.forwarding; in6_dev_put(idev); pac->acl_ifindex = dev->ifindex; /* XXX * For hosts, allow link-local or matching prefix anycasts. * This obviates the need for propagating anycast routes while * still allowing some non-router anycast participation. */ if (!ip6_onlink(addr, dev)) { if (ishost) err = -EADDRNOTAVAIL; if (err) goto out_dev_put; } err = ipv6_dev_ac_inc(dev, addr); if (err) goto out_dev_put; write_lock_bh(&ipv6_sk_ac_lock); pac->acl_next = np->ipv6_ac_list; np->ipv6_ac_list = pac; write_unlock_bh(&ipv6_sk_ac_lock); dev_put(dev); return 0; out_dev_put: dev_put(dev); out_free_pac: sock_kfree_s(sk, pac, sizeof(*pac)); return err; }
static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr) { kernel_pid_t iface = KERNEL_PID_UNDEF; gnrc_pktsnip_t *ipv6, *payload; ipv6_addr_t *tmp; ipv6_hdr_t *hdr; /* get IPv6 snip and (if present) generic interface header */ if (pkt->type == GNRC_NETTYPE_NETIF) { /* If there is already a netif header (routing protocols and * neighbor discovery might add them to preset sending interface) */ iface = ((gnrc_netif_hdr_t *)pkt->data)->if_pid; /* seize payload as temporary variable */ ipv6 = gnrc_pktbuf_start_write(pkt); /* write protect for later removal * in _send_unicast() */ if (ipv6 == NULL) { DEBUG("ipv6: unable to get write access to netif header, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } pkt = ipv6; /* Reset pkt from temporary variable */ ipv6 = pkt->next; } else { ipv6 = pkt; } /* seize payload as temporary variable */ payload = gnrc_pktbuf_start_write(ipv6); if (payload == NULL) { DEBUG("ipv6: unable to get write access to IPv6 header, dropping packet\n"); gnrc_pktbuf_release(pkt); return; } if (ipv6 != pkt) { /* in case packet has netif header */ pkt->next = payload;/* pkt is already write-protected so we can do that */ } else { pkt = payload; /* pkt is the IPv6 header so we just write-protected it */ } ipv6 = payload; /* Reset ipv6 from temporary variable */ hdr = ipv6->data; payload = ipv6->next; if (ipv6_addr_is_multicast(&hdr->dst)) { _send_multicast(iface, pkt, ipv6, payload, prep_hdr); } else if ((ipv6_addr_is_loopback(&hdr->dst)) || /* dst is loopback address */ ((iface == KERNEL_PID_UNDEF) && /* or dst registered to any local interface */ ((iface = gnrc_ipv6_netif_find_by_addr(&tmp, &hdr->dst)) != KERNEL_PID_UNDEF)) || ((iface != KERNEL_PID_UNDEF) && /* or dst registered to given interface */ (gnrc_ipv6_netif_find_addr(iface, &hdr->dst) != NULL))) { uint8_t *rcv_data; gnrc_pktsnip_t *ptr = ipv6, *rcv_pkt; if (prep_hdr) { if (_fill_ipv6_hdr(iface, ipv6, payload) < 0) { /* error on filling up header */ gnrc_pktbuf_release(pkt); return; } } rcv_pkt = gnrc_pktbuf_add(NULL, NULL, gnrc_pkt_len(ipv6), GNRC_NETTYPE_IPV6); if (rcv_pkt == NULL) { DEBUG("ipv6: error on generating loopback packet\n"); gnrc_pktbuf_release(pkt); return; } rcv_data = rcv_pkt->data; /* "reverse" packet (by making it one snip as if received from NIC) */ while (ptr != NULL) { memcpy(rcv_data, ptr->data, ptr->size); rcv_data += ptr->size; ptr = ptr->next; } gnrc_pktbuf_release(pkt); DEBUG("ipv6: packet is addressed to myself => loopback\n"); if (gnrc_netapi_receive(gnrc_ipv6_pid, rcv_pkt) < 1) { DEBUG("ipv6: unable to deliver packet\n"); gnrc_pktbuf_release(rcv_pkt); } } else { uint8_t l2addr_len = GNRC_IPV6_NC_L2_ADDR_MAX; uint8_t l2addr[l2addr_len]; iface = _next_hop_l2addr(l2addr, &l2addr_len, iface, &hdr->dst, pkt); if (iface == KERNEL_PID_UNDEF) { DEBUG("ipv6: error determining next hop's link layer address\n"); gnrc_pktbuf_release(pkt); return; } if (prep_hdr) { if (_fill_ipv6_hdr(iface, ipv6, payload) < 0) { /* error on filling up header */ gnrc_pktbuf_release(pkt); return; } } _send_unicast(iface, l2addr, l2addr_len, pkt); } }
/* tipc_send_msg - enqueue a send request */ static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb, struct tipc_bearer *b, struct tipc_media_addr *dest) { int ttl, err = 0; struct udp_bearer *ub; struct udp_media_addr *dst = (struct udp_media_addr *)&dest->value; struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value; struct rtable *rt; if (skb_headroom(skb) < UDP_MIN_HEADROOM) { err = pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC); if (err) goto tx_error; } skb_set_inner_protocol(skb, htons(ETH_P_TIPC)); ub = rcu_dereference_rtnl(b->media_ptr); if (!ub) { err = -ENODEV; goto tx_error; } if (dst->proto == htons(ETH_P_IP)) { struct flowi4 fl = { .daddr = dst->ipv4.s_addr, .saddr = src->ipv4.s_addr, .flowi4_mark = skb->mark, .flowi4_proto = IPPROTO_UDP }; rt = ip_route_output_key(net, &fl); if (IS_ERR(rt)) { err = PTR_ERR(rt); goto tx_error; } skb->dev = rt->dst.dev; ttl = ip4_dst_hoplimit(&rt->dst); udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr, dst->ipv4.s_addr, 0, ttl, 0, src->port, dst->port, false, true); #if IS_ENABLED(CONFIG_IPV6) } else { struct dst_entry *ndst; struct flowi6 fl6 = { .flowi6_oif = ub->ifindex, .daddr = dst->ipv6, .saddr = src->ipv6, .flowi6_proto = IPPROTO_UDP }; err = ipv6_stub->ipv6_dst_lookup(net, ub->ubsock->sk, &ndst, &fl6); if (err) goto tx_error; ttl = ip6_dst_hoplimit(ndst); err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, ndst->dev, &src->ipv6, &dst->ipv6, 0, ttl, 0, src->port, dst->port, false); #endif } return err; tx_error: kfree_skb(skb); return err; } /* tipc_udp_recv - read data from bearer socket */ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb) { struct udp_bearer *ub; struct tipc_bearer *b; ub = rcu_dereference_sk_user_data(sk); if (!ub) { pr_err_ratelimited("Failed to get UDP bearer reference"); kfree_skb(skb); return 0; } skb_pull(skb, sizeof(struct udphdr)); rcu_read_lock(); b = rcu_dereference_rtnl(ub->bearer); if (b) { tipc_rcv(sock_net(sk), skb, b); rcu_read_unlock(); return 0; } rcu_read_unlock(); kfree_skb(skb); return 0; } static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote) { int err = 0; struct ip_mreqn mreqn; struct sock *sk = ub->ubsock->sk; if (ntohs(remote->proto) == ETH_P_IP) { if (!ipv4_is_multicast(remote->ipv4.s_addr)) return 0; mreqn.imr_multiaddr = remote->ipv4; mreqn.imr_ifindex = ub->ifindex; err = ip_mc_join_group(sk, &mreqn); #if IS_ENABLED(CONFIG_IPV6) } else { if (!ipv6_addr_is_multicast(&remote->ipv6)) return 0; err = ipv6_stub->ipv6_sock_mc_join(sk, ub->ifindex, &remote->ipv6); #endif } return err; }
int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { const struct ipv6hdr *hdr; u32 pkt_len; struct inet6_dev *idev; struct net *net = dev_net(skb->dev); if (skb->pkt_type == PACKET_OTHERHOST) { kfree_skb(skb); return NET_RX_DROP; } rcu_read_lock(); idev = __in6_dev_get(skb->dev); IP6_UPD_PO_STATS_BH(net, idev, IPSTATS_MIB_IN, skb->len); if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL || !idev || unlikely(idev->cnf.disable_ipv6)) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDISCARDS); goto drop; } memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm)); /* * Store incoming device index. When the packet will * be queued, we cannot refer to skb->dev anymore. * * BTW, when we send a packet for our own local address on a * non-loopback interface (e.g. ethX), it is being delivered * via the loopback interface (lo) here; skb->dev = loopback_dev. * It, however, should be considered as if it is being * arrived via the sending interface (ethX), because of the * nature of scoping architecture. --yoshfuji */ IP6CB(skb)->iif = skb_dst(skb) ? ip6_dst_idev(skb_dst(skb))->dev->ifindex : dev->ifindex; if (unlikely(!pskb_may_pull(skb, sizeof(*hdr)))) goto err; hdr = ipv6_hdr(skb); if (hdr->version != 6) goto err; /* * RFC4291 2.5.3 * A packet received on an interface with a destination address * of loopback must be dropped. */ if (!(dev->flags & IFF_LOOPBACK) && ipv6_addr_loopback(&hdr->daddr)) goto err; /* * RFC4291 2.7 * Multicast addresses must not be used as source addresses in IPv6 * packets or appear in any Routing header. */ if (ipv6_addr_is_multicast(&hdr->saddr)) goto err; skb->transport_header = skb->network_header + sizeof(*hdr); IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); pkt_len = ntohs(hdr->payload_len); /* pkt_len may be zero if Jumbo payload option is present */ if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) { if (pkt_len + sizeof(struct ipv6hdr) > skb->len) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; } if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); goto drop; } hdr = ipv6_hdr(skb); } if (hdr->nexthdr == NEXTHDR_HOP) { if (ipv6_parse_hopopts(skb) < 0) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); rcu_read_unlock(); return NET_RX_DROP; } } rcu_read_unlock(); /* Must drop socket now because of tproxy. */ skb_orphan(skb); return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish); err: IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS); drop: rcu_read_unlock(); kfree_skb(skb); return NET_RX_DROP; }
/* <DTS2012022805249 g00206962 2012/2/27 add begin */ static inline int ip6_input_icmp_cheat(struct sk_buff *skb) { struct inet6_protocol *ipprot; unsigned int nhoff; int nexthdr, raw; u8 hash; struct inet6_dev *idev; struct net *net = dev_net(skb->dst->dev); /* * Parse extension headers */ rcu_read_lock(); resubmit: idev = ip6_dst_idev(skb->dst); if (!pskb_pull(skb, skb_transport_offset(skb))) goto discard; nhoff = IP6CB(skb)->nhoff; nexthdr = skb_network_header(skb)[nhoff]; raw = raw6_local_deliver(skb, nexthdr); hash = nexthdr & (MAX_INET_PROTOS - 1); if ((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) { int ret; if (ipprot->flags & INET6_PROTO_FINAL) { struct ipv6hdr *hdr; /* Free reference early: we don't need it any more, and it may hold ip_conntrack module loaded indefinitely. */ nf_reset(skb); skb_postpull_rcsum(skb, skb_network_header(skb), skb_network_header_len(skb)); hdr = ipv6_hdr(skb); /* if (ipv6_addr_is_multicast(&hdr->daddr) && !ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, &hdr->saddr) && !ipv6_is_mld(skb, nexthdr)) goto discard; */ if (ipv6_addr_is_multicast(&hdr->daddr) && IPPROTO_ICMPV6 != nexthdr) goto discard; else if (!ipv6_addr_is_multicast(&hdr->daddr)) goto discard; } if (!(ipprot->flags & INET6_PROTO_NOPOLICY) && !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) goto discard; ret = ipprot->handler(skb); if (ret > 0) goto resubmit; else if (ret == 0) IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDELIVERS); } else { if (!raw) { if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INUNKNOWNPROTOS); icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_NEXTHDR, nhoff, skb->dev); } } else IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDELIVERS); kfree_skb(skb); } rcu_read_unlock(); return 0; discard: IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INDISCARDS); rcu_read_unlock(); kfree_skb(skb); return 0; }
int ipv6_send_packet(ipv6_hdr_t *packet) { uint16_t length = IPV6_HDR_LEN + NTOHS(packet->length); ndp_neighbor_cache_t *nce; DEBUGF("Got a packet to send to %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &packet->destaddr)); ipv6_net_if_get_best_src_addr(&packet->srcaddr, &packet->destaddr); if (!ipv6_addr_is_multicast(&packet->destaddr) && ndp_addr_is_on_link(&packet->destaddr)) { /* not multicast, on-link */ nce = ndp_get_ll_address(&packet->destaddr); if (nce == NULL || sixlowpan_lowpan_sendto(nce->if_id, &nce->lladdr, nce->lladdr_len, (uint8_t *)packet, length) < 0) { /* XXX: this is wrong, but until ND does work correctly, * this is the only way (aka the old way)*/ uint16_t raddr = NTOHS(packet->destaddr.uint16[7]); sixlowpan_lowpan_sendto(0, &raddr, 2, (uint8_t *)packet, length); /* return -1; */ } return length; } else { /* see if dest should be routed to a different next hop */ if (ipv6_addr_is_multicast(&packet->destaddr)) { /* if_id will be ignored */ uint16_t addr = 0xffff; return sixlowpan_lowpan_sendto(0, &addr, 2, (uint8_t *)packet, length); } if (ip_get_next_hop == NULL) { return -1; } DEBUG("Trying to find the next hop for %s\n", ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &packet->destaddr)); ipv6_addr_t *dest = ip_get_next_hop(&packet->destaddr); if (dest == NULL) { return -1; } nce = ndp_get_ll_address(dest); if (nce == NULL || sixlowpan_lowpan_sendto(nce->if_id, &nce->lladdr, nce->lladdr_len, (uint8_t *)packet, length) < 0) { /* XXX: this is wrong, but until ND does work correctly, * this is the only way (aka the old way)*/ uint16_t raddr = dest->uint16[7]; sixlowpan_lowpan_sendto(0, &raddr, 2, (uint8_t *)packet, length); /* return -1; */ } return length; } }
static void _netif_list(kernel_pid_t dev) { uint8_t hwaddr[MAX_ADDR_LEN]; uint16_t u16; int16_t i16; int res; netopt_state_t state; netopt_enable_t enable; bool linebreak = false; #ifdef MODULE_GNRC_IPV6_NETIF gnrc_ipv6_netif_t *entry = gnrc_ipv6_netif_get(dev); char ipv6_addr[IPV6_ADDR_MAX_STR_LEN]; #endif printf("Iface %2d ", dev); res = gnrc_netapi_get(dev, NETOPT_ADDRESS, 0, hwaddr, sizeof(hwaddr)); if (res >= 0) { char hwaddr_str[res * 3]; printf(" HWaddr: "); printf("%s", gnrc_netif_addr_to_str(hwaddr_str, sizeof(hwaddr_str), hwaddr, res)); printf(" "); } res = gnrc_netapi_get(dev, NETOPT_CHANNEL, 0, &u16, sizeof(u16)); if (res >= 0) { printf(" Channel: %" PRIu16 " ", u16); } res = gnrc_netapi_get(dev, NETOPT_NID, 0, &u16, sizeof(u16)); if (res >= 0) { printf(" NID: 0x%" PRIx16 " ", u16); } res = gnrc_netapi_get(dev, NETOPT_TX_POWER, 0, &i16, sizeof(i16)); if (res >= 0) { printf(" TX-Power: %" PRIi16 "dBm ", i16); } res = gnrc_netapi_get(dev, NETOPT_STATE, 0, &state, sizeof(state)); if (res >= 0) { printf(" State: "); _print_netopt_state(state); } printf("\n "); res = gnrc_netapi_get(dev, NETOPT_ADDRESS_LONG, 0, hwaddr, sizeof(hwaddr)); if (res >= 0) { char hwaddr_str[res * 3]; printf("Long HWaddr: "); printf("%s", gnrc_netif_addr_to_str(hwaddr_str, sizeof(hwaddr_str), hwaddr, res)); printf("\n "); } res = gnrc_netapi_get(dev, NETOPT_PROMISCUOUSMODE, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { printf("PROMISC "); linebreak = true; } res = gnrc_netapi_get(dev, NETOPT_AUTOACK, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { printf("AUTOACK "); linebreak = true; } res = gnrc_netapi_get(dev, NETOPT_PRELOADING, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { printf("PRELOAD "); linebreak = true; } res = gnrc_netapi_get(dev, NETOPT_RAWMODE, 0, &enable, sizeof(enable)); if ((res >= 0) && (enable == NETOPT_ENABLE)) { printf("RAWMODE "); linebreak = true; } #ifdef MODULE_GNRC_IPV6_NETIF if ((entry != NULL) && (entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { printf("6LO "); linebreak = true; } #endif #if defined(MODULE_GNRC_SIXLOWPAN_NETIF) && defined(MODULE_GNRC_SIXLOWPAN_IPHC) gnrc_sixlowpan_netif_t *sixlo_entry = gnrc_sixlowpan_netif_get(dev); if ((sixlo_entry != NULL) && (sixlo_entry->iphc_enabled)) { printf("IPHC "); linebreak = true; } #endif if (linebreak) { printf("\n "); } res = gnrc_netapi_get(dev, NETOPT_SRC_LEN, 0, &u16, sizeof(u16)); if (res >= 0) { printf("Source address length: %" PRIu16 "\n ", u16); } #ifdef MODULE_GNRC_IPV6_NETIF for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { if (!ipv6_addr_is_unspecified(&entry->addrs[i].addr)) { printf("inet6 addr: "); if (ipv6_addr_to_str(ipv6_addr, &entry->addrs[i].addr, IPV6_ADDR_MAX_STR_LEN)) { printf("%s/%" PRIu8 " scope: ", ipv6_addr, entry->addrs[i].prefix_len); if ((ipv6_addr_is_link_local(&entry->addrs[i].addr))) { printf("local"); } else { printf("global"); } if (entry->addrs[i].flags & GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST) { if (ipv6_addr_is_multicast(&entry->addrs[i].addr)) { printf(" [multicast]"); } else { printf(" [anycast]"); } } } else { printf("error in conversion"); } printf("\n "); } } #endif puts(""); }
static ipv6_addr_t *_add_addr_to_entry(gnrc_ipv6_netif_t *entry, const ipv6_addr_t *addr, uint8_t prefix_len, uint8_t flags) { gnrc_ipv6_netif_addr_t *tmp_addr = NULL; for (int i = 0; i < GNRC_IPV6_NETIF_ADDR_NUMOF; i++) { if (ipv6_addr_equal(&(entry->addrs[i].addr), addr)) { return &(entry->addrs[i].addr); } if (ipv6_addr_is_unspecified(&(entry->addrs[i].addr)) && !tmp_addr) { tmp_addr = &(entry->addrs[i]); } } if (!tmp_addr) { DEBUG("ipv6 netif: couldn't add %s/%" PRIu8 " to interface %" PRIkernel_pid "\n: No space left.", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), prefix_len, entry->pid); return NULL; } memcpy(&(tmp_addr->addr), addr, sizeof(ipv6_addr_t)); DEBUG("ipv6 netif: Added %s/%" PRIu8 " to interface %" PRIkernel_pid "\n", ipv6_addr_to_str(addr_str, addr, sizeof(addr_str)), prefix_len, entry->pid); tmp_addr->prefix_len = prefix_len; tmp_addr->flags = flags; #ifdef MODULE_GNRC_SIXLOWPAN_ND if (!ipv6_addr_is_multicast(&(tmp_addr->addr)) && (entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN)) { ipv6_addr_t *router = gnrc_ndp_internal_default_router(); if (router != NULL) { mutex_unlock(&entry->mutex); /* function below relocks mutex */ gnrc_ndp_internal_send_nbr_sol(entry->pid, &tmp_addr->addr, router, router); mutex_lock(&entry->mutex); /* relock mutex */ } /* otherwise there is no default router to register to */ } #endif if (ipv6_addr_is_multicast(addr)) { tmp_addr->flags |= GNRC_IPV6_NETIF_ADDR_FLAGS_NON_UNICAST; } else { if (!ipv6_addr_is_link_local(addr)) { #ifdef MODULE_GNRC_SIXLOWPAN_ND_BORDER_ROUTER tmp_addr->valid = 0xFFFF; gnrc_sixlowpan_nd_router_abr_t *abr = gnrc_sixlowpan_nd_router_abr_get(); if (gnrc_sixlowpan_nd_router_abr_add_prf(abr, entry, tmp_addr) < 0) { DEBUG("ipv6_netif: error adding prefix to 6LoWPAN-ND management\n"); } #endif #if defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER) if ((entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER) && (entry->flags & GNRC_IPV6_NETIF_FLAGS_RTR_ADV)) { mutex_unlock(&entry->mutex); /* function below relocks mutex */ #ifdef MODULE_GNRC_SIXLOWPAN_ND_ROUTER if (entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) { gnrc_ndp_internal_send_rtr_adv(entry->pid, NULL, NULL, false); } #endif #ifdef MODULE_GNRC_NDP_ROUTER /* New 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_SIXLOWPAN)) { entry->rtr_adv_count = GNRC_NDP_MAX_INIT_RTR_ADV_NUMOF; gnrc_ndp_router_retrans_rtr_adv(entry); } #endif mutex_lock(&entry->mutex); /* relock mutex */ } #endif } else { tmp_addr->flags |= GNRC_IPV6_NETIF_ADDR_FLAGS_NDP_ON_LINK; } #if defined(MODULE_GNRC_NDP_NODE) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER) /* add solicited-nodes multicast address for new address if interface is not a * 6LoWPAN host interface (see: https://tools.ietf.org/html/rfc6775#section-5.2) */ if (!(entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) || (entry->flags & GNRC_IPV6_NETIF_FLAGS_ROUTER)) { ipv6_addr_t sol_node; ipv6_addr_set_solicited_nodes(&sol_node, addr); _add_addr_to_entry(entry, &sol_node, IPV6_ADDR_BIT_LEN, 0); } #endif /* TODO: send NS with ARO on 6LoWPAN interfaces, but not so many and only for the new * source address. */ } return &(tmp_addr->addr); }
static int ip6_finish_output2(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst->dev; struct neighbour *neigh; int res; skb->protocol = htons(ETH_P_IPV6); skb->dev = dev; if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) { struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(skb->sk) && ((mroute6_socket(dev_net(dev), skb) && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr))) { struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); /* Do not check for IFF_ALLMULTI; multicast routing is not supported in any case. */ if (newskb) NF_HOOK(NFPROTO_IPV6, NF_INET_POST_ROUTING, newskb, NULL, newskb->dev, ip6_dev_loopback_xmit); if (ipv6_hdr(skb)->hop_limit == 0) { IP6_INC_STATS(dev_net(dev), idev, IPSTATS_MIB_OUTDISCARDS); kfree_skb(skb); return 0; } } IP6_UPD_PO_STATS(dev_net(dev), idev, IPSTATS_MIB_OUTMCAST, skb->len); } rcu_read_lock(); if (dst->hh) { res = neigh_hh_output(dst->hh, skb); rcu_read_unlock(); return res; } else { neigh = dst_get_neighbour(dst); if (neigh) { res = neigh->output(skb); rcu_read_unlock(); return res; } rcu_read_unlock(); } IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES); kfree_skb(skb); return -EINVAL; }
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; }